diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 00000000000..eef634fae2a
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,9 @@
+{
+ "extensions": [
+ "github.vscode-codeql",
+ "slevesque.vscode-zipexplorer"
+ ],
+ "settings": {
+ "codeQL.experimentalBqrsParsing": true
+ }
+}
diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml
new file mode 100644
index 00000000000..4f21e2ef639
--- /dev/null
+++ b/.github/codeql/codeql-config.yml
@@ -0,0 +1,4 @@
+name: "CodeQL config"
+
+queries:
+ - uses: security-and-quality
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 00000000000..f1844da86cf
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,52 @@
+name: "Code scanning - action"
+
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '0 9 * * 1'
+
+jobs:
+ CodeQL-Build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+ with:
+ # We must fetch at least the immediate parents so that if this is
+ # a pull request then we can checkout the head.
+ fetch-depth: 2
+
+ # If this run was triggered by a pull request event, then checkout
+ # the head of the pull request instead of the merge commit.
+ - run: git checkout HEAD^2
+ if: ${{ github.event_name == 'pull_request' }}
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v1
+ # Override language selection by uncommenting this and choosing your languages
+ with:
+ languages: csharp
+ config-file: ./.github/codeql/codeql-config.yml
+
+ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
+ # If this step fails, then you should remove it and run the build manually (see below)
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v1
+
+ # ℹ️ Command-line programs to run using the OS shell.
+ # 📚 https://git.io/JvXDl
+
+ # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
+ # and modify them (or add more) to build your code if your project
+ # uses a compiled language
+
+ #- run: |
+ # make bootstrap
+ # make release
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v1
diff --git a/.gitignore b/.gitignore
index dc932d1f5c1..b14dab0a6b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,4 +21,3 @@
/codeql/
csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json
-.vscode
diff --git a/.vscode/.gitattributes b/.vscode/.gitattributes
new file mode 100644
index 00000000000..5fc4d971ee6
--- /dev/null
+++ b/.vscode/.gitattributes
@@ -0,0 +1 @@
+*.json linguist-language=JSON-with-Comments
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 00000000000..8465a7c2f86
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,10 @@
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
+ // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
+ // List of extensions which should be recommended for users of this workspace.
+ "recommendations": [
+ "github.vscode-codeql"
+ ],
+ // List of extensions recommended by VS Code that should not be recommended for users of this workspace.
+ "unwantedRecommendations": []
+}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 00000000000..169b6bdd64d
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,27 @@
+{
+ // To run a task, select the `Terminal | Run Task...` menu option, and then select the task from
+ // the list in the dropdown, or invoke the `Tasks: Run Task` command from the command palette/
+ // To bind a keyboard shortcut to invoke a task, see https://code.visualstudio.com/docs/editor/tasks#_binding-keyboard-shortcuts-to-tasks.
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "Sync Identical Files",
+ "type": "process",
+ // Non-Windows OS will usually have Python 3 already installed at /usr/bin/python3.
+ "command": "python3",
+ "args": [
+ "config/sync-files.py",
+ "--latest"
+ ],
+ "group": "build",
+ "windows": {
+ // On Windows, use whatever Python interpreter is configured for this workspace. The default is
+ // just `python`, so if Python is already on the path, this will find it.
+ "command": "${config:python.pythonPath}",
+ },
+ "problemMatcher": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/CODEOWNERS b/CODEOWNERS
index 8d4e4fd9b06..64bda94db77 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -1,11 +1,20 @@
-/cpp/ @Semmle/cpp-analysis
-/csharp/ @Semmle/cs
-/java/ @Semmle/java
-/javascript/ @Semmle/js
-/python/ @Semmle/python
+/cpp/ @github/codeql-c-analysis
+/csharp/ @github/codeql-csharp
+/java/ @github/codeql-java
+/javascript/ @github/codeql-javascript
+/python/ @github/codeql-python
+
+# Assign query help for docs review
/cpp/**/*.qhelp @hubwriter
/csharp/**/*.qhelp @jf205
/java/**/*.qhelp @felicitymay
/javascript/**/*.qhelp @mchammer01
/python/**/*.qhelp @felicitymay
/docs/language/ @shati-patel @jf205
+
+# Exclude help for experimental queries from docs review
+/cpp/**/experimental/**/*.qhelp @github/codeql-c-analysis
+/csharp/**/experimental/**/*.qhelp @github/codeql-csharp
+/java/**/experimental/**/*.qhelp @github/codeql-java
+/javascript/**/experimental/**/*.qhelp @github/codeql-javascript
+/python/**/experimental/**/*.qhelp @github/codeql-python
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index fcece36b0b1..641b30ebe3f 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -1,39 +1,126 @@
-# Code of Conduct
+## Our Pledge
-This code of conduct outlines expectations for participation in the Semmle open source community, including any open source repositories on GitHub.com, as well as steps for reporting unacceptable behavior. We are committed to providing a welcoming and inspiring community for all.
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, religion, or sexual identity
+and orientation.
-People violating this code of conduct may be banned from the community.
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
-Our community strives to:
-* Be friendly and patient: Remember you might not be communicating in someone else’s primary spoken or programming language, and others may not have your level of understanding.
-* Be welcoming: Our community welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, color, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability.
-* Be respectful: We are a world-wide community of professionals, and we conduct ourselves professionally. Disagreement is no excuse for poor behavior and poor manners. Disrespectful and unacceptable behavior includes, but is not limited to:
- * Violent threats or language.
- * Discriminatory or derogatory jokes and language.
- * Posting sexually explicit or violent material.
- * Posting, or threatening to post, people’s personally identifying information (“doxing”).
- * Insults, especially those using discriminatory terms or slurs.
- * Behavior that could be perceived as sexual attention.
- * Advocating for or encouraging any of the above behaviors.
-* Understand disagreements: Disagreements, both social and technical, are useful learning opportunities. Seek to understand others’ viewpoints and resolve differences constructively.
+## Our Standards
-This code is not exhaustive or complete. It serves to capture our common understanding of a productive, collaborative environment. We expect the code to be followed in spirit as much as in the letter.
+Examples of behavior that contributes to a positive environment for our
+community include:
-# Scope
+* Demonstrating empathy and kindness toward other people
+* Being respectful of differing opinions, viewpoints, and experiences
+* Giving and gracefully accepting constructive feedback
+* Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+* Focusing on what is best not just for us as individuals, but for the
+ overall community
-This code of conduct applies to all repositories and communities for Semmle open source projects, regardless of whether or not the repository explicitly calls out its use of this code. The code also applies in public spaces when an individual is representing the Semmle open source community. Examples include using an official project email address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
+Examples of unacceptable behavior include:
+* The use of sexualized language or imagery, and sexual attention or
+ advances of any kind
+* Trolling, insulting or derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or email
+ address, without their explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
-# Reporting Code of Conduct Issues
-We encourage members of the community to resolve issues on their own whenever possible. This builds a broader and deeper understanding and ultimately a healthier interaction. In the event that an issue cannot be resolved locally, please feel free to report your concerns by contacting code-of-conduct@semmle.com.
-In your report please include:
-* Your contact information.
-* Names (real, usernames or pseudonyms) of any individuals involved. If there are additional witnesses, please include them as well.
-* Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly available record (e.g. a mailing list archive or a public chat log), please include a link or attachment.
-* Any additional information that may be helpful.
+## Enforcement Responsibilities
-All reports will be reviewed by a multi-person team and will result in a response that is deemed necessary and appropriate to the circumstances. Where additional perspectives are needed, the team may seek insight from others with relevant expertise or experience. The confidentiality of the person reporting the incident will be kept at all times. Involved parties are never part of the review team.
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
-Anyone asked to stop unacceptable behavior is expected to comply immediately. If an individual engages in unacceptable behavior, the review team may take any action they deem appropriate, including a permanent ban from the community.
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
-*This text is licensed under the [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/) license. It is based on a template established by the [TODO Group](http://todogroup.org/) and variants thereof used by numerous other large communities (e.g., [Microsoft](https://microsoft.github.io/codeofconduct/), [Facebook](https://code.fb.com/codeofconduct/), [Yahoo](https://yahoo.github.io/codeofconduct), [Twitter](https://github.com/twitter/code-of-conduct), [GitHub](https://blog.github.com/2015-07-20-adopting-the-open-code-of-conduct/)) and the Scope section from the [Contributor Covenant version 1.4](http://contributor-covenant.org/version/1/4/).*
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement at
+opensource@github.com.
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series
+of actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or
+permanent ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within
+the community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.0, available at
+https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
+
+Community Impact Guidelines were inspired by [Mozilla's code of conduct
+enforcement ladder](https://github.com/mozilla/diversity).
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see the FAQ at
+https://www.contributor-covenant.org/faq. Translations are available at
+https://www.contributor-covenant.org/translations.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9853e6b91b8..fa88395e5d0 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -2,7 +2,7 @@
We welcome contributions to our CodeQL libraries and queries. Got an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request! Contributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [project's open source license](LICENSE).
-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).
+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 [CodeQL queries](https://help.semmle.com/QL/learn-ql/writing-queries/writing-queries.html) on [help.semmle.com](https://help.semmle.com).
## Submitting a new experimental query
@@ -20,7 +20,7 @@ If you have an idea for a query that you would like to share with other CodeQL u
* Python: `python/ql/src`
Each language-specific directory contains further subdirectories that group queries based on their `@tags` or purpose.
- - Experimental queries and libraries are stored in the `experimental` subdirectory within each language-specific directory in the [CodeQL repository](https://github.com/Semmle/ql). For example, experimental Java queries and libraries are stored in `java/ql/src/experimental` and any corresponding tests in `java/ql/test/experimental`.
+ - Experimental queries and libraries are stored in the `experimental` subdirectory within each language-specific directory in the [CodeQL repository](https://github.com/github/codeql). For example, experimental Java queries and libraries are stored in `java/ql/src/experimental` and any corresponding tests in `java/ql/test/experimental`.
- The structure of an `experimental` subdirectory mirrors the structure of its parent directory.
- Select or create an appropriate directory in `experimental` based on the existing directory structure of `experimental` or its parent directory.
@@ -32,11 +32,11 @@ If you have an idea for a query that you would like to share with other CodeQL u
For details, see the [guide on query metadata](docs/query-metadata-style-guide.md).
- Make sure the `select` statement is compatible with the query `@kind`. See [Introduction to query files](https://help.semmle.com/QL/learn-ql/writing-queries/introduction-to-queries.html#select-clause) on help.semmle.com.
+ Make sure the `select` statement is compatible with the query `@kind`. See [About CodeQL queries](https://help.semmle.com/QL/learn-ql/writing-queries/introduction-to-queries.html#select-clause) on help.semmle.com.
3. **Formatting**
- - The queries and libraries must be [autoformatted](https://help.semmle.com/codeql/codeql-for-vscode/reference/editor.html#autoformatting).
+ - The queries and libraries must be autoformatted, for example using the "Format Document" command in [CodeQL for Visual Studio Code](https://help.semmle.com/codeql/codeql-for-vscode/procedures/about-codeql-for-vscode.html).
4. **Compilation**
@@ -53,14 +53,6 @@ After the experimental query is merged, we welcome pull requests to improve it.
## Using your personal data
-If you contribute to this project, we will record your name and email
-address (as provided by you with your contributions) as part of the code
-repositories, which are public. We might also use this information
-to contact you in relation to your contributions, as well as in the
-normal course of software development. We also store records of your
-CLA agreements. Under GDPR legislation, we do this
-on the basis of our legitimate interest in creating the CodeQL product.
-
-Please do get in touch (privacy@github.com) if you have any questions about
-this or our data protection policies.
+If you contribute to this project, we will record your name and email address (as provided by you with your contributions) as part of the code repositories, which are public. We might also use this information to contact you in relation to your contributions, as well as in the normal course of software development. We also store records of CLA agreements signed in the past, but no longer require contributors to sign a CLA. Under GDPR legislation, we do this on the basis of our legitimate interest in creating the CodeQL product.
+Please do get in touch (privacy@github.com) if you have any questions about this or our data protection policies.
diff --git a/README.md b/README.md
index 1f77856cfc4..08e572a0246 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# CodeQL
-This open source repository contains the standard CodeQL libraries and queries that power [LGTM](https://lgtm.com) and the other CodeQL products that [GitHub](https://github.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 CodeQL products that [GitHub](https://github.com) makes available to its customers worldwide. For the queries, libraries, and extractor that power Go analysis, visit the [CodeQL for Go repository](https://github.com/github/codeql-go).
## How do I learn CodeQL and run queries?
@@ -9,8 +9,20 @@ You can use the [interactive query console](https://lgtm.com/help/lgtm/using-que
## Contributing
-We welcome contributions to our standard library and standard checks. Do you have an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request! Before you do, though, please take the time to read our [contributing guidelines](CONTRIBUTING.md). You can also consult our [style guides](https://github.com/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.
+We welcome contributions to our standard library and standard checks. Do you have an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request! Before you do, though, please take the time to read our [contributing guidelines](CONTRIBUTING.md). You can also consult our [style guides](https://github.com/github/codeql/tree/master/docs) to learn how to format your code for consistency and clarity, how to write query metadata, and how to write query help documentation for your query.
## License
-The code in this repository is licensed under [Apache License 2.0](LICENSE) by [GitHub](https://github.com).
+The code in this repository is licensed under the [MIT License](LICENSE) by [GitHub](https://github.com).
+
+## Visual Studio Code integration
+
+If you use Visual Studio Code to work in this repository, there are a few integration features to make development easier.
+
+### CodeQL for Visual Studio Code
+
+You can install the [CodeQL for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-codeql) extension to get syntax highlighting, IntelliSense, and code navigation for the QL language, as well as unit test support for testing CodeQL libraries and queries.
+
+### Tasks
+
+The `.vscode/tasks.json` file defines custom tasks specific to working in this repository. To invoke one of these tasks, select the `Terminal | Run Task...` menu option, and then select the desired task from the dropdown. You can also invoke the `Tasks: Run Task` command from the command palette.
diff --git a/change-notes/1.24/analysis-cpp.md b/change-notes/1.24/analysis-cpp.md
index 7ab002857cf..a89a360de3e 100644
--- a/change-notes/1.24/analysis-cpp.md
+++ b/change-notes/1.24/analysis-cpp.md
@@ -4,6 +4,8 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
## General improvements
+You can now suppress alerts using either single-line block comments (`/* ... */`) or line comments (`// ...`).
+
## New queries
| **Query** | **Tags** | **Purpose** |
@@ -12,46 +14,71 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
## Changes to existing queries
+A new taint-tracking library is used by all the security queries that track tainted values
+(`cpp/path-injection`, `cpp/cgi-xss`, `cpp/sql-injection`, `cpp/uncontrolled-process-operation`,
+`cpp/unbounded-write`, `cpp/tainted-format-string`, `cpp/tainted-format-string-through-global`,
+`cpp/uncontrolled-arithmetic`, `cpp/uncontrolled-allocation-size`, `cpp/user-controlled-bypass`,
+`cpp/cleartext-storage-buffer`, `cpp/tainted-permissions-check`).
+These queries now have more precise results and also offer _path explanations_ so you can explore the results easily.
+There is a performance cost to this, and the LGTM query suite will overall run slower than before.
+
| **Query** | **Expected impact** | **Change** |
|----------------------------|------------------------|------------------------------------------------------------------|
+| Boost\_asio TLS Settings Misconfiguration (`cpp/boost/tls-settings-misconfiguration`) | Query id change | The identifier was updated to use dashes in place of underscores (previous identifier `cpp/boost/tls_settings_misconfiguration`). |
| Buffer not sufficient for string (`cpp/overflow-calculated`) | More true positive results | This query now identifies a wider variety of buffer allocations using the `semmle.code.cpp.models.interfaces.Allocation` library. |
-| No space for zero terminator (`cpp/no-space-for-terminator`) | More true positive results | This query now identifies a wider variety of buffer allocations using the `semmle.code.cpp.models.interfaces.Allocation` library. |
+| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | | This query is no longer run on LGTM. |
| Memory is never freed (`cpp/memory-never-freed`) | More true positive results | This query now identifies a wider variety of buffer allocations using the `semmle.code.cpp.models.interfaces.Allocation` library. |
| Memory may not be freed (`cpp/memory-may-not-be-freed`) | More true positive results | This query now identifies a wider variety of buffer allocations using the `semmle.code.cpp.models.interfaces.Allocation` library. |
-| Mismatching new/free or malloc/delete (`cpp/new-free-mismatch`) | Fewer false positive results | Fixed false positive results in template code. |
-| Missing return statement (`cpp/missing-return`) | Fewer false positive results | Functions containing `asm` statements are no longer highlighted by this query. |
-| Missing return statement (`cpp/missing-return`) | More accurate locations | Locations reported by this query are now more accurate in some cases. |
-| No space for zero terminator (`cpp/no-space-for-terminator`) | More correct results | String arguments to formatting functions are now (usually) expected to be null terminated strings. |
-| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | | This query is no longer run on LGTM. |
-| No space for zero terminator (`cpp/no-space-for-terminator`) | Fewer false positive results | This query has been modified to be more conservative when identifying which pointers point to null-terminated strings. This approach produces fewer, more accurate results. |
-| Overflow in uncontrolled allocation size (`cpp/uncontrolled-allocation-size`) | Fewer false positive results | Cases where the tainted allocation size is range checked are now more reliably excluded. |
+| Mismatching new/free or malloc/delete (`cpp/new-free-mismatch`) | Fewer false positive results | Improved handling of template code gives greater precision. |
+| Missing return statement (`cpp/missing-return`) | Fewer false positive results and more accurate locations | Functions containing `asm` statements are no longer highlighted by this query. The locations reported by this query are now more accurate in some cases. |
+| No space for zero terminator (`cpp/no-space-for-terminator`) | More results with greater precision | The query gives more precise results for a wider variety of buffer allocations. String arguments to formatting functions are now (usually) expected to be null terminated strings. Use of the `semmle.code.cpp.models.interfaces.Allocation` library identifies problems with a wider variety of buffer allocations. This query is also more conservative when identifying which pointers point to null-terminated strings. |
+| Overflow in uncontrolled allocation size (`cpp/uncontrolled-allocation-size`) | Fewer false positive results | The query now produces fewer, more accurate results. Cases where the tainted allocation size is range checked are more reliably excluded. |
| Overloaded assignment does not return 'this' (`cpp/assignment-does-not-return-this`) | Fewer false positive results | This query no longer reports incorrect results in template classes. |
+| Pointer overflow check (`cpp/pointer-overflow-check`),
Possibly wrong buffer size in string copy (`cpp/bad-strncpy-size`),
Signed overflow check (`cpp/signed-overflow-check`) | More correct results | A new library is used for determining which expressions have identical value, giving more precise results. There is a performance cost to this, and the LGTM suite will overall run slower than before. |
| Unsafe array for days of the year (`cpp/leap-year/unsafe-array-for-days-of-the-year`) | | This query is no longer run on LGTM. |
| Unsigned comparison to zero (`cpp/unsigned-comparison-zero`) | More correct results | This query now also looks for comparisons of the form `0 <= x`. |
## Changes to libraries
-* The data-flow library has been improved, which affects and improves some security queries. The improvements are:
- - Track flow through functions that combine taint tracking with flow through fields.
- - Track flow through clone-like functions, that is, functions that read contents of a field from a
- parameter and stores the value in the field of a returned object.
-* Created the `semmle.code.cpp.models.interfaces.Allocation` library to model allocation such as `new` expressions and calls to `malloc`. This in intended to replace the functionality in `semmle.code.cpp.commons.Alloc` with a more consistent and useful interface.
-* Created the `semmle.code.cpp.models.interfaces.Deallocation` library to model deallocation such as `delete` expressions and calls to `free`. This in intended to replace the functionality in `semmle.code.cpp.commons.Alloc` with a more consistent and useful interface.
+* The built-in C++20 "spaceship operator" (`<=>`) is now supported via the QL
+ class `SpaceshipExpr`. Overloaded forms are modeled as calls to functions
+ named `operator<=>`.
+* The data-flow library (`semmle.code.cpp.dataflow.DataFlow` and
+ `semmle.code.cpp.dataflow.TaintTracking`) has been improved, which affects
+ and improves some security queries. The improvements are:
+ - Track flow through functions that combine taint tracking with flow through fields.
+ - Track flow through clone-like functions, that is, functions that read contents of a field from a
+ parameter and stores the value in the field of a returned object.
+* The security pack taint tracking library
+ (`semmle.code.cpp.security.TaintTracking`) uses a new intermediate
+ representation. This provides a more precise analysis of flow through
+ parameters and pointers. For new queries, however, we continue to recommend
+ using `semmle.code.cpp.dataflow.TaintTracking`.
+* The global value numbering library
+ (`semmle.code.cpp.valuenumbering.GlobalValueNumbering`) uses a new
+ intermediate representation to provide a more precise analysis of
+ heap-allocated memory and pointers to stack variables.
+* New libraries have been created to provide a more consistent and useful interface
+ for modeling allocation and deallocation. These replace the old
+ `semmle.code.cpp.commons.Alloc` library.
+ * The new `semmle.code.cpp.models.interfaces.Allocation` library models
+ allocations, such as `new` expressions and calls to `malloc`.
+ * The new `semmle.code.cpp.models.interfaces.Deallocation` library
+ models deallocations, such as `delete` expressions and calls to `free`.
+ * The predicate `freeCall` in `semmle.code.cpp.commons.Alloc` has been
+ deprecated. The `Allocation` and `Deallocation` models in
+ `semmle.code.cpp.models.interfaces` should be used instead.
* The new class `StackVariable` should be used in place of `LocalScopeVariable`
in most cases. The difference is that `StackVariable` does not include
variables declared with `static` or `thread_local`.
- * As a rule of thumb, custom queries about the _values_ of variables should
- be changed from `LocalScopeVariable` to `StackVariable`, while queries
- about the _name or scope_ of variables should remain unchanged.
- * The `LocalScopeVariableReachability` library is deprecated in favor of
- `StackVariableReachability`. The functionality is the same.
-* The models library models `strlen` in more detail, and includes common variations such as `wcslen`.
-* The models library models `gets` and similar functions.
-* The models library now partially models `std::string`.
-* The taint tracking library (`semmle.code.cpp.dataflow.TaintTracking`) has had
- the following improvements:
- * The library now models data flow through `strdup` and similar functions.
- * The library now models data flow through formatting functions such as `sprintf`.
-* The security pack taint tracking library (`semmle.code.cpp.security.TaintTracking`) uses a new intermediate representation. This provides a more precise analysis of pointers to stack variables and flow through parameters, improving the results of many security queries.
-* The global value numbering library (`semmle.code.cpp.valuenumbering.GlobalValueNumbering`) uses a new intermediate representation to provide a more precise analysis of heap allocated memory and pointers to stack variables.
-* `freeCall` in `semmle.code.cpp.commons.Alloc` has been deprecated. The`Allocation` and `Deallocation` models in `semmle.code.cpp.models.interfaces` should be used instead.
+ * As a rule of thumb, custom queries about the _values_ of variables should
+ be changed from `LocalScopeVariable` to `StackVariable`, while queries
+ about the _name or scope_ of variables should remain unchanged.
+ * The `LocalScopeVariableReachability` library is deprecated in favor of
+ `StackVariableReachability`. The functionality is the same.
+* Taint tracking and data flow now features better modeling of commonly-used
+ library functions:
+ * `gets` and similar functions,
+ * the most common operations on `std::string`,
+ * `strdup` and similar functions, and
+ * formatting functions such as `sprintf`.
diff --git a/change-notes/1.24/analysis-csharp.md b/change-notes/1.24/analysis-csharp.md
index a745e985eae..3ed64a6b27b 100644
--- a/change-notes/1.24/analysis-csharp.md
+++ b/change-notes/1.24/analysis-csharp.md
@@ -2,30 +2,31 @@
The following changes in version 1.24 affect C# analysis in all applications.
+## General improvements
+
+You can now suppress alerts using either single-line block comments (`/* ... */`) or line comments (`// ...`).
+
## New queries
| **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------|
-| Assembly path injection (`cs/assembly-path-injection`) | security, external/cwe/cwe-114 | Finds user-controlled data used to load an assembly. |
-| Insecure configuration for ASP.NET requestValidationMode (`cs/insecure-request-validation-mode`) | security, external/cwe/cwe-016 | Finds where this attribute has been set to a value less than 4.5, which turns off some validation features and makes the application less secure. |
-| Insecure SQL connection (`cs/insecure-sql-connection`) | security, external/cwe/cwe-327 | Finds unencrypted SQL connection strings. |
-| Page request validation is disabled (`cs/web/request-validation-disabled`) | security, frameworks/asp.net, external/cwe/cwe-016 | Finds where ASP.NET page request validation has been disabled, which could make the application less secure. |
-| Serialization check bypass (`cs/serialization-check-bypass`) | security, external/cwe/cwe-20 | Finds where data is not validated in a deserialization method. |
-| XML injection (`cs/xml-injection`) | security, external/cwe/cwe-091 | Finds user-controlled data that is used to write directly to an XML document. |
+| Assembly path injection (`cs/assembly-path-injection`) | security, external/cwe/cwe-114 | Finds user-controlled data used to load an assembly. Results are shown on LGTM by default. |
+| Insecure configuration for ASP.NET requestValidationMode (`cs/insecure-request-validation-mode`) | security, external/cwe/cwe-016 | Finds where this attribute has been set to a value less than 4.5, which turns off some validation features and makes the application less secure. By default, the query is not run on LGTM. |
+| Insecure SQL connection (`cs/insecure-sql-connection`) | security, external/cwe/cwe-327 | Finds unencrypted SQL connection strings. Results are not shown on LGTM by default. |
+| Page request validation is disabled (`cs/web/request-validation-disabled`) | security, frameworks/asp.net, external/cwe/cwe-016 | Finds where ASP.NET page request validation has been disabled, which could make the application less secure. By default, the query is not run on LGTM. |
+| Serialization check bypass (`cs/serialization-check-bypass`) | security, external/cwe/cwe-20 | Finds where data is not validated in a deserialization method. Results are not shown on LGTM by default. |
+| XML injection (`cs/xml-injection`) | security, external/cwe/cwe-091 | Finds user-controlled data that is used to write directly to an XML document. Results are shown on LGTM by default. |
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
|------------------------------|------------------------|-----------------------------------|
-| Useless assignment to local variable (`cs/useless-assignment-to-local`) | Fewer false positive results | Results have been removed when the variable is named `_` in a `foreach` statement. |
-| Potentially dangerous use of non-short-circuit logic (`cs/non-short-circuit`) | Fewer false positive results | Results have been removed when the expression contains an `out` parameter. |
| Dereferenced variable may be null (`cs/dereferenced-value-may-be-null`) | More results | Results are reported from parameters with a default value of `null`. |
-| Useless assignment to local variable (`cs/useless-assignment-to-local`) | Fewer false positive results | Results have been removed when the value assigned is an (implicitly or explicitly) cast default-like value. For example, `var s = (string)null` and `string s = default`. |
-| XPath injection (`cs/xml/xpath-injection`) | More results | The query now recognizes calls to methods on `System.Xml.XPath.XPathNavigator` objects. |
-| Information exposure through transmitted data (`cs/sensitive-data-transmission`) | More results | The query now recognizes writes to cookies and writes to ASP.NET (`Inner`)`Text` properties as additional sinks. |
| Information exposure through an exception (`cs/information-exposure-through-exception`) | More results | The query now recognizes writes to cookies, writes to ASP.NET (`Inner`)`Text` properties, and email contents as additional sinks. |
-
-## Removal of old queries
+| Information exposure through transmitted data (`cs/sensitive-data-transmission`) | More results | The query now recognizes writes to cookies and writes to ASP.NET (`Inner`)`Text` properties as additional sinks. |
+| Potentially dangerous use of non-short-circuit logic (`cs/non-short-circuit`) | Fewer false positive results | Results have been removed when the expression contains an `out` parameter. |
+| Useless assignment to local variable (`cs/useless-assignment-to-local`) | Fewer false positive results | Results have been removed when the value assigned is an (implicitly or explicitly) cast default-like value. For example, `var s = (string)null` and `string s = default`. Results have also been removed when the variable is named `_` in a `foreach` statement. |
+| XPath injection (`cs/xml/xpath-injection`) | More results | The query now recognizes calls to methods on `System.Xml.XPath.XPathNavigator` objects. |
## Changes to code extraction
@@ -37,13 +38,11 @@ The following changes in version 1.24 affect C# analysis in all applications.
## Changes to libraries
* The data-flow library has been improved, which affects and improves most security queries. The improvements are:
- - Track flow through methods that combine taint tracking with flow through fields.
- - Track flow through clone-like methods, that is, methods that read contents of a field from a
- parameter and stores the value in the field of a returned object.
+ - Track flow through methods that combine taint tracking with flow through fields.
+ - Track flow through clone-like methods, that is, methods that read the contents of a field from a
+ parameter and store the value in the field of a returned object.
* The taint tracking library now tracks flow through (implicit or explicit) conversion operator calls.
* [Code contracts](https://docs.microsoft.com/en-us/dotnet/framework/debug-trace-profile/code-contracts) are now recognized, and are treated like any other assertion methods.
* Expression nullability flow state is given by the predicates `Expr.hasNotNullFlowState()` and `Expr.hasMaybeNullFlowState()`.
* `stackalloc` array creations are now represented by the QL class `Stackalloc`. Previously they were represented by the class `ArrayCreation`.
-* A new class `RemoteFlowSink` has been added to model sinks where data might be exposed to external users. Examples include web page output, e-mails, and cookies.
-
-## Changes to autobuilder
+* A new class `RemoteFlowSink` has been added to model sinks where data might be exposed to external users. Examples include web page output, emails, and cookies.
diff --git a/change-notes/1.24/analysis-java.md b/change-notes/1.24/analysis-java.md
index 36210c0457e..a594cf08e1e 100644
--- a/change-notes/1.24/analysis-java.md
+++ b/change-notes/1.24/analysis-java.md
@@ -4,7 +4,7 @@ The following changes in version 1.24 affect Java analysis in all applications.
## General improvements
-* Alert suppression can now be done with single-line block comments (`/* ... */`) as well as line comments (`// ...`).
+* You can now suppress alerts using either single-line block comments (`/* ... */`) or line comments (`// ...`).
* A `Customizations.qll` file has been added to allow customizations of the standard library that apply to all queries.
## New queries
@@ -21,16 +21,16 @@ The following changes in version 1.24 affect Java analysis in all applications.
| **Query** | **Expected impact** | **Change** |
|------------------------------|------------------------|-----------------------------------|
-| Dereferenced variable may be null (`java/dereferenced-value-may-be-null`) | Fewer false positives | Final fields with a non-null initializer are no longer reported. |
-| Expression always evaluates to the same value (`java/evaluation-to-constant`) | Fewer false positives | Expressions of the form `0 * x` are usually intended and no longer reported. Also left shift of ints by 32 bits and longs by 64 bits are no longer reported as they are not constant, these results are instead reported by the new query `java/lshift-larger-than-type-width`. |
-| Useless null check (`java/useless-null-check`) | More true positives | Useless checks on final fields with a non-null initializer are now reported. |
+| Dereferenced variable may be null (`java/dereferenced-value-may-be-null`) | Fewer false positive results | Final fields with a non-null initializer are no longer reported. |
+| Expression always evaluates to the same value (`java/evaluation-to-constant`) | Fewer false positive results | Expressions of the form `0 * x` are usually intended and no longer reported. Also left shift of ints by 32 bits and longs by 64 bits are no longer reported as they are not constant, these results are instead reported by the new query `java/lshift-larger-than-type-width`. |
+| Useless null check (`java/useless-null-check`) | More true positive results | Useless checks on final fields with a non-null initializer are now reported. |
## Changes to libraries
* The data-flow library has been improved, which affects and improves most security queries. The improvements are:
- - Track flow through methods that combine taint tracking with flow through fields.
- - Track flow through clone-like methods, that is, methods that read contents of a field from a
- parameter and stores the value in the field of a returned object.
+ - Track flow through methods that combine taint tracking with flow through fields.
+ - Track flow through clone-like methods, that is, methods that read contents of a field from a
+ parameter and stores the value in the field of a returned object.
* Identification of test classes has been improved. Previously, one of the
match conditions would classify any class with a name containing the string
"Test" as a test class, but now this matching has been replaced with one that
@@ -38,6 +38,6 @@ The following changes in version 1.24 affect Java analysis in all applications.
general file classification mechanism and thus suppression of alerts, and
also any security queries using taint tracking, as test classes act as
default barriers stopping taint flow.
-* Parentheses are now no longer modelled directly in the AST, that is, the
+* Parentheses are now no longer modeled directly in the AST, that is, the
`ParExpr` class is empty. Instead, a parenthesized expression can be
identified with the `Expr.isParenthesized()` member predicate.
diff --git a/change-notes/1.24/analysis-javascript.md b/change-notes/1.24/analysis-javascript.md
index 0e92d66033d..ee776a61acb 100644
--- a/change-notes/1.24/analysis-javascript.md
+++ b/change-notes/1.24/analysis-javascript.md
@@ -4,67 +4,68 @@
* TypeScript 3.8 is now supported.
-* Alert suppression can now be done with single-line block comments (`/* ... */`) as well as line comments (`// ...`).
+* You can now suppress alerts using either single-line block comments (`/* ... */`) or line comments (`// ...`).
* Resolution of imports has improved, leading to more results from the security queries:
- - Imports with the `.js` extension can now be resolved to a TypeScript file,
- when the import refers to a file generated by TypeScript.
- - Imports that rely on path-mappings from a `tsconfig.json` file can now be resolved.
- - Export declarations of the form `export * as ns from "x"` are now analyzed more precisely.
+ - Imports with the `.js` extension can now be resolved to a TypeScript file,
+ when the import refers to a file generated by TypeScript.
+ - Imports that rely on path-mappings from a `tsconfig.json` file can now be resolved.
+ - Export declarations of the form `export * as ns from "x"` are now analyzed more precisely.
* The analysis of sanitizers has improved, leading to more accurate results from the security queries.
In particular:
- - Sanitizer guards now act across function boundaries in more cases.
- - Sanitizers can now better distinguish between a tainted value and an object _containing_ a tainted value.
+ - Sanitizer guards now act across function boundaries in more cases.
+ - Sanitizers can now better distinguish between a tainted value and an object _containing_ a tainted value.
* Call graph construction has been improved, leading to more results from the security queries:
- - Calls can now be resolved to indirectly-defined class members in more cases.
- - Calls through partial invocations such as `.bind` can now be resolved in more cases.
+ - Calls can now be resolved to indirectly-defined class members in more cases.
+ - Calls through partial invocations such as `.bind` can now be resolved in more cases.
* Support for flow summaries has been more clearly marked as being experimental and moved to the new `experimental` folder.
* Support for the following frameworks and libraries has been improved:
- - [Electron](https://electronjs.org/)
- - [fstream](https://www.npmjs.com/package/fstream)
- - [Handlebars](https://www.npmjs.com/package/handlebars)
- - [jsonfile](https://www.npmjs.com/package/jsonfile)
- - [Koa](https://www.npmjs.com/package/koa)
- - [Node.js](https://nodejs.org/)
- - [Socket.IO](https://socket.io/)
- - [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API)
- - [chrome-remote-interface](https://www.npmjs.com/package/chrome-remote-interface)
- - [for-in](https://www.npmjs.com/package/for-in)
- - [for-own](https://www.npmjs.com/package/for-own)
- - [http2](https://nodejs.org/api/http2.html)
- - [jQuery](https://jquery.com/)
- - [lazy-cache](https://www.npmjs.com/package/lazy-cache)
- - [mongodb](https://www.npmjs.com/package/mongodb)
- - [ncp](https://www.npmjs.com/package/ncp)
- - [node-dir](https://www.npmjs.com/package/node-dir)
- - [path-exists](https://www.npmjs.com/package/path-exists)
- - [pg](https://www.npmjs.com/package/pg)
- - [react](https://www.npmjs.com/package/react)
- - [recursive-readdir](https://www.npmjs.com/package/recursive-readdir)
- - [request](https://www.npmjs.com/package/request)
- - [rimraf](https://www.npmjs.com/package/rimraf)
- - [send](https://www.npmjs.com/package/send)
- - [SockJS](https://www.npmjs.com/package/sockjs)
- - [SockJS-client](https://www.npmjs.com/package/sockjs-client)
- - [typeahead.js](https://www.npmjs.com/package/typeahead.js)
- - [vinyl-fs](https://www.npmjs.com/package/vinyl-fs)
- - [write-file-atomic](https://www.npmjs.com/package/write-file-atomic)
- - [ws](https://github.com/websockets/ws)
+ - [chrome-remote-interface](https://www.npmjs.com/package/chrome-remote-interface)
+ - [Electron](https://electronjs.org/)
+ - [for-in](https://www.npmjs.com/package/for-in)
+ - [for-own](https://www.npmjs.com/package/for-own)
+ - [fstream](https://www.npmjs.com/package/fstream)
+ - [Handlebars](https://www.npmjs.com/package/handlebars)
+ - [http2](https://nodejs.org/api/http2.html)
+ - [jQuery](https://jquery.com/)
+ - [jsonfile](https://www.npmjs.com/package/jsonfile)
+ - [Koa](https://www.npmjs.com/package/koa)
+ - [lazy-cache](https://www.npmjs.com/package/lazy-cache)
+ - [mongodb](https://www.npmjs.com/package/mongodb)
+ - [ncp](https://www.npmjs.com/package/ncp)
+ - [Node.js](https://nodejs.org/)
+ - [node-dir](https://www.npmjs.com/package/node-dir)
+ - [path-exists](https://www.npmjs.com/package/path-exists)
+ - [pg](https://www.npmjs.com/package/pg)
+ - [react](https://www.npmjs.com/package/react)
+ - [recursive-readdir](https://www.npmjs.com/package/recursive-readdir)
+ - [request](https://www.npmjs.com/package/request)
+ - [rimraf](https://www.npmjs.com/package/rimraf)
+ - [send](https://www.npmjs.com/package/send)
+ - [Socket.IO](https://socket.io/)
+ - [SockJS](https://www.npmjs.com/package/sockjs)
+ - [SockJS-client](https://www.npmjs.com/package/sockjs-client)
+ - [typeahead.js](https://www.npmjs.com/package/typeahead.js)
+ - [vinyl-fs](https://www.npmjs.com/package/vinyl-fs)
+ - [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API)
+ - [write-file-atomic](https://www.npmjs.com/package/write-file-atomic)
+ - [ws](https://github.com/websockets/ws)
+
## New queries
| **Query** | **Tags** | **Purpose** |
|---------------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Cross-site scripting through exception (`js/xss-through-exception`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities where an exception is written to the DOM. Results are not shown on LGTM by default. |
-| Regular expression always matches (`js/regex/always-matches`) | correctness, regular-expressions | Highlights regular expression checks that trivially succeed by matching an empty substring. Results are shown on LGTM by default. |
| Missing await (`js/missing-await`) | correctness | Highlights expressions that operate directly on a promise object in a nonsensical way, instead of awaiting its result. Results are shown on LGTM by default. |
| Polynomial regular expression used on uncontrolled data (`js/polynomial-redos`) | security, external/cwe/cwe-730, external/cwe/cwe-400 | Highlights expensive regular expressions that may be used on malicious input. Results are shown on LGTM by default. |
| Prototype pollution in utility function (`js/prototype-pollution-utility`) | security, external/cwe/cwe-400, external/cwe/cwe-471 | Highlights recursive assignment operations that are susceptible to prototype pollution. Results are shown on LGTM by default. |
-| Unsafe jQuery plugin (`js/unsafe-jquery-plugin`) | Highlights potential XSS vulnerabilities in unsafely designed jQuery plugins. Results are shown on LGTM by default. |
+| Regular expression always matches (`js/regex/always-matches`) | correctness, regular-expressions | Highlights regular expression checks that trivially succeed by matching an empty substring. Results are shown on LGTM by default. |
+| Unsafe jQuery plugin (`js/unsafe-jquery-plugin`) | | Highlights potential XSS vulnerabilities in unsafely designed jQuery plugins. Results are shown on LGTM by default. |
| Unnecessary use of `cat` process (`js/unnecessary-use-of-cat`) | correctness, security, maintainability | Highlights command executions of `cat` where the fs API should be used instead. Results are shown on LGTM by default. |
@@ -73,20 +74,20 @@
| **Query** | **Expected impact** | **Change** |
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
| Clear-text logging of sensitive information (`js/clear-text-logging`) | More results | More results involving `process.env` and indirect calls to logging methods are recognized. |
-| Duplicate parameter names (`js/duplicate-parameter-name`) | Fewer results | This query now recognizes additional parameters that reasonably can have duplicated names. |
-| Incomplete string escaping or encoding (`js/incomplete-sanitization`) | Fewer false positive results | This query now recognizes additional cases where a single replacement is likely to be intentional. |
-| Unbound event handler receiver (`js/unbound-event-handler-receiver`) | Fewer false positive results | This query now recognizes additional ways event handler receivers can be bound. |
+| Duplicate parameter names (`js/duplicate-parameter-name`) | Fewer results | This query now ignores additional parameters that reasonably can have duplicated names. |
| Expression has no effect (`js/useless-expression`) | Fewer false positive results | The query now recognizes block-level flow type annotations and ignores the first statement of a try block. |
-| Use of call stack introspection in strict mode (`js/strict-mode-call-stack-introspection`) | Fewer false positive results | The query no longer flags expression statements. |
+| Identical operands (`js/redundant-operation`) | Fewer results | This query now excludes cases where the operands change a value using ++/-- expressions. |
+| Incomplete string escaping or encoding (`js/incomplete-sanitization`) | Fewer false positive results | This query now recognizes and excludes additional cases where a single replacement is likely to be intentional. |
+| Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | More results | This query now recognizes additional variations of URL scheme checks. |
| Missing CSRF middleware (`js/missing-token-validation`) | Fewer false positive results | The query reports fewer duplicates and only flags handlers that explicitly access cookie data. |
-| Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional ways dangerous paths can be constructed and used. |
-| Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional ways of constructing arguments to `cmd.exe` and `/bin/sh`. |
+| Superfluous trailing arguments (`js/superfluous-trailing-arguments`) | Fewer results | This query now excludes cases where a function uses the `Function.arguments` value to process a variable number of parameters. |
| Syntax error (`js/syntax-error`) | Lower severity | This results of this query are now displayed with lower severity. |
-| Use of password hash with insufficient computational effort (`js/insufficient-password-hash`) | Fewer false positive results | This query now recognizes additional cases that do not require secure hashing. |
-| Useless regular-expression character escape (`js/useless-regexp-character-escape`) | Fewer false positive results | This query now distinguishes escapes in strings and regular expression literals. |
-| Identical operands (`js/redundant-operation`) | Fewer results | This query now recognizes cases where the operands change a value using ++/-- expressions. |
-| Superfluous trailing arguments (`js/superfluous-trailing-arguments`) | Fewer results | This query now recognizes cases where a function uses the `Function.arguments` value to process a variable number of parameters. |
-| Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | More results | This query now recognizes more variations of URL scheme checks. |
+| Unbound event handler receiver (`js/unbound-event-handler-receiver`) | Fewer false positive results | This query now recognizes additional ways event handler receivers can be bound. |
+| Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional ways of constructing arguments to `cmd.exe` and `/bin/sh`. |
+| Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional ways dangerous paths can be constructed and used. |
+| Use of call stack introspection in strict mode (`js/strict-mode-call-stack-introspection`) | Fewer false positive results | The query no longer flags expression statements. |
+| Use of password hash with insufficient computational effort (`js/insufficient-password-hash`) | Fewer false positive results | This query now recognizes and excludes additional cases that do not require secure hashing. |
+| Useless regular-expression character escape (`js/useless-regexp-character-escape`) | Fewer false positive results | This query now distinguishes between escapes in strings and regular expression literals. |
## Changes to libraries
@@ -94,6 +95,6 @@
* An extensible model of the `EventEmitter` pattern has been implemented.
* Taint-tracking configurations now interact differently with the `data` flow label, which may affect queries
that combine taint-tracking and flow labels.
- - Sources added by the 1-argument `isSource` predicate are associated with the `taint` label now, instead of the `data` label.
- - Sanitizers now only block the `taint` label. As a result, sanitizers no longer block the flow of tainted values wrapped inside a property of an object.
- To retain the old behavior, instead use a barrier, or block the `data` flow label using a labeled sanitizer.
+ - Sources added by the 1-argument `isSource` predicate are associated with the `taint` label now, instead of the `data` label.
+ - Sanitizers now only block the `taint` label. As a result, sanitizers no longer block the flow of tainted values wrapped inside a property of an object.
+ To retain the old behavior, instead use a barrier, or block the `data` flow label using a labeled sanitizer.
diff --git a/change-notes/1.24/analysis-python.md b/change-notes/1.24/analysis-python.md
index fe5e637f041..16a3d4156e4 100644
--- a/change-notes/1.24/analysis-python.md
+++ b/change-notes/1.24/analysis-python.md
@@ -4,37 +4,52 @@ The following changes in version 1.24 affect Python analysis in all applications
## General improvements
-Support for Django version 2.x and 3.x
+- Support for Django version 2.x and 3.x
-## New queries
+- Taint tracking now correctly tracks taint in destructuring assignments. For example, if `tainted_list` is a list of tainted tainted elements, then
+ ```python
+ head, *tail = tainted_list
+ ```
+ will result in `tail` being tainted with the same taint as `tainted_list`, and `head` being tainted with the taint of the elements of `tainted_list`.
+
+- A large number of libraries and queries have been moved to the new `Value` API, which should result in more precise results.
+
+- The `Value` interface has been extended in various ways:
+ - A new `StringValue` class has been added, for tracking string literals.
+ - Values now have a `booleanValue` method which returns the boolean interpretation of the given value.
+ - Built-in methods for which the return type is not fixed are now modeled as returning an unknown value by default.
-| **Query** | **Tags** | **Purpose** |
-|-----------------------------|-----------|--------------------------------------------------------------------|
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
|----------------------------|------------------------|------------------------------------------------------------------|
-| Uncontrolled command line (`py/command-line-injection`) | More results | We now model the `fabric` and `invoke` pacakges for command execution. |
+| Arbitrary file write during tarfile extraction (`py/tarslip`) | Fewer false negative results | Negations are now handled correctly in conditional expressions that may sanitize tainted values. |
+| First parameter of a method is not named 'self' (`py/not-named-self`) | Fewer false positive results | `__class_getitem__` is now recognized as a class method. |
+| Import of deprecated module (`py/import-deprecated-module`) | Fewer false positive results | Deprecated modules that are used to provide backwards compatibility are no longer reported.|
+| Module imports itself (`py/import-own-module`) | Fewer false positive results | Imports local to a given package are no longer classified as self-imports. |
+| Uncontrolled command line (`py/command-line-injection`) | More results | We now model the `fabric` and `invoke` packages for command execution. |
### Web framework support
-The QL-library support for the web frameworks Bottle, CherryPy, Falcon, Pyramid, TurboGears, Tornado, and Twisted have
-been fixed so they provide a proper HttpRequestTaintSource, instead of a TaintSource. This will enable results for the following queries:
+The CodeQL library has improved support for the web frameworks: Bottle, CherryPy, Falcon, Pyramid, TurboGears, Tornado, and Twisted. They now provide a proper `HttpRequestTaintSource`, instead of a `TaintSource`. This will enable results for the following queries:
-- py/path-injection
-- py/command-line-injection
-- py/reflective-xss
-- py/sql-injection
-- py/code-injection
-- py/unsafe-deserialization
-- py/url-redirection
+- `py/path-injection`
+- `py/command-line-injection`
+- `py/reflective-xss`
+- `py/sql-injection`
+- `py/code-injection`
+- `py/unsafe-deserialization`
+- `py/url-redirection`
-The QL-library support for the web framework Twisted have been fixed so they provide a proper
-HttpResponseTaintSink, instead of a TaintSink. This will enable results for the following
+The library also has improved support for the web framework Twisted. It now provides a proper
+`HttpResponseTaintSink`, instead of a `TaintSink`. This will enable results for the following
queries:
-- py/reflective-xss
-- py/stack-trace-exposure
+- `py/reflective-xss`
+- `py/stack-trace-exposure`
## Changes to libraries
+### Taint tracking
+- The `urlsplit` and `urlparse` functions now propagate taint appropriately.
+- HTTP requests using the `requests` library are now modeled.
diff --git a/change-notes/1.25/analysis-cpp.md b/change-notes/1.25/analysis-cpp.md
new file mode 100644
index 00000000000..7ab98ffe859
--- /dev/null
+++ b/change-notes/1.25/analysis-cpp.md
@@ -0,0 +1,44 @@
+# Improvements to C/C++ analysis
+
+The following changes in version 1.25 affect C/C++ analysis in all applications.
+
+## General improvements
+
+## New queries
+
+| **Query** | **Tags** | **Purpose** |
+|-----------------------------|-----------|--------------------------------------------------------------------|
+
+## Changes to existing queries
+
+| **Query** | **Expected impact** | **Change** |
+|----------------------------|------------------------|------------------------------------------------------------------|
+
+## Changes to libraries
+
+* The library `VCS.qll` and all queries that imported it have been removed.
+* The data-flow library has been improved, which affects most security queries by potentially
+ adding more results. Flow through functions now takes nested field reads/writes into account.
+ For example, the library is able to track flow from `taint()` to `sink()` via the method
+ `getf2f1()` in
+ ```c
+ struct C {
+ int f1;
+ };
+
+ struct C2
+ {
+ C f2;
+
+ int getf2f1() {
+ return f2.f1; // Nested field read
+ }
+
+ void m() {
+ f2.f1 = taint();
+ sink(getf2f1()); // NEW: taint() reaches here
+ }
+ };
+ ```
+* The security pack taint tracking library (`semmle.code.cpp.security.TaintTracking`) now considers that equality checks may block the flow of taint. This results in fewer false positive results from queries that use this library.
+* The length of a tainted string (such as the return value of a call to `strlen` or `strftime` with tainted parameters) is no longer itself considered tainted by the `models` library. This leads to fewer false positive results in queries that use any of our taint libraries.
diff --git a/change-notes/1.25/analysis-csharp.md b/change-notes/1.25/analysis-csharp.md
new file mode 100644
index 00000000000..de4761c9248
--- /dev/null
+++ b/change-notes/1.25/analysis-csharp.md
@@ -0,0 +1,54 @@
+# Improvements to C# analysis
+
+The following changes in version 1.25 affect C# analysis in all applications.
+
+## New queries
+
+| **Query** | **Tags** | **Purpose** |
+|-----------------------------|-----------|--------------------------------------------------------------------|
+
+
+## Changes to existing queries
+
+| **Query** | **Expected impact** | **Change** |
+|------------------------------|------------------------|-----------------------------------|
+
+
+## Removal of old queries
+
+## Changes to code extraction
+
+* Index initializers, of the form `{ [1] = "one" }`, are extracted correctly. Previously, the kind of the
+ expression was incorrect, and the index was not extracted.
+
+## Changes to libraries
+
+* The class `UnboundGeneric` has been refined to only be those declarations that actually
+ have type parameters. This means that non-generic nested types inside constructed types,
+ such as `A.B`, no longer are considered unbound generics. (Such nested types do,
+ however, still have relevant `.getSourceDeclaration()`s, for example `A<>.B`.)
+* The data-flow library has been improved, which affects most security queries by potentially
+ adding more results. Flow through methods now takes nested field reads/writes into account.
+ For example, the library is able to track flow from `"taint"` to `Sink()` via the method
+ `GetF2F1()` in
+ ```csharp
+ class C1
+ {
+ string F1;
+ }
+
+ class C2
+ {
+ C1 F2;
+
+ string GetF2F1() => F2.F1; // Nested field read
+
+ void M()
+ {
+ F2 = new C1() { F1 = "taint" };
+ Sink(GetF2F1()); // NEW: "taint" reaches here
+ }
+ }
+ ```
+
+## Changes to autobuilder
diff --git a/change-notes/1.25/analysis-java.md b/change-notes/1.25/analysis-java.md
new file mode 100644
index 00000000000..7cdd9e491a2
--- /dev/null
+++ b/change-notes/1.25/analysis-java.md
@@ -0,0 +1,41 @@
+# Improvements to Java analysis
+
+The following changes in version 1.25 affect Java analysis in all applications.
+
+## General improvements
+
+## New queries
+
+| **Query** | **Tags** | **Purpose** |
+|-----------------------------|-----------|--------------------------------------------------------------------|
+
+
+## Changes to existing queries
+
+| **Query** | **Expected impact** | **Change** |
+|------------------------------|------------------------|-----------------------------------|
+
+
+## Changes to libraries
+
+* The data-flow library has been improved, which affects most security queries by potentially
+ adding more results. Flow through methods now takes nested field reads/writes into account.
+ For example, the library is able to track flow from `"taint"` to `sink()` via the method
+ `getF2F1()` in
+ ```java
+ class C1 {
+ String f1;
+ C1(String f1) { this.f1 = f1; }
+ }
+
+ class C2 {
+ C1 f2;
+ String getF2F1() {
+ return this.f2.f1; // Nested field read
+ }
+ void m() {
+ this.f2 = new C1("taint");
+ sink(this.getF2F1()); // NEW: "taint" reaches here
+ }
+ }
+ ```
diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md
new file mode 100644
index 00000000000..5a99e47cc39
--- /dev/null
+++ b/change-notes/1.25/analysis-javascript.md
@@ -0,0 +1,89 @@
+# Improvements to JavaScript analysis
+
+## General improvements
+
+* Support for the following frameworks and libraries has been improved:
+ - [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
+ - [bluebird](http://bluebirdjs.com/)
+ - [express](https://www.npmjs.com/package/express)
+ - [fastify](https://www.npmjs.com/package/fastify)
+ - [fstream](https://www.npmjs.com/package/fstream)
+ - [jGrowl](https://github.com/stanlemon/jGrowl)
+ - [jQuery](https://jquery.com/)
+ - [marsdb](https://www.npmjs.com/package/marsdb)
+ - [minimongo](https://www.npmjs.com/package/minimongo/)
+ - [mssql](https://www.npmjs.com/package/mssql)
+ - [mysql](https://www.npmjs.com/package/mysql)
+ - [pg](https://www.npmjs.com/package/pg)
+ - [sequelize](https://www.npmjs.com/package/sequelize)
+ - [spanner](https://www.npmjs.com/package/spanner)
+ - [sqlite](https://www.npmjs.com/package/sqlite)
+ - [ssh2-streams](https://www.npmjs.com/package/ssh2-streams)
+ - [ssh2](https://www.npmjs.com/package/ssh2)
+ - [webpack-dev-server](https://www.npmjs.com/package/webpack-dev-server)
+
+* TypeScript 3.9 is now supported.
+
+* The analysis of sanitizers has improved, leading to more accurate
+ results from the security queries.
+
+## New queries
+
+| **Query** | **Tags** | **Purpose** |
+|---------------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Cross-site scripting through DOM (`js/xss-through-dom`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities where existing text from the DOM is used as HTML. Results are not shown on LGTM by default. |
+| Incomplete HTML attribute sanitization (`js/incomplete-html-attribute-sanitization`) | security, external/cwe/cwe-20, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities due to incomplete sanitization of HTML meta-characters. Results are shown on LGTM by default. |
+| Unsafe expansion of self-closing HTML tag (`js/unsafe-html-expansion`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities caused by unsafe expansion of self-closing HTML tags. |
+| Unsafe shell command constructed from library input (`js/shell-command-constructed-from-input`) | correctness, security, external/cwe/cwe-078, external/cwe/cwe-088 | Highlights potential command injections due to a shell command being constructed from library inputs. Results are shown on LGTM by default. |
+
+## Changes to existing queries
+
+| **Query** | **Expected impact** | **Change** |
+|--------------------------------|------------------------------|---------------------------------------------------------------------------|
+| Client-side cross-site scripting (`js/xss`) | Fewer results | This query now recognizes additional safe patterns of constructing HTML. |
+| Client-side URL redirect (`js/client-side-unvalidated-url-redirection`) | Fewer results | This query now recognizes additional safe patterns of doing URL redirects. |
+| Code injection (`js/code-injection`) | More results | More potential vulnerabilities involving NoSQL code operators are now recognized. |
+| Expression has no effect (`js/useless-expression`) | Fewer results | This query no longer flags an expression when that expression is the only content of the containing file. |
+| Hard-coded credentials (`js/hardcoded-credentials`) | More results | This query now recognizes hard-coded credentials sent via HTTP authorization headers. |
+| Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | More results | This query now recognizes additional url scheme checks. |
+| Misspelled variable name (`js/misspelled-variable-name`) | Message changed | The message for this query now correctly identifies the misspelled variable in additional cases. |
+| Non-linear pattern (`js/non-linear-pattern`) | Fewer duplicates and message changed | This query now generates fewer duplicate alerts and has a clearer explanation in case of type annotations used in a pattern. |
+| Prototype pollution in utility function (`js/prototype-pollution-utility`) | More results | This query now recognizes additional utility functions as vulnerable to prototype polution. |
+| Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional command execution calls. |
+| Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional file system calls. |
+| Unknown directive (`js/unknown-directive`) | Fewer results | This query no longer flags directives generated by the Babel compiler. |
+| Unused property (`js/unused-property`) | Fewer results | This query no longer flags properties of objects that are operands of `yield` expressions. |
+| Zip Slip (`js/zipslip`) | More results | This query now recognizes additional vulnerabilities. |
+
+The following low-precision queries are no longer run by default on LGTM (their results already were not displayed):
+
+ - `js/angular/dead-event-listener`
+ - `js/angular/unused-dependency`
+ - `js/bitwise-sign-check`
+ - `js/comparison-of-identical-expressions`
+ - `js/conflicting-html-attribute`
+ - `js/ignored-setter-parameter`
+ - `js/jsdoc/malformed-param-tag`
+ - `js/jsdoc/missing-parameter`
+ - `js/jsdoc/unknown-parameter`
+ - `js/json-in-javascript-file`
+ - `js/misspelled-identifier`
+ - `js/nested-loops-with-same-variable`
+ - `js/node/cyclic-import`
+ - `js/node/unused-npm-dependency`
+ - `js/omitted-array-element`
+ - `js/return-outside-function`
+ - `js/single-run-loop`
+ - `js/too-many-parameters`
+ - `js/unused-property`
+ - `js/useless-assignment-to-global`
+
+## Changes to libraries
+
+* A library `semmle.javascript.explore.CallGraph` has been added to help write queries for exploring the call graph.
+* Added data flow for `Map` and `Set`, and added matching type-tracking steps that can accessed using the `CollectionsTypeTracking` module.
+* The data-flow node representing a parameter or destructuring pattern is now always the `ValueNode` corresponding to that AST node. This has a few consequences:
+ - `Parameter.flow()` now gets the correct data flow node for a parameter. Previously this had a result, but the node was disconnected from the data flow graph.
+ - `ParameterNode.asExpr()` and `.getAstNode()` now gets the parameter's AST node, whereas previously it had no result.
+ - `Expr.flow()` now has a more meaningful result for destructuring patterns. Previously this node was disconnected from the data flow graph. Now it represents the values being destructured by the pattern.
+* The global data-flow and taint-tracking libraries now model indirect parameter accesses through the `arguments` object in some cases, which may lead to additional results from some of the security queries, particularly "Prototype pollution in utility function".
diff --git a/change-notes/1.25/analysis-python.md b/change-notes/1.25/analysis-python.md
new file mode 100644
index 00000000000..5d0fc69ec80
--- /dev/null
+++ b/change-notes/1.25/analysis-python.md
@@ -0,0 +1,22 @@
+# Improvements to Python analysis
+
+The following changes in version 1.25 affect Python analysis in all applications.
+
+## General improvements
+
+
+## New queries
+
+| **Query** | **Tags** | **Purpose** |
+|-----------------------------|-----------|--------------------------------------------------------------------|
+
+
+## Changes to existing queries
+
+| **Query** | **Expected impact** | **Change** |
+|----------------------------|------------------------|------------------------------------------------------------------|
+
+
+## Changes to libraries
+
+* Importing `semmle.python.web.HttpRequest` will no longer import `UntrustedStringKind` transitively. `UntrustedStringKind` is the most commonly used non-abstract subclass of `ExternalStringKind`. If not imported (by one mean or another), taint-tracking queries that concern `ExternalStringKind` will not produce any results. Please ensure such queries contain an explicit import (`import semmle.python.security.strings.Untrusted`).
diff --git a/config/identical-files.json b/config/identical-files.json
index a5af067ab5d..35c9169df56 100644
--- a/config/identical-files.json
+++ b/config/identical-files.json
@@ -53,114 +53,122 @@
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll"
],
"IR IRBlock": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRBlock.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRBlock.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll"
],
"IR IRVariable": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll"
],
"IR IRFunction": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRFunction.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRFunction.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/IRFunction.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRFunction.qll"
],
"IR Operand": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Operand.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Operand.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/Operand.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll"
],
"IR IRType": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/IRType.qll"
+ "csharp/ql/src/experimental/ir/implementation/IRType.qll"
],
"IR IRConfiguration": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/IRConfiguration.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/IRConfiguration.qll"
+ "csharp/ql/src/experimental/ir/implementation/IRConfiguration.qll"
],
"IR UseSoundEscapeAnalysis": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/UseSoundEscapeAnalysis.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/UseSoundEscapeAnalysis.qll"
+ "csharp/ql/src/experimental/ir/implementation/UseSoundEscapeAnalysis.qll"
+ ],
+ "IR IRFunctionBase": [
+ "cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll",
+ "csharp/ql/src/experimental/ir/implementation/internal/IRFunctionBase.qll"
],
"IR Operand Tag": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/internal/OperandTag.qll"
+ "csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll"
+ ],
+ "IR TInstruction":[
+ "cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll",
+ "csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll"
],
"IR TIRVariable":[
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TIRVariable.qll"
+ "csharp/ql/src/experimental/ir/implementation/internal/TIRVariable.qll"
],
"IR IR": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IR.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IR.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/IR.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll"
],
- "IR 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/unaliased_ssa/IRSanity.qll"
+ "IR IRConsistency": [
+ "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll",
+ "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll",
+ "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll",
+ "csharp/ql/src/experimental/ir/implementation/raw/IRConsistency.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRConsistency.qll"
],
"IR PrintIR": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/PrintIR.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/PrintIR.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll"
],
"IR IntegerConstant": [
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll",
- "csharp/ql/src/semmle/code/csharp/ir/internal/IntegerConstant.qll"
+ "csharp/ql/src/experimental/ir/internal/IntegerConstant.qll"
],
"IR IntegerInteval": [
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerInterval.qll",
- "csharp/ql/src/semmle/code/csharp/ir/internal/IntegerInterval.qll"
+ "csharp/ql/src/experimental/ir/internal/IntegerInterval.qll"
],
"IR IntegerPartial": [
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerPartial.qll",
- "csharp/ql/src/semmle/code/csharp/ir/internal/IntegerPartial.qll"
+ "csharp/ql/src/experimental/ir/internal/IntegerPartial.qll"
],
"IR Overlap": [
"cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll",
- "csharp/ql/src/semmle/code/csharp/ir/internal/Overlap.qll"
+ "csharp/ql/src/experimental/ir/internal/Overlap.qll"
],
"IR EdgeKind": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/EdgeKind.qll"
+ "csharp/ql/src/experimental/ir/implementation/EdgeKind.qll"
],
"IR MemoryAccessKind": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/MemoryAccessKind.qll"
+ "csharp/ql/src/experimental/ir/implementation/MemoryAccessKind.qll"
],
"IR TempVariableTag": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/TempVariableTag.qll"
+ "csharp/ql/src/experimental/ir/implementation/TempVariableTag.qll"
],
"IR Opcode": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/Opcode.qll"
+ "csharp/ql/src/experimental/ir/implementation/Opcode.qll"
],
- "IR SSASanity": [
- "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSASanity.qll",
- "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSASanity.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSASanity.qll"
+ "IR SSAConsistency": [
+ "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll",
+ "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll"
],
"C++ IR InstructionImports": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll",
@@ -177,6 +185,11 @@
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRBlockImports.qll"
],
+ "C++ IR IRFunctionImports": [
+ "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll",
+ "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll",
+ "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll"
+ ],
"C++ IR IRVariableImports": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRVariableImports.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll",
@@ -199,7 +212,7 @@
"SSA AliasAnalysis": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll"
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll"
],
"C++ SSA AliasAnalysisImports": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll",
@@ -212,42 +225,42 @@
],
"IR SSA SimpleSSA": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll"
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll"
],
"IR AliasConfiguration (unaliased_ssa)": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll"
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll"
],
"IR SSA SSAConstruction": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll"
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll"
],
"IR SSA PrintSSA": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll"
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/PrintSSA.qll"
],
"IR ValueNumberInternal": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll"
],
"C++ IR ValueNumber": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/ValueNumbering.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/gvn/ValueNumbering.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll"
],
"C++ IR PrintValueNumbering": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/PrintValueNumbering.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/PrintValueNumbering.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/PrintValueNumbering.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/gvn/PrintValueNumbering.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll"
],
"C++ IR ConstantAnalysis": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll",
@@ -276,32 +289,36 @@
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
],
"C# IR InstructionImports": [
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionImports.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/internal/InstructionImports.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/InstructionImports.qll"
],
"C# IR IRImports": [
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRImports.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRImports.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/internal/IRImports.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRImports.qll"
],
"C# IR IRBlockImports": [
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRBlockImports.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/internal/IRBlockImports.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll"
+ ],
+ "C# IR IRFunctionImports": [
+ "csharp/ql/src/experimental/ir/implementation/raw/internal/IRFunctionImports.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll"
],
"C# IR IRVariableImports": [
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRVariableImports.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/internal/IRVariableImports.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll"
],
"C# IR OperandImports": [
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/OperandImports.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/OperandImports.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/internal/OperandImports.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/OperandImports.qll"
],
"C# IR PrintIRImports": [
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/PrintIRImports.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/internal/PrintIRImports.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll"
],
"C# IR ValueNumberingImports": [
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll"
],
"XML": [
"cpp/ql/src/semmle/code/cpp/XML.qll",
diff --git a/config/sync-files.py b/config/sync-files.py
index 30be173c18b..9645d42f1e5 100644
--- a/config/sync-files.py
+++ b/config/sync-files.py
@@ -59,21 +59,32 @@ def file_checksum(filename):
return hashlib.sha1(file_handle.read()).hexdigest()
def check_group(group_name, files, master_file_picker, emit_error):
- checksums = {file_checksum(f) for f in files}
-
- if len(checksums) == 1:
+ extant_files = [f for f in files if path.isfile(f)]
+ if len(extant_files) == 0:
+ emit_error(__file__, 0, "No files found from group '" + group_name + "'.")
+ emit_error(__file__, 0,
+ "Create one of the following files, and then run this script with "
+ "the --latest switch to sync it to the other file locations.")
+ for filename in files:
+ emit_error(__file__, 0, " " + filename)
return
- master_file = master_file_picker(files)
+ checksums = {file_checksum(f) for f in extant_files}
+
+ if len(checksums) == 1 and len(extant_files) == len(files):
+ # All files are present and identical.
+ return
+
+ master_file = master_file_picker(extant_files)
if master_file is None:
emit_error(__file__, 0,
"Files from group '"+ group_name +"' not in sync.")
emit_error(__file__, 0,
"Run this script with a file-name argument among the "
"following to overwrite the remaining files with the contents "
- "of that file or run with the --latest switch to update each "
+ "of that file, or run with the --latest switch to update each "
"group of files from the most recently modified file in the group.")
- for filename in files:
+ for filename in extant_files:
emit_error(__file__, 0, " " + filename)
else:
print(" Syncing others from", master_file)
@@ -81,7 +92,8 @@ def check_group(group_name, files, master_file_picker, emit_error):
if filename == master_file:
continue
print(" " + filename)
- os.replace(filename, filename + '~')
+ if path.isfile(filename):
+ os.replace(filename, filename + '~')
shutil.copy(master_file, filename)
print(" Backups written with '~' appended to file names")
@@ -107,7 +119,7 @@ def choose_latest_file(files):
local_error_count = 0
def emit_local_error(path, line, error):
- print('ERROR: ' + path + ':' + line + " - " + error)
+ print('ERROR: ' + path + ':' + str(line) + " - " + error)
global local_error_count
local_error_count += 1
diff --git a/cpp/ql/src/Critical/FileClosed.qll b/cpp/ql/src/Critical/FileClosed.qll
index 1e782259156..e2ba3c25b81 100644
--- a/cpp/ql/src/Critical/FileClosed.qll
+++ b/cpp/ql/src/Critical/FileClosed.qll
@@ -1,5 +1,6 @@
import semmle.code.cpp.pointsto.PointsTo
+/** Holds if there exists a call to a function that might close the file specified by `e`. */
predicate closed(Expr e) {
fcloseCall(_, e) or
exists(ExprCall c |
@@ -8,10 +9,19 @@ predicate closed(Expr e) {
)
}
+/** An expression for which there exists a function call that might close it. */
class ClosedExpr extends PointsToExpr {
ClosedExpr() { closed(this) }
override predicate interesting() { closed(this) }
}
+/**
+ * Holds if `fc` is a call to a function that opens a file that might be closed. For example:
+ * ```
+ * FILE* f = fopen("file.txt", "r");
+ * ...
+ * fclose(f);
+ * ```
+ */
predicate fopenCallMayBeClosed(FunctionCall fc) { fopenCall(fc) and anythingPointsTo(fc) }
diff --git a/cpp/ql/src/Critical/LoopBounds.qll b/cpp/ql/src/Critical/LoopBounds.qll
index f10e62c909f..b2cde8d6fe8 100644
--- a/cpp/ql/src/Critical/LoopBounds.qll
+++ b/cpp/ql/src/Critical/LoopBounds.qll
@@ -2,12 +2,24 @@
import cpp
+/**
+ * An assignment to a variable with the value `0`. For example:
+ * ```
+ * int x;
+ * x = 0;
+ * ```
+ * but not:
+ * ```
+ * int x = 0;
+ * ```
+ */
class ZeroAssignment extends AssignExpr {
ZeroAssignment() {
this.getAnOperand() instanceof VariableAccess and
this.getAnOperand() instanceof Zero
}
+ /** Gets a variable that is assigned the value `0`. */
Variable assignedVariable() { result.getAnAccess() = this.getAnOperand() }
}
diff --git a/cpp/ql/src/Critical/MemoryFreed.qll b/cpp/ql/src/Critical/MemoryFreed.qll
index 880199e54c9..44557503e43 100644
--- a/cpp/ql/src/Critical/MemoryFreed.qll
+++ b/cpp/ql/src/Critical/MemoryFreed.qll
@@ -9,10 +9,19 @@ private predicate freed(Expr e) {
)
}
+/** An expression that might be deallocated. */
class FreedExpr extends PointsToExpr {
FreedExpr() { freed(this) }
override predicate interesting() { freed(this) }
}
+/**
+ * An allocation expression that might be deallocated. For example:
+ * ```
+ * int* p = new int;
+ * ...
+ * delete p;
+ * ```
+ */
predicate allocMayBeFreed(AllocationExpr alloc) { anythingPointsTo(alloc) }
diff --git a/cpp/ql/src/Critical/Negativity.qll b/cpp/ql/src/Critical/Negativity.qll
index 161fe234967..e9c0a3d2410 100644
--- a/cpp/ql/src/Critical/Negativity.qll
+++ b/cpp/ql/src/Critical/Negativity.qll
@@ -1,10 +1,19 @@
import cpp
+/**
+ * Holds if `val` is an access to the variable `v`, or if `val`
+ * is an assignment with an access to `v` on the left-hand side.
+ */
predicate valueOfVar(Variable v, Expr val) {
val = v.getAnAccess() or
val.(AssignExpr).getLValue() = v.getAnAccess()
}
+/**
+ * Holds if either:
+ * - `cond` is an (in)equality expression that compares the variable `v` to the value `-1`, or
+ * - `cond` is a relational expression that compares the variable `v` to a constant.
+ */
predicate boundsCheckExpr(Variable v, Expr cond) {
exists(EQExpr eq |
cond = eq and
@@ -43,6 +52,18 @@ predicate boundsCheckExpr(Variable v, Expr cond) {
)
}
+/**
+ * Holds if `node` is an expression in a conditional statement and `succ` is an
+ * immediate successor of `node` that may be reached after evaluating `node`.
+ * For example, given
+ * ```
+ * if (a < 10 && b) func1();
+ * else func2();
+ * ```
+ * this predicate holds when either:
+ * - `node` is `a < 10` and `succ` is `func2()` or `b`, or
+ * - `node` is `b` and `succ` is `func1()` or `func2()`
+ */
predicate conditionalSuccessor(ControlFlowNode node, ControlFlowNode succ) {
if node.isCondition()
then succ = node.getATrueSuccessor() or succ = node.getAFalseSuccessor()
@@ -52,6 +73,12 @@ predicate conditionalSuccessor(ControlFlowNode node, ControlFlowNode succ) {
)
}
+/**
+ * Holds if the current value of the variable `v` at control-flow
+ * node `n` has been used either in:
+ * - an (in)equality comparison with the value `-1`, or
+ * - a relational comparison that compares `v` to a constant.
+ */
predicate boundsChecked(Variable v, ControlFlowNode node) {
exists(Expr test |
boundsCheckExpr(v, test) and
@@ -63,6 +90,14 @@ predicate boundsChecked(Variable v, ControlFlowNode node) {
)
}
+/**
+ * Holds if `cond` compares `v` to some common error values. Specifically, this
+ * predicate holds when:
+ * - `cond` checks that `v` is equal to `-1`, or
+ * - `cond` checks that `v` is less than `0`, or
+ * - `cond` checks that `v` is less than or equal to `-1`, or
+ * - `cond` checks that `v` is not some common success value (see `successCondition`).
+ */
predicate errorCondition(Variable v, Expr cond) {
exists(EQExpr eq |
cond = eq and
@@ -88,6 +123,14 @@ predicate errorCondition(Variable v, Expr cond) {
)
}
+/**
+ * Holds if `cond` compares `v` to some common success values. Specifically, this
+ * predicate holds when:
+ * - `cond` checks that `v` is not equal to `-1`, or
+ * - `cond` checks that `v` is greater than or equal than `0`, or
+ * - `cond` checks that `v` is greater than `-1`, or
+ * - `cond` checks that `v` is not some common error value (see `errorCondition`).
+ */
predicate successCondition(Variable v, Expr cond) {
exists(NEExpr ne |
cond = ne and
@@ -113,6 +156,11 @@ predicate successCondition(Variable v, Expr cond) {
)
}
+/**
+ * Holds if there exists a comparison operation that checks whether `v`
+ * represents some common *error* values, and `n` may be reached
+ * immediately following the comparison operation.
+ */
predicate errorSuccessor(Variable v, ControlFlowNode n) {
exists(Expr cond |
errorCondition(v, cond) and n = cond.getATrueSuccessor()
@@ -121,6 +169,11 @@ predicate errorSuccessor(Variable v, ControlFlowNode n) {
)
}
+/**
+ * Holds if there exists a comparison operation that checks whether `v`
+ * represents some common *success* values, and `n` may be reached
+ * immediately following the comparison operation.
+ */
predicate successSuccessor(Variable v, ControlFlowNode n) {
exists(Expr cond |
successCondition(v, cond) and n = cond.getATrueSuccessor()
@@ -129,6 +182,10 @@ predicate successSuccessor(Variable v, ControlFlowNode n) {
)
}
+/**
+ * Holds if the current value of the variable `v` at control-flow node
+ * `n` may have been checked against a common set of *error* values.
+ */
predicate checkedError(Variable v, ControlFlowNode n) {
errorSuccessor(v, n)
or
@@ -139,6 +196,10 @@ predicate checkedError(Variable v, ControlFlowNode n) {
)
}
+/**
+ * Holds if the current value of the variable `v` at control-flow node
+ * `n` may have been checked against a common set of *success* values.
+ */
predicate checkedSuccess(Variable v, ControlFlowNode n) {
successSuccessor(v, n)
or
diff --git a/cpp/ql/src/Documentation/CaptionedComments.qll b/cpp/ql/src/Documentation/CaptionedComments.qll
index c955f97a029..0ccd678e79f 100644
--- a/cpp/ql/src/Documentation/CaptionedComments.qll
+++ b/cpp/ql/src/Documentation/CaptionedComments.qll
@@ -4,6 +4,10 @@
import cpp
+/**
+ * Gets a string representation of the comment `c` containing the caption 'TODO' or 'FIXME'.
+ * If `c` spans multiple lines, all lines after the first are abbreviated as [...].
+ */
string getCommentTextCaptioned(Comment c, string caption) {
(caption = "TODO" or caption = "FIXME") and
exists(
diff --git a/cpp/ql/src/Documentation/CommentedOutCode.qll b/cpp/ql/src/Documentation/CommentedOutCode.qll
index 14e74b0a20c..172474bbe29 100644
--- a/cpp/ql/src/Documentation/CommentedOutCode.qll
+++ b/cpp/ql/src/Documentation/CommentedOutCode.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes and predicates for identifying C/C++ comments that look like code.
+ */
+
import cpp
/**
@@ -137,8 +141,14 @@ class CommentBlock extends Comment {
)
}
+ /**
+ * Gets the last comment associated with this comment block.
+ */
Comment lastComment() { result = this.getComment(max(int i | exists(this.getComment(i)))) }
+ /**
+ * Gets the contents of the `i`'th comment associated with this comment block.
+ */
string getLine(int i) {
this instanceof CStyleComment and
result = this.getContents().regexpCapture("(?s)/\\*+(.*)\\*+/", 1).splitAt("\n", i)
@@ -146,14 +156,24 @@ class CommentBlock extends Comment {
this instanceof CppStyleComment and result = this.getComment(i).getContents().suffix(2)
}
+ /**
+ * Gets the number of lines in the comments associated with this comment block.
+ */
int numLines() {
result = strictcount(int i, string line | line = this.getLine(i) and line.trim() != "")
}
+ /**
+ * Gets the number of lines that look like code in the comments associated with this comment block.
+ */
int numCodeLines() {
result = strictcount(int i, string line | line = this.getLine(i) and looksLikeCode(line))
}
+ /**
+ * Holds if the comment block is a C-style comment, and each
+ * comment line starts with a *.
+ */
predicate isDocumentation() {
// If a C-style comment starts each line with a *, then it's
// probably documentation rather than code.
@@ -161,6 +181,12 @@ class CommentBlock extends Comment {
forex(int i | i in [1 .. this.numLines() - 1] | this.getLine(i).trim().matches("*%"))
}
+ /**
+ * Holds if this comment block looks like code that has been commented out. Specifically:
+ * 1. It does not look like documentation (see `isDocumentation`).
+ * 2. It is not in a header file without any declaration entries or top level declarations.
+ * 3. More than half of the lines in the comment block look like code.
+ */
predicate isCommentedOutCode() {
not this.isDocumentation() and
not this.getFile().(HeaderFile).noTopLevelCode() and
diff --git a/cpp/ql/src/JPL_C/LOC-3/Rule 17/BasicIntTypes.ql b/cpp/ql/src/JPL_C/LOC-3/Rule 17/BasicIntTypes.ql
index 72852f0b86c..965bb8440b0 100644
--- a/cpp/ql/src/JPL_C/LOC-3/Rule 17/BasicIntTypes.ql
+++ b/cpp/ql/src/JPL_C/LOC-3/Rule 17/BasicIntTypes.ql
@@ -30,7 +30,7 @@ predicate allowedTypedefs(TypedefType t) {
* Gets a type which appears literally in the declaration of `d`.
*/
Type getAnImmediateUsedType(Declaration d) {
- d.isDefined() and
+ d.hasDefinition() and
(
result = d.(Function).getType() or
result = d.(Variable).getType()
diff --git a/cpp/ql/src/Likely Bugs/RedundantNullCheckSimple.ql b/cpp/ql/src/Likely Bugs/RedundantNullCheckSimple.ql
index 6ba835acbdc..65ba665dff2 100644
--- a/cpp/ql/src/Likely Bugs/RedundantNullCheckSimple.ql
+++ b/cpp/ql/src/Likely Bugs/RedundantNullCheckSimple.ql
@@ -23,7 +23,7 @@ import semmle.code.cpp.ir.ValueNumbering
class NullInstruction extends ConstantValueInstruction {
NullInstruction() {
this.getValue() = "0" and
- this.getResultType().getUnspecifiedType() instanceof PointerType
+ this.getResultIRType() instanceof IRAddressType
}
}
@@ -44,8 +44,8 @@ predicate explicitNullTestOfInstruction(Instruction checked, Instruction bool) {
bool =
any(ConvertInstruction convert |
checked = convert.getUnary() and
- convert.getResultType() instanceof BoolType and
- checked.getResultType() instanceof PointerType
+ convert.getResultIRType() instanceof IRBooleanType and
+ checked.getResultIRType() instanceof IRAddressType
)
}
diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.qll b/cpp/ql/src/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.qll
index 39b4f5902da..2dced5d8d84 100644
--- a/cpp/ql/src/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.qll
+++ b/cpp/ql/src/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.qll
@@ -6,6 +6,7 @@
import cpp
+pragma[inline]
private predicate arithTypesMatch(Type arg, Type parm) {
arg = parm
or
diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.qll b/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.qll
index b65d256f45c..6f3f4d43e9a 100644
--- a/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.qll
+++ b/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.qll
@@ -6,29 +6,50 @@
import cpp
-// True if function was ()-declared, but not (void)-declared or K&R-defined
+/**
+ * Holds if `fde` has a parameter declaration that's clear on the minimum
+ * number of parameters. This is essentially true for everything except
+ * `()`-declarations.
+ */
+private predicate hasDefiniteNumberOfParameters(FunctionDeclarationEntry fde) {
+ fde.hasVoidParamList()
+ or
+ fde.getNumberOfParameters() > 0
+ or
+ fde.isDefinition()
+}
+
+/* Holds if function was ()-declared, but not (void)-declared or K&R-defined. */
private predicate hasZeroParamDecl(Function f) {
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
- not fde.hasVoidParamList() and fde.getNumberOfParameters() = 0 and not fde.isDefinition()
+ not hasDefiniteNumberOfParameters(fde)
)
}
-// True if this file (or header) was compiled as a C file
+/* Holds if this file (or header) was compiled as a C file. */
private predicate isCompiledAsC(File f) {
f.compiledAsC()
or
exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
}
+/** Holds if `fc` is a call to `f` with too few arguments. */
predicate tooFewArguments(FunctionCall fc, Function f) {
f = fc.getTarget() and
not f.isVarargs() and
not f instanceof BuiltInFunction and
+ // This query should only have results on C (not C++) functions that have a
+ // `()` parameter list somewhere. If it has results on other functions, then
+ // it's probably because the extractor only saw a partial compilation.
hasZeroParamDecl(f) and
isCompiledAsC(f.getFile()) and
- // There is an explicit declaration of the function whose parameter count is larger
- // than the number of call arguments
- exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
+ // Produce an alert when all declarations that are authoritative on the
+ // parameter count specify a parameter count larger than the number of call
+ // arguments.
+ forex(FunctionDeclarationEntry fde |
+ fde = f.getADeclarationEntry() and
+ hasDefiniteNumberOfParameters(fde)
+ |
fde.getNumberOfParameters() > fc.getNumberOfArguments()
)
}
diff --git a/cpp/ql/src/Metrics/History/HChurn.ql b/cpp/ql/src/Metrics/History/HChurn.ql
deleted file mode 100644
index 7ff156b5300..00000000000
--- a/cpp/ql/src/Metrics/History/HChurn.ql
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * @name Churned lines per file
- * @description Number of churned lines per file, across the revision
- * history in the database.
- * @kind treemap
- * @id cpp/historical-churn
- * @treemap.warnOn highValues
- * @metricType file
- * @metricAggregate avg sum max
- * @tags external-data
- * @deprecated
- */
-
-import cpp
-import external.VCS
-
-from File f, int n
-where
- n =
- sum(Commit entry, int churn |
- churn = entry.getRecentChurnForFile(f) and
- not artificialChange(entry)
- |
- churn
- ) and
- exists(f.getMetrics().getNumberOfLinesOfCode())
-select f, n order by n desc
diff --git a/cpp/ql/src/Metrics/History/HLinesAdded.ql b/cpp/ql/src/Metrics/History/HLinesAdded.ql
deleted file mode 100644
index ce03c5b4190..00000000000
--- a/cpp/ql/src/Metrics/History/HLinesAdded.ql
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * @name Added lines per file
- * @description Number of added lines per file, across the revision
- * history in the database.
- * @kind treemap
- * @id cpp/historical-lines-added
- * @treemap.warnOn highValues
- * @metricType file
- * @metricAggregate avg sum max
- * @tags external-data
- * @deprecated
- */
-
-import cpp
-import external.VCS
-
-from File f, int n
-where
- n =
- sum(Commit entry, int churn |
- churn = entry.getRecentAdditionsForFile(f) and
- not artificialChange(entry)
- |
- churn
- ) and
- exists(f.getMetrics().getNumberOfLinesOfCode())
-select f, n order by n desc
diff --git a/cpp/ql/src/Metrics/History/HLinesDeleted.ql b/cpp/ql/src/Metrics/History/HLinesDeleted.ql
deleted file mode 100644
index 3d8ce78b6c4..00000000000
--- a/cpp/ql/src/Metrics/History/HLinesDeleted.ql
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * @name Deleted lines per file
- * @description Number of deleted lines per file, across the revision
- * history in the database.
- * @kind treemap
- * @id cpp/historical-lines-deleted
- * @treemap.warnOn highValues
- * @metricType file
- * @metricAggregate avg sum max
- * @tags external-data
- * @deprecated
- */
-
-import cpp
-import external.VCS
-
-from File f, int n
-where
- n =
- sum(Commit entry, int churn |
- churn = entry.getRecentDeletionsForFile(f) and
- not artificialChange(entry)
- |
- churn
- ) and
- exists(f.getMetrics().getNumberOfLinesOfCode())
-select f, n order by n desc
diff --git a/cpp/ql/src/Metrics/History/HNumberOfAuthors.ql b/cpp/ql/src/Metrics/History/HNumberOfAuthors.ql
deleted file mode 100644
index 2d8f1f382c3..00000000000
--- a/cpp/ql/src/Metrics/History/HNumberOfAuthors.ql
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * @name Number of authors
- * @description Number of distinct authors for each file.
- * @kind treemap
- * @id cpp/historical-number-of-authors
- * @treemap.warnOn highValues
- * @metricType file
- * @metricAggregate avg min max
- * @tags external-data
- * @deprecated
- */
-
-import cpp
-import external.VCS
-
-from File f
-where exists(f.getMetrics().getNumberOfLinesOfCode())
-select f, count(Author author | author.getAnEditedFile() = f)
diff --git a/cpp/ql/src/Metrics/History/HNumberOfChanges.ql b/cpp/ql/src/Metrics/History/HNumberOfChanges.ql
deleted file mode 100644
index 451dc575636..00000000000
--- a/cpp/ql/src/Metrics/History/HNumberOfChanges.ql
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * @name Number of file-level changes
- * @description The number of file-level changes made (by version
- * control history).
- * @kind treemap
- * @id cpp/historical-number-of-changes
- * @treemap.warnOn highValues
- * @metricType file
- * @metricAggregate avg min max sum
- * @tags external-data
- * @deprecated
- */
-
-import cpp
-import external.VCS
-
-from File f
-where exists(f.getMetrics().getNumberOfLinesOfCode())
-select f, count(Commit svn | f = svn.getAnAffectedFile() and not artificialChange(svn))
diff --git a/cpp/ql/src/Metrics/History/HNumberOfCoCommits.ql b/cpp/ql/src/Metrics/History/HNumberOfCoCommits.ql
deleted file mode 100644
index ecd5c4ccb0d..00000000000
--- a/cpp/ql/src/Metrics/History/HNumberOfCoCommits.ql
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * @name Number of co-committed files
- * @description The average number of other files that are touched
- * whenever a file is affected by a commit.
- * @kind treemap
- * @id cpp/historical-number-of-co-commits
- * @treemap.warnOn highValues
- * @metricType file
- * @metricAggregate avg min max
- * @tags external-data
- * @deprecated
- */
-
-import cpp
-import external.VCS
-
-int committedFiles(Commit commit) { result = count(commit.getAnAffectedFile()) }
-
-from File f
-where exists(f.getMetrics().getNumberOfLinesOfCode())
-select f, avg(Commit commit | commit.getAnAffectedFile() = f | committedFiles(commit) - 1)
diff --git a/cpp/ql/src/Metrics/History/HNumberOfReCommits.ql b/cpp/ql/src/Metrics/History/HNumberOfReCommits.ql
deleted file mode 100644
index fe98e66964e..00000000000
--- a/cpp/ql/src/Metrics/History/HNumberOfReCommits.ql
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * @name Number of re-commits for each file
- * @description A re-commit is taken to mean a commit to a file that
- * was touched less than five days ago.
- * @kind treemap
- * @id cpp/historical-number-of-re-commits
- * @treemap.warnOn highValues
- * @metricType file
- * @metricAggregate avg min max
- * @tags external-data
- * @deprecated
- */
-
-import cpp
-import external.VCS
-
-predicate inRange(Commit first, Commit second) {
- first.getAnAffectedFile() = second.getAnAffectedFile() and
- first != second and
- exists(int n |
- n = first.getDate().daysTo(second.getDate()) and
- n >= 0 and
- n < 5
- )
-}
-
-int recommitsForFile(File f) {
- result =
- count(Commit recommit |
- f = recommit.getAnAffectedFile() and
- exists(Commit prev | inRange(prev, recommit))
- )
-}
-
-from File f
-where exists(f.getMetrics().getNumberOfLinesOfCode())
-select f, recommitsForFile(f)
diff --git a/cpp/ql/src/Metrics/History/HNumberOfRecentAuthors.ql b/cpp/ql/src/Metrics/History/HNumberOfRecentAuthors.ql
deleted file mode 100644
index 7f40c8706d9..00000000000
--- a/cpp/ql/src/Metrics/History/HNumberOfRecentAuthors.ql
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * @name Number of recent authors
- * @description Number of distinct authors that have recently made
- * changes.
- * @kind treemap
- * @id cpp/historical-number-of-recent-authors
- * @treemap.warnOn highValues
- * @metricType file
- * @metricAggregate avg min max
- * @tags external-data
- * @deprecated
- */
-
-import cpp
-import external.VCS
-
-from File f
-where exists(f.getMetrics().getNumberOfLinesOfCode())
-select f,
- count(Author author |
- exists(Commit e |
- e = author.getACommit() and
- f = e.getAnAffectedFile() and
- e.daysToNow() <= 180 and
- not artificialChange(e)
- )
- )
diff --git a/cpp/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql b/cpp/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql
deleted file mode 100644
index 751449eb5aa..00000000000
--- a/cpp/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * @name Recently changed files
- * @description Number of files recently edited.
- * @kind treemap
- * @id cpp/historical-number-of-recent-changed-files
- * @treemap.warnOn highValues
- * @metricType file
- * @metricAggregate avg min max sum
- * @tags external-data
- * @deprecated
- */
-
-import cpp
-import external.VCS
-
-from File f
-where
- exists(Commit e |
- e.getAnAffectedFile() = f and
- e.daysToNow() <= 180 and
- not artificialChange(e)
- ) and
- exists(f.getMetrics().getNumberOfLinesOfCode())
-select f, 1
diff --git a/cpp/ql/src/Metrics/History/HNumberOfRecentChanges.ql b/cpp/ql/src/Metrics/History/HNumberOfRecentChanges.ql
deleted file mode 100644
index 886e8da6ca8..00000000000
--- a/cpp/ql/src/Metrics/History/HNumberOfRecentChanges.ql
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
- * @name Recent changes
- * @description Number of recent commits to this file.
- * @kind treemap
- * @id cpp/historical-number-of-recent-changes
- * @treemap.warnOn highValues
- * @metricType file
- * @metricAggregate avg min max sum
- * @tags external-data
- * @deprecated
- */
-
-import cpp
-import external.VCS
-
-from File f, int n
-where
- n =
- count(Commit e |
- e.getAnAffectedFile() = f and
- e.daysToNow() <= 180 and
- not artificialChange(e)
- ) and
- exists(f.getMetrics().getNumberOfLinesOfCode())
-select f, n order by n desc
diff --git a/cpp/ql/src/Options.qll b/cpp/ql/src/Options.qll
index 353d012e339..3c7e320dff6 100644
--- a/cpp/ql/src/Options.qll
+++ b/cpp/ql/src/Options.qll
@@ -4,7 +4,7 @@
*
* By default they fall back to the reasonable defaults provided in
* `DefaultOptions.qll`, but by modifying this file, you can customize
- * the standard Semmle analyses to give better results for your project.
+ * the standard analyses to give better results for your project.
*/
import cpp
diff --git a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql
index 2ed95e6cb55..54c6d1e7a96 100644
--- a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql
+++ b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql
@@ -15,31 +15,31 @@ import cpp
import semmle.code.cpp.security.TaintTracking
import TaintedWithPath
-predicate taintedChild(Expr e, Expr tainted) {
- (
- isAllocationExpr(e)
- or
- any(MulExpr me | me.getAChild() instanceof SizeofOperator) = e
- ) and
- tainted = e.getAChild() and
+/**
+ * Holds if `alloc` is an allocation, and `tainted` is a child of it that is a
+ * taint sink.
+ */
+predicate allocSink(Expr alloc, Expr tainted) {
+ isAllocationExpr(alloc) and
+ tainted = alloc.getAChild() and
tainted.getUnspecifiedType() instanceof IntegralType
}
class TaintedAllocationSizeConfiguration extends TaintTrackingConfiguration {
- override predicate isSink(Element tainted) { taintedChild(_, tainted) }
+ override predicate isSink(Element tainted) { allocSink(_, tainted) }
}
predicate taintedAllocSize(
- Expr e, Expr source, PathNode sourceNode, PathNode sinkNode, string taintCause
+ Expr source, Expr alloc, PathNode sourceNode, PathNode sinkNode, string taintCause
) {
isUserInput(source, taintCause) and
exists(Expr tainted |
- taintedChild(e, tainted) and
+ allocSink(alloc, tainted) and
taintedWithPath(source, tainted, sourceNode, sinkNode)
)
}
-from Expr e, Expr source, PathNode sourceNode, PathNode sinkNode, string taintCause
-where taintedAllocSize(e, source, sourceNode, sinkNode, taintCause)
-select e, sourceNode, sinkNode, "This allocation size is derived from $@ and might overflow",
+from Expr source, Expr alloc, PathNode sourceNode, PathNode sinkNode, string taintCause
+where taintedAllocSize(source, alloc, sourceNode, sinkNode, taintCause)
+select alloc, sourceNode, sinkNode, "This allocation size is derived from $@ and might overflow",
source, "user input (" + taintCause + ")"
diff --git a/cpp/ql/src/Security/CWE/CWE-457/InitializationFunctions.qll b/cpp/ql/src/Security/CWE/CWE-457/InitializationFunctions.qll
index a10e3df0bdd..c5285fdac57 100644
--- a/cpp/ql/src/Security/CWE/CWE-457/InitializationFunctions.qll
+++ b/cpp/ql/src/Security/CWE/CWE-457/InitializationFunctions.qll
@@ -198,12 +198,12 @@ class InitializationFunction extends Function {
)
or
// If we have no definition, we look at SAL annotations
- not this.isDefined() and
+ not this.hasDefinition() and
this.getParameter(i).(SALParameter).isOut() and
evidence = SuggestiveSALAnnotation()
or
// We have some external information that this function conditionally initializes
- not this.isDefined() and
+ not this.hasDefinition() and
any(ValidatedExternalCondInitFunction vc).isExternallyVerified(this, i) and
evidence = ExternalEvidence()
}
@@ -406,7 +406,7 @@ class ConditionalInitializationFunction extends InitializationFunction {
* Explicitly ignore pure virtual functions.
*/
- this.isDefined() and
+ this.hasDefinition() and
this.paramNotReassignedAt(this, i, c) and
not this instanceof PureVirtualFunction
)
@@ -616,11 +616,11 @@ private predicate functionSignature(Function f, string qualifiedName, string typ
* are never statically linked together.
*/
private Function getAPossibleDefinition(Function undefinedFunction) {
- not undefinedFunction.isDefined() and
+ not undefinedFunction.hasDefinition() and
exists(string qn, string typeSig |
functionSignature(undefinedFunction, qn, typeSig) and functionSignature(result, qn, typeSig)
) and
- result.isDefined()
+ result.hasDefinition()
}
/**
@@ -631,7 +631,7 @@ private Function getAPossibleDefinition(Function undefinedFunction) {
*/
private Function getTarget1(Call c) {
result = VirtualDispatch::getAViableTarget(c) and
- result.isDefined()
+ result.hasDefinition()
}
/**
diff --git a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScaling.ql b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScaling.ql
index 7cc00c05379..d49eef4202a 100644
--- a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScaling.ql
+++ b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScaling.ql
@@ -21,7 +21,7 @@ where
destBase = baseType(destType) and
destBase.getSize() != sourceBase.getSize() and
not dest.isInMacroExpansion() and
- // If the source type is a char* or void* then don't
+ // If the source type is a `char*` or `void*` then don't
// produce a result, because it is likely to be a false
// positive.
not sourceBase instanceof CharType and
diff --git a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingChar.ql b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingChar.ql
index f96598edd9e..d333a2b37bc 100644
--- a/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingChar.ql
+++ b/cpp/ql/src/Security/CWE/CWE-468/IncorrectPointerScalingChar.ql
@@ -21,7 +21,7 @@ where
destBase = baseType(destType) and
destBase.getSize() != sourceBase.getSize() and
not dest.isInMacroExpansion() and
- // If the source type is a char* or void* then don't
+ // If the source type is a `char*` or `void*` then don't
// produce a result, because it is likely to be a false
// positive.
not sourceBase instanceof CharType and
diff --git a/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql b/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql
index 692325ff373..d356ba7bbc4 100644
--- a/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql
+++ b/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql
@@ -24,9 +24,9 @@ private predicate isCharSzPtrExpr(Expr e) {
from Expr sizeofExpr, Expr e
where
// If we see an addWithSizeof then we expect the type of
- // the pointer expression to be char* or void*. Otherwise it
+ // the pointer expression to be `char*` or `void*`. Otherwise it
// is probably a mistake.
addWithSizeof(e, sizeofExpr, _) and not isCharSzPtrExpr(e)
select sizeofExpr,
- "Suspicious sizeof offset in a pointer arithmetic expression. " + "The type of the pointer is " +
- e.getFullyConverted().getType().toString() + "."
+ "Suspicious sizeof offset in a pointer arithmetic expression. The type of the pointer is $@.",
+ e.getFullyConverted().getType() as t, t.toString()
diff --git a/cpp/ql/src/codeql-suites/cpp-code-scanning.qls b/cpp/ql/src/codeql-suites/cpp-code-scanning.qls
index 27bff98ea5d..f811010a26a 100644
--- a/cpp/ql/src/codeql-suites/cpp-code-scanning.qls
+++ b/cpp/ql/src/codeql-suites/cpp-code-scanning.qls
@@ -2,3 +2,5 @@
- qlpack: codeql-cpp
- apply: code-scanning-selectors.yml
from: codeql-suite-helpers
+- apply: codeql-suites/exclude-slow-queries.yml
+ from: codeql-cpp
diff --git a/cpp/ql/src/codeql-suites/cpp-lgtm-full.qls b/cpp/ql/src/codeql-suites/cpp-lgtm-full.qls
index 2036584e44c..1032261fb94 100644
--- a/cpp/ql/src/codeql-suites/cpp-lgtm-full.qls
+++ b/cpp/ql/src/codeql-suites/cpp-lgtm-full.qls
@@ -2,13 +2,10 @@
- qlpack: codeql-cpp
- apply: lgtm-selectors.yml
from: codeql-suite-helpers
-# These queries are infeasible to compute on large projects:
+- apply: codeql-suites/exclude-slow-queries.yml
+ from: codeql-cpp
+# These are only for IDE use.
- 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
+ tags contain:
+ - ide-contextual-queries/local-definitions
+ - ide-contextual-queries/local-references
diff --git a/cpp/ql/src/codeql-suites/cpp-security-and-quality.qls b/cpp/ql/src/codeql-suites/cpp-security-and-quality.qls
new file mode 100644
index 00000000000..297f46bc752
--- /dev/null
+++ b/cpp/ql/src/codeql-suites/cpp-security-and-quality.qls
@@ -0,0 +1,6 @@
+- description: Security-and-quality queries for C and C++
+- qlpack: codeql-cpp
+- apply: security-and-quality-selectors.yml
+ from: codeql-suite-helpers
+- apply: codeql-suites/exclude-slow-queries.yml
+ from: codeql-cpp
diff --git a/cpp/ql/src/codeql-suites/cpp-security-extended.qls b/cpp/ql/src/codeql-suites/cpp-security-extended.qls
new file mode 100644
index 00000000000..fa69559add0
--- /dev/null
+++ b/cpp/ql/src/codeql-suites/cpp-security-extended.qls
@@ -0,0 +1,6 @@
+- description: Security-extended queries for C and C++
+- qlpack: codeql-cpp
+- apply: security-extended-selectors.yml
+ from: codeql-suite-helpers
+- apply: codeql-suites/exclude-slow-queries.yml
+ from: codeql-cpp
diff --git a/cpp/ql/src/codeql-suites/exclude-slow-queries.yml b/cpp/ql/src/codeql-suites/exclude-slow-queries.yml
new file mode 100644
index 00000000000..a1a4ced9c7d
--- /dev/null
+++ b/cpp/ql/src/codeql-suites/exclude-slow-queries.yml
@@ -0,0 +1,11 @@
+- description: C/C++ queries which are infeasible to compute on large projects
+# 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/cpp.qll b/cpp/ql/src/cpp.qll
index 78fde101a42..a989c9a6c9d 100644
--- a/cpp/ql/src/cpp.qll
+++ b/cpp/ql/src/cpp.qll
@@ -32,6 +32,7 @@ import semmle.code.cpp.Enum
import semmle.code.cpp.Member
import semmle.code.cpp.Field
import semmle.code.cpp.Function
+import semmle.code.cpp.MemberFunction
import semmle.code.cpp.Parameter
import semmle.code.cpp.Variable
import semmle.code.cpp.Initializer
diff --git a/cpp/ql/src/default.qll b/cpp/ql/src/default.qll
index 4996ace8452..6bc0f1b009d 100644
--- a/cpp/ql/src/default.qll
+++ b/cpp/ql/src/default.qll
@@ -1 +1,7 @@
+/**
+ * DEPRECATED: use `import cpp` instead of `import default`.
+ *
+ * Provides classes and predicates for working with C/C++ code.
+ */
+
import cpp
diff --git a/cpp/ql/src/definitions.qll b/cpp/ql/src/definitions.qll
index 4af638e07b9..b42903953b6 100644
--- a/cpp/ql/src/definitions.qll
+++ b/cpp/ql/src/definitions.qll
@@ -132,6 +132,7 @@ private predicate constructorCallTypeMention(ConstructorCall cc, TypeMention tm)
* - `"X"` for macro accesses
* - `"I"` for import / include directives
*/
+cached
Top definitionOf(Top e, string kind) {
(
// call -> function called
@@ -213,3 +214,11 @@ Top definitionOf(Top e, string kind) {
// later on.
strictcount(result.getLocation()) < 10
}
+
+/**
+ * Returns an appropriately encoded version of a filename `name`
+ * passed by the VS Code extension in order to coincide with the
+ * output of `.getFile()` on locatable entities.
+ */
+cached
+File getEncodedFile(string name) { result.getAbsolutePath().replaceAll(":", "_") = name }
diff --git a/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/ArrayLengthAnalysis.qll b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/ArrayLengthAnalysis.qll
new file mode 100644
index 00000000000..8388511932e
--- /dev/null
+++ b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/ArrayLengthAnalysis.qll
@@ -0,0 +1,282 @@
+/**
+ * Provides precise tracking of how big the memory pointed to by pointers is.
+ * For each pointer, we start tracking (starting from the allocation or an array declaration)
+ * 1) how long is the chunk of memory allocated
+ * 2) where the current pointer is in this chunk of memory
+ * As computing this information is obviously not possible for all pointers,
+ * we do not guarantee the existence of length/offset information for all pointers.
+ * However, when it exists it is guaranteed to be accurate.
+ *
+ * The length and offset are tracked in a similar way to the Rangeanalysis.
+ * Each length is a `ValueNumber + delta`, and each Offset is an `Operand + delta`.
+ * We choose to track a `ValueNumber` for length, because the Rangeanalysis offers
+ * integer bounds on instructions and operands in terms of `ValueNumber`s,
+ * and `Operand` for offset because integer bounds on `Operand`s are
+ * tighter than bounds on `Instruction`s.
+ */
+
+import cpp
+import semmle.code.cpp.ir.IR
+private import semmle.code.cpp.ir.ValueNumbering
+private import semmle.code.cpp.ir.internal.CppType
+private import semmle.code.cpp.models.interfaces.Allocation
+private import semmle.code.cpp.rangeanalysis.RangeUtils
+
+private newtype TLength =
+ TZeroLength() or
+ TVNLength(ValueNumber vn) {
+ not vn.getAnInstruction() instanceof ConstantInstruction and
+ exists(Instruction i |
+ vn.getAnInstruction() = i and
+ (
+ i.getResultIRType() instanceof IRSignedIntegerType or
+ i.getResultIRType() instanceof IRUnsignedIntegerType
+ )
+ |
+ i instanceof PhiInstruction
+ or
+ i instanceof InitializeParameterInstruction
+ or
+ i instanceof CallInstruction
+ or
+ i.(LoadInstruction).getSourceAddress() instanceof VariableAddressInstruction
+ or
+ i.(LoadInstruction).getSourceAddress() instanceof FieldAddressInstruction
+ or
+ i.getAUse() instanceof ArgumentOperand
+ )
+ }
+
+/**
+ * Array lengths are represented in a ValueNumber | Zero + delta format.
+ * This class keeps track of the ValueNumber or Zero.
+ * The delta is tracked in the predicate `knownArrayLength`.
+ */
+class Length extends TLength {
+ string toString() { none() } // overridden in subclasses
+}
+
+/**
+ * This length class corresponds to an array having a constant length
+ * that is tracked by the delta value.
+ */
+class ZeroLength extends Length, TZeroLength {
+ override string toString() { result = "ZeroLength" }
+}
+
+/**
+ * This length class corresponds to an array having variable length, i.e. the
+ * length is tracked by a value number. One example is an array having length
+ * `count` for an integer variable `count` in the program.
+ */
+class VNLength extends Length, TVNLength {
+ ValueNumber vn;
+
+ VNLength() { this = TVNLength(vn) }
+
+ /** Gets an instruction with this value number bound. */
+ Instruction getInstruction() { this = TVNLength(valueNumber(result)) }
+
+ ValueNumber getValueNumber() { result = vn }
+
+ override string toString() { result = "VNLength(" + vn.getExampleInstruction().toString() + ")" }
+}
+
+private newtype TOffset =
+ TZeroOffset() or
+ TOpOffset(Operand op) {
+ op.getAnyDef().getResultIRType() instanceof IRSignedIntegerType or
+ op.getAnyDef().getResultIRType() instanceof IRUnsignedIntegerType
+ }
+
+/**
+ * This class describes the offset of a pointer in a chunk of memory.
+ * It is either an `Operand` or zero, an additional integer delta is added later.
+ */
+class Offset extends TOffset {
+ string toString() { none() } // overridden in subclasses
+}
+
+/**
+ * This class represents a fixed offset, only specified by a delta.
+ */
+class ZeroOffset extends Offset, TZeroOffset {
+ override string toString() { result = "ZeroOffset" }
+}
+
+/**
+ * This class represents an offset of an operand.
+ */
+class OpOffset extends Offset, TOpOffset {
+ Operand op;
+
+ OpOffset() { this = TOpOffset(op) }
+
+ Operand getOperand() { result = op }
+
+ override string toString() { result = "OpOffset(" + op.getDef().toString() + ")" }
+}
+
+private int getBaseSizeForPointerType(PointerType type) { result = type.getBaseType().getSize() }
+
+/**
+ * Holds if pointer `prev` that points at offset `prevOffset + prevOffsetDelta`
+ * steps to `array` that points to `offset + offsetDelta` in one step.
+ * This predicate does not contain any recursive steps.
+ */
+bindingset[prevOffset, prevOffsetDelta]
+predicate simpleArrayLengthStep(
+ Instruction array, Offset offset, int offsetDelta, Instruction prev, Offset prevOffset,
+ int prevOffsetDelta
+) {
+ // array assign
+ array.(CopyInstruction).getSourceValue() = prev and
+ offset = prevOffset and
+ offsetDelta = prevOffsetDelta
+ or
+ // pointer add with constant
+ array.(PointerAddInstruction).getLeft() = prev and
+ offset = prevOffset and
+ offsetDelta = prevOffsetDelta + getConstantValue(array.(PointerAddInstruction).getRight())
+ or
+ // pointer add with variable
+ array.(PointerAddInstruction).getLeft() = prev and
+ prevOffset instanceof ZeroOffset and
+ offset.(OpOffset).getOperand() = array.(PointerAddInstruction).getRightOperand() and
+ offsetDelta = prevOffsetDelta and
+ not exists(getConstantValue(array.(PointerAddInstruction).getRight()))
+ or
+ // pointer sub with constant
+ array.(PointerSubInstruction).getLeft() = prev and
+ offset = prevOffset and
+ offsetDelta = prevOffsetDelta - getConstantValue(array.(PointerSubInstruction).getRight())
+ or
+ // array to pointer decay
+ array.(ConvertInstruction).getUnary() = prev and
+ array.getConvertedResultExpression() instanceof ArrayToPointerConversion and
+ offset = prevOffset and
+ offsetDelta = prevOffsetDelta
+ or
+ // cast of pointer to pointer with the same element size
+ exists(PointerType fromTyp, PointerType toTyp |
+ array.(PtrToPtrCastInstruction).getUnary() = prev and
+ prev.getResultLanguageType().hasType(fromTyp, false) and
+ array.getResultLanguageType().hasType(toTyp, false) and
+ offset = prevOffset and
+ offsetDelta = prevOffsetDelta and
+ if fromTyp instanceof VoidPointerType
+ then getBaseSizeForPointerType(toTyp) = 1
+ else (
+ if toTyp instanceof VoidPointerType
+ then getBaseSizeForPointerType(fromTyp) = 1
+ else getBaseSizeForPointerType(toTyp) = getBaseSizeForPointerType(fromTyp)
+ )
+ )
+}
+
+/**
+ * Parses a `sizeExpr` of malloc into a variable part (`lengthExpr`) and an integer offset (`delta`).
+ */
+private predicate deconstructMallocSizeExpr(Expr sizeExpr, Expr lengthExpr, int delta) {
+ sizeExpr instanceof AddExpr and
+ exists(Expr constantExpr |
+ lengthExpr = sizeExpr.(AddExpr).getAnOperand() and
+ constantExpr = sizeExpr.(AddExpr).getAnOperand() and
+ lengthExpr != constantExpr and
+ delta = constantExpr.getValue().toInt()
+ )
+ or
+ sizeExpr instanceof SubExpr and
+ exists(Expr constantExpr |
+ lengthExpr = sizeExpr.(SubExpr).getLeftOperand() and
+ constantExpr = sizeExpr.(SubExpr).getRightOperand() and
+ delta = -constantExpr.getValue().toInt()
+ )
+}
+
+/**
+ * Holds if the instruction `array` is a dynamic memory allocation of `length`+`delta` elements.
+ */
+private predicate allocation(Instruction array, Length length, int delta) {
+ exists(AllocationExpr alloc, Type ptrTyp |
+ array.getUnconvertedResultExpression() = alloc and
+ array.getResultLanguageType().hasType(ptrTyp, false) and
+ // ensure that we have the same type of the allocation and the pointer
+ ptrTyp.stripTopLevelSpecifiers().(PointerType).getBaseType().getUnspecifiedType() =
+ alloc.getAllocatedElementType().getUnspecifiedType() and
+ // ensure that the size multiplier of the allocation is the same as the
+ // size of the type we are allocating
+ alloc.getSizeMult() = getBaseSizeForPointerType(ptrTyp) and
+ (
+ length instanceof ZeroLength and
+ delta = alloc.getSizeExpr().getValue().toInt()
+ or
+ not exists(alloc.getSizeExpr().getValue().toInt()) and
+ (
+ exists(Expr lengthExpr |
+ deconstructMallocSizeExpr(alloc.getSizeExpr(), lengthExpr, delta) and
+ length.(VNLength).getInstruction().getConvertedResultExpression() = lengthExpr
+ )
+ or
+ not exists(int d | deconstructMallocSizeExpr(alloc.getSizeExpr(), _, d)) and
+ length.(VNLength).getInstruction().getConvertedResultExpression() = alloc.getSizeExpr() and
+ delta = 0
+ )
+ )
+ )
+}
+
+/**
+ * Holds if `array` is declared as an array with length `length + lengthDelta`
+ */
+private predicate arrayDeclaration(Instruction array, Length length, int lengthDelta) {
+ (
+ array instanceof VariableAddressInstruction or
+ array instanceof FieldAddressInstruction
+ ) and
+ exists(ArrayType type | array.getResultLanguageType().hasType(type, _) |
+ length instanceof ZeroLength and
+ lengthDelta = type.getArraySize()
+ )
+}
+
+/**
+ * Holds if `array` is declared as an array or allocated
+ * with length `length + lengthDelta`
+ */
+predicate arrayAllocationOrDeclaration(Instruction array, Length length, int lengthDelta) {
+ allocation(array, length, lengthDelta)
+ or
+ // declaration of variable of array type
+ arrayDeclaration(array, length, lengthDelta)
+}
+
+/**
+ * Holds if the instruction `array` represents a pointer to a chunk of memory that holds
+ * `length + lengthDelta` elements, using only local analysis.
+ * `array` points at `offset + offsetDelta` in the chunk of memory.
+ * The pointer is in-bounds if `offset + offsetDelta < length + lengthDelta` and
+ * `offset + offsetDelta >= 0` holds.
+ * The pointer is out-of-bounds if `offset + offsetDelta >= length + lengthDelta`
+ * or `offset + offsetDelta < 0` holds.
+ * All pointers in this predicate are guaranteed to be non-null,
+ * but are not guaranteed to be live.
+ */
+predicate knownArrayLength(
+ Instruction array, Length length, int lengthDelta, Offset offset, int offsetDelta
+) {
+ arrayAllocationOrDeclaration(array, length, lengthDelta) and
+ offset instanceof ZeroOffset and
+ offsetDelta = 0
+ or
+ // simple step (no phi nodes)
+ exists(Instruction prev, Offset prevOffset, int prevOffsetDelta |
+ knownArrayLength(prev, length, lengthDelta, prevOffset, prevOffsetDelta) and
+ simpleArrayLengthStep(array, offset, offsetDelta, prev, prevOffset, prevOffsetDelta)
+ )
+ or
+ // merge control flow after phi node - but only if all the bounds agree
+ forex(Instruction input | array.(PhiInstruction).getAnInput() = input |
+ knownArrayLength(input, length, lengthDelta, offset, offsetDelta)
+ )
+}
diff --git a/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/InBoundsPointerDeref.qll b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/InBoundsPointerDeref.qll
new file mode 100644
index 00000000000..94227a5a8ac
--- /dev/null
+++ b/cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/InBoundsPointerDeref.qll
@@ -0,0 +1,105 @@
+/**
+ * This library proves that a subset of pointer dereferences in a program are
+ * safe, i.e. in-bounds.
+ * It does so by first defining what a pointer dereference is (on the IR
+ * `Instruction` level), and then using the array length analysis and the range
+ * analysis together to prove that some of these pointer dereferences are safe.
+ *
+ * The analysis is soundy, i.e. it is sound if no undefined behaviour is present
+ * in the program.
+ * Furthermore, it crucially depends on the soundiness of the range analysis and
+ * the array length analysis.
+ */
+
+import cpp
+private import experimental.semmle.code.cpp.rangeanalysis.ArrayLengthAnalysis
+private import semmle.code.cpp.rangeanalysis.RangeAnalysis
+
+/**
+ * Gets the instruction that computes the address of memory that `i` accesses.
+ * Only holds if `i` dereferences a pointer, not when the computation of the
+ * memory address is constant, or if the address of a local variable is loaded/stored to.
+ */
+private Instruction getMemoryAddressInstruction(Instruction i) {
+ (
+ result = i.(FieldAddressInstruction).getObjectAddress() or
+ result = i.(LoadInstruction).getSourceAddress() or
+ result = i.(StoreInstruction).getDestinationAddress()
+ ) and
+ not result instanceof FieldAddressInstruction and
+ not result instanceof VariableAddressInstruction and
+ not result instanceof ConstantValueInstruction
+}
+
+/**
+ * All instructions that dereference a pointer.
+ */
+class PointerDereferenceInstruction extends Instruction {
+ PointerDereferenceInstruction() { exists(getMemoryAddressInstruction(this)) }
+
+ Instruction getAddress() { result = getMemoryAddressInstruction(this) }
+}
+
+/**
+ * Holds if `ptrDeref` can be proven to always access allocated memory.
+ */
+predicate inBounds(PointerDereferenceInstruction ptrDeref) {
+ exists(Length length, int lengthDelta, Offset offset, int offsetDelta |
+ knownArrayLength(ptrDeref.getAddress(), length, lengthDelta, offset, offsetDelta) and
+ // lower bound - note that we treat a pointer that accesses an array of
+ // length 0 as on upper-bound violation, but not as a lower-bound violation
+ (
+ offset instanceof ZeroOffset and
+ offsetDelta >= 0
+ or
+ offset instanceof OpOffset and
+ exists(int lowerBoundDelta |
+ boundedOperand(offset.(OpOffset).getOperand(), any(ZeroBound b), lowerBoundDelta,
+ /*upper*/ false, _) and
+ lowerBoundDelta + offsetDelta >= 0
+ )
+ ) and
+ // upper bound
+ (
+ // both offset and length are only integers
+ length instanceof ZeroLength and
+ offset instanceof ZeroOffset and
+ offsetDelta < lengthDelta
+ or
+ exists(int lengthBound |
+ // array length is variable+integer, and there's a fixed (integer-only)
+ // lower bound on the variable, so we can guarantee this access is always in-bounds
+ length instanceof VNLength and
+ offset instanceof ZeroOffset and
+ boundedInstruction(length.(VNLength).getInstruction(), any(ZeroBound b), lengthBound,
+ /* upper*/ false, _) and
+ offsetDelta < lengthBound + lengthDelta
+ )
+ or
+ exists(int offsetBoundDelta |
+ length instanceof ZeroLength and
+ offset instanceof OpOffset and
+ boundedOperand(offset.(OpOffset).getOperand(), any(ZeroBound b), offsetBoundDelta,
+ /* upper */ true, _) and
+ // offset <= offsetBoundDelta, so offset + offsetDelta <= offsetDelta + offsetBoundDelta
+ // Thus, in-bounds if offsetDelta + offsetBoundDelta < lengthDelta
+ // as we have length instanceof ZeroLength
+ offsetDelta + offsetBoundDelta < lengthDelta
+ )
+ or
+ exists(ValueNumberBound b, int offsetBoundDelta |
+ length instanceof VNLength and
+ offset instanceof OpOffset and
+ b.getValueNumber() = length.(VNLength).getValueNumber() and
+ // It holds that offset <= length + offsetBoundDelta
+ boundedOperand(offset.(OpOffset).getOperand(), b, offsetBoundDelta, /*upper*/ true, _) and
+ // it also holds that
+ offsetDelta < lengthDelta - offsetBoundDelta
+ // taking both inequalities together we get
+ // offset <= length + offsetBoundDelta
+ // => offset + offsetDelta <= length + offsetBoundDelta + offsetDelta < length + offsetBoundDelta + lengthDelta - offsetBoundDelta
+ // as required
+ )
+ )
+ )
+}
diff --git a/cpp/ql/src/external/CodeDuplication.qll b/cpp/ql/src/external/CodeDuplication.qll
index 2f4fd0d05da..4548e0be85e 100644
--- a/cpp/ql/src/external/CodeDuplication.qll
+++ b/cpp/ql/src/external/CodeDuplication.qll
@@ -1,3 +1,5 @@
+/** Provides classes for detecting duplicate or similar code. */
+
import cpp
private string relativePath(File file) { result = file.getRelativePath().replaceAll("\\", "/") }
@@ -8,9 +10,12 @@ private predicate tokenLocation(string path, int sl, int sc, int ec, int el, Cop
tokens(copy, index, sl, sc, ec, el)
}
+/** A token block used for detection of duplicate and similar code. */
class Copy extends @duplication_or_similarity {
+ /** Gets the index of the last token in this block. */
private int lastToken() { result = max(int i | tokens(this, i, _, _, _, _) | i) }
+ /** Gets the index of the token in this block starting at the location `loc`, if any. */
int tokenStartingAt(Location loc) {
exists(string filepath, int startline, int startcol |
loc.hasLocationInfo(filepath, startline, startcol, _, _) and
@@ -18,6 +23,7 @@ class Copy extends @duplication_or_similarity {
)
}
+ /** Gets the index of the token in this block ending at the location `loc`, if any. */
int tokenEndingAt(Location loc) {
exists(string filepath, int endline, int endcol |
loc.hasLocationInfo(filepath, _, _, endline, endcol) and
@@ -25,24 +31,38 @@ class Copy extends @duplication_or_similarity {
)
}
+ /** Gets the line on which the first token in this block starts. */
int sourceStartLine() { tokens(this, 0, result, _, _, _) }
+ /** Gets the column on which the first token in this block starts. */
int sourceStartColumn() { tokens(this, 0, _, result, _, _) }
+ /** Gets the line on which the last token in this block ends. */
int sourceEndLine() { tokens(this, lastToken(), _, _, result, _) }
+ /** Gets the column on which the last token in this block ends. */
int sourceEndColumn() { tokens(this, lastToken(), _, _, _, result) }
+ /** Gets the number of lines containing at least (part of) one token in this block. */
int sourceLines() { result = this.sourceEndLine() + 1 - this.sourceStartLine() }
+ /** Gets an opaque identifier for the equivalence class of this block. */
int getEquivalenceClass() { duplicateCode(this, _, result) or similarCode(this, _, result) }
+ /** Gets the source file in which this block appears. */
File sourceFile() {
exists(string name | duplicateCode(this, name, _) or similarCode(this, name, _) |
name.replaceAll("\\", "/") = relativePath(result)
)
}
+ /**
+ * Holds if this element is at the specified location.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `filepath`.
+ * For more information, see
+ * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ */
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
@@ -53,25 +73,30 @@ class Copy extends @duplication_or_similarity {
endcolumn = sourceEndColumn()
}
+ /** Gets a textual representation of this element. */
string toString() { none() }
}
+/** A block of duplicated code. */
class DuplicateBlock extends Copy, @duplication {
override string toString() { result = "Duplicate code: " + sourceLines() + " duplicated lines." }
}
+/** A block of similar code. */
class SimilarBlock extends Copy, @similarity {
override string toString() {
result = "Similar code: " + sourceLines() + " almost duplicated lines."
}
}
+/** Gets a function with a body and a location. */
FunctionDeclarationEntry sourceMethod() {
result.isDefinition() and
exists(result.getLocation()) and
numlines(unresolveElement(result.getFunction()), _, _, _)
}
+/** Gets the number of member functions in `c` with a body and a location. */
int numberOfSourceMethods(Class c) {
result =
count(FunctionDeclarationEntry m |
@@ -108,6 +133,10 @@ private predicate duplicateStatement(
)
}
+/**
+ * Holds if `m1` is a function with `total` lines, and `m2` is a function
+ * that has `duplicate` lines in common with `m1`.
+ */
predicate duplicateStatements(
FunctionDeclarationEntry m1, FunctionDeclarationEntry m2, int duplicate, int total
) {
@@ -115,13 +144,16 @@ predicate duplicateStatements(
total = strictcount(statementInMethod(m1))
}
-/**
- * Find pairs of methods are identical
- */
+/** Holds if `m` and other are identical functions. */
predicate duplicateMethod(FunctionDeclarationEntry m, FunctionDeclarationEntry other) {
exists(int total | duplicateStatements(m, other, total, total))
}
+/**
+ * INTERNAL: do not use.
+ *
+ * Holds if `line` in `f` is similar to a line somewhere else.
+ */
predicate similarLines(File f, int line) {
exists(SimilarBlock b | b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()])
}
@@ -152,6 +184,7 @@ private predicate similarLinesCoveredFiles(File f, File otherFile) {
)
}
+/** Holds if `coveredLines` lines of `f` are similar to lines in `otherFile`. */
predicate similarLinesCovered(File f, int coveredLines, File otherFile) {
exists(int numLines | numLines = f.getMetrics().getNumberOfLines() |
similarLinesCoveredFiles(f, otherFile) and
@@ -166,6 +199,11 @@ predicate similarLinesCovered(File f, int coveredLines, File otherFile) {
)
}
+/**
+ * INTERNAL: do not use.
+ *
+ * Holds if `line` in `f` is duplicated by a line somewhere else.
+ */
predicate duplicateLines(File f, int line) {
exists(DuplicateBlock b |
b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()]
@@ -182,6 +220,7 @@ private predicate duplicateLinesPerEquivalenceClass(int equivClass, int lines, F
)
}
+/** Holds if `coveredLines` lines of `f` are duplicates of lines in `otherFile`. */
predicate duplicateLinesCovered(File f, int coveredLines, File otherFile) {
exists(int numLines | numLines = f.getMetrics().getNumberOfLines() |
exists(int coveredApprox |
@@ -206,6 +245,7 @@ predicate duplicateLinesCovered(File f, int coveredLines, File otherFile) {
)
}
+/** Holds if most of `f` (`percent`%) is similar to `other`. */
predicate similarFiles(File f, File other, int percent) {
exists(int covered, int total |
similarLinesCovered(f, covered, other) and
@@ -216,6 +256,7 @@ predicate similarFiles(File f, File other, int percent) {
not duplicateFiles(f, other, _)
}
+/** Holds if most of `f` (`percent`%) is duplicated by `other`. */
predicate duplicateFiles(File f, File other, int percent) {
exists(int covered, int total |
duplicateLinesCovered(f, covered, other) and
@@ -225,6 +266,10 @@ predicate duplicateFiles(File f, File other, int percent) {
)
}
+/**
+ * Holds if most member functions of `c` (`numDup` out of `total`) are
+ * duplicates of member functions in `other`.
+ */
predicate mostlyDuplicateClassBase(Class c, Class other, int numDup, int total) {
numDup =
strictcount(FunctionDeclarationEntry m1 |
@@ -240,6 +285,11 @@ predicate mostlyDuplicateClassBase(Class c, Class other, int numDup, int total)
(numDup * 100) / total > 80
}
+/**
+ * Holds if most member functions of `c` are duplicates of member functions in
+ * `other`. Provides the human-readable `message` to describe the amount of
+ * duplication.
+ */
predicate mostlyDuplicateClass(Class c, Class other, string message) {
exists(int numDup, int total |
mostlyDuplicateClassBase(c, other, numDup, total) and
@@ -264,12 +314,21 @@ predicate mostlyDuplicateClass(Class c, Class other, string message) {
)
}
+/** Holds if `f` and `other` are similar or duplicates. */
predicate fileLevelDuplication(File f, File other) {
similarFiles(f, other, _) or duplicateFiles(f, other, _)
}
+/**
+ * Holds if most member functions of `c` are duplicates of member functions in
+ * `other`.
+ */
predicate classLevelDuplication(Class c, Class other) { mostlyDuplicateClass(c, other, _) }
+/**
+ * Holds if `line` in `f` should be allowed to be duplicated. This is the case
+ * for `#include` directives.
+ */
predicate whitelistedLineForDuplication(File f, int line) {
exists(Include i | i.getFile() = f and i.getLocation().getStartLine() = line)
}
diff --git a/cpp/ql/src/external/DefectFilter.qll b/cpp/ql/src/external/DefectFilter.qll
index da63893b8bb..675f3b25faa 100644
--- a/cpp/ql/src/external/DefectFilter.qll
+++ b/cpp/ql/src/external/DefectFilter.qll
@@ -1,31 +1,52 @@
+/** Provides a class for working with defect query results stored in dashboard databases. */
+
import cpp
+/**
+ * Holds if `id` in the opaque identifier of a result reported by query `queryPath`,
+ * such that `message` is the associated message and the location of the result spans
+ * column `startcolumn` of line `startline` to column `endcolumn` of line `endline`
+ * in file `filepath`.
+ *
+ * For more information, see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ */
external predicate defectResults(
int id, string queryPath, string file, int startline, int startcol, int endline, int endcol,
string message
);
+/**
+ * A defect query result stored in a dashboard database.
+ */
class DefectResult extends int {
DefectResult() { defectResults(this, _, _, _, _, _, _, _) }
+ /** Gets the path of the query that reported the result. */
string getQueryPath() { defectResults(this, result, _, _, _, _, _, _) }
+ /** Gets the file in which this query result was reported. */
File getFile() {
exists(string path |
defectResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path
)
}
+ /** Gets the line on which the location of this query result starts. */
int getStartLine() { defectResults(this, _, _, result, _, _, _, _) }
+ /** Gets the column on which the location of this query result starts. */
int getStartColumn() { defectResults(this, _, _, _, result, _, _, _) }
+ /** Gets the line on which the location of this query result ends. */
int getEndLine() { defectResults(this, _, _, _, _, result, _, _) }
+ /** Gets the column on which the location of this query result ends. */
int getEndColumn() { defectResults(this, _, _, _, _, _, result, _) }
+ /** Gets the message associated with this query result. */
string getMessage() { defectResults(this, _, _, _, _, _, _, result) }
+ /** Gets the URL corresponding to the location of this query result. */
string getURL() {
result =
"file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" +
diff --git a/cpp/ql/src/external/ExternalArtifact.qll b/cpp/ql/src/external/ExternalArtifact.qll
index 94fa0d7e31a..abbc96a7b47 100644
--- a/cpp/ql/src/external/ExternalArtifact.qll
+++ b/cpp/ql/src/external/ExternalArtifact.qll
@@ -1,26 +1,45 @@
+/**
+ * Provides classes for working with external data.
+ */
+
import cpp
+/**
+ * An external data item.
+ */
class ExternalData extends @externalDataElement {
+ /** Gets the path of the file this data was loaded from. */
string getDataPath() { externalData(this, result, _, _) }
+ /**
+ * Gets the path of the file this data was loaded from, with its
+ * extension replaced by `.ql`.
+ */
string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") }
+ /** Gets the number of fields in this data item. */
int getNumFields() { result = 1 + max(int i | externalData(this, _, i, _) | i) }
- string getField(int index) { externalData(this, _, index, result) }
+ /** Gets the value of the `i`th field of this data item. */
+ string getField(int i) { externalData(this, _, i, result) }
- int getFieldAsInt(int index) { result = getField(index).toInt() }
+ /** Gets the integer value of the `i`th field of this data item. */
+ int getFieldAsInt(int i) { result = getField(i).toInt() }
- float getFieldAsFloat(int index) { result = getField(index).toFloat() }
+ /** Gets the floating-point value of the `i`th field of this data item. */
+ float getFieldAsFloat(int i) { result = getField(i).toFloat() }
- date getFieldAsDate(int index) { result = getField(index).toDate() }
+ /** Gets the value of the `i`th field of this data item, interpreted as a date. */
+ date getFieldAsDate(int i) { result = getField(i).toDate() }
+ /** Gets a textual representation of this data item. */
string toString() { result = getQueryPath() + ": " + buildTupleString(0) }
- private string buildTupleString(int start) {
- start = getNumFields() - 1 and result = getField(start)
+ /** Gets a textual representation of this data item, starting with the `n`th field. */
+ private string buildTupleString(int n) {
+ n = getNumFields() - 1 and result = getField(n)
or
- start < getNumFields() - 1 and result = getField(start) + "," + buildTupleString(start + 1)
+ n < getNumFields() - 1 and result = getField(n) + "," + buildTupleString(n + 1)
}
}
@@ -33,7 +52,9 @@ class DefectExternalData extends ExternalData {
this.getNumFields() = 2
}
+ /** Gets the URL associated with this data item. */
string getURL() { result = getField(0) }
+ /** Gets the message associated with this data item. */
string getMessage() { result = getField(1) }
}
diff --git a/cpp/ql/src/external/MetricFilter.qll b/cpp/ql/src/external/MetricFilter.qll
index b159b4cad5c..dd9cece78ce 100644
--- a/cpp/ql/src/external/MetricFilter.qll
+++ b/cpp/ql/src/external/MetricFilter.qll
@@ -1,31 +1,58 @@
+/** Provides a class for working with metric query results stored in dashboard databases. */
+
import cpp
+/**
+ * Holds if `id` in the opaque identifier of a result reported by query `queryPath`,
+ * such that `value` is the reported metric value and the location of the result spans
+ * column `startcolumn` of line `startline` to column `endcolumn` of line `endline`
+ * in file `filepath`.
+ *
+ * For more information, see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ */
external predicate metricResults(
int id, string queryPath, string file, int startline, int startcol, int endline, int endcol,
float value
);
+/**
+ * A metric query result stored in a dashboard database.
+ */
class MetricResult extends int {
MetricResult() { metricResults(this, _, _, _, _, _, _, _) }
+ /** Gets the path of the query that reported the result. */
string getQueryPath() { metricResults(this, result, _, _, _, _, _, _) }
+ /** Gets the file in which this query result was reported. */
File getFile() {
exists(string path |
metricResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path
)
}
+ /** Gets the line on which the location of this query result starts. */
int getStartLine() { metricResults(this, _, _, result, _, _, _, _) }
+ /** Gets the column on which the location of this query result starts. */
int getStartColumn() { metricResults(this, _, _, _, result, _, _, _) }
+ /** Gets the line on which the location of this query result ends. */
int getEndLine() { metricResults(this, _, _, _, _, result, _, _) }
+ /** Gets the column on which the location of this query result ends. */
int getEndColumn() { metricResults(this, _, _, _, _, _, result, _) }
+ /**
+ * Holds if there is a `Location` entity whose location is the same as
+ * the location of this query result.
+ */
predicate hasMatchingLocation() { exists(this.getMatchingLocation()) }
+ /**
+ * Gets the `Location` entity whose location is the same as the location
+ * of this query result.
+ */
Location getMatchingLocation() {
result.getFile() = this.getFile() and
result.getStartLine() = this.getStartLine() and
@@ -34,8 +61,10 @@ class MetricResult extends int {
result.getEndColumn() = this.getEndColumn()
}
+ /** Gets the value associated with this query result. */
float getValue() { metricResults(this, _, _, _, _, _, _, result) }
+ /** Gets the URL corresponding to the location of this query result. */
string getURL() {
result =
"file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" +
diff --git a/cpp/ql/src/external/VCS.qll b/cpp/ql/src/external/VCS.qll
deleted file mode 100644
index ce36ace56d9..00000000000
--- a/cpp/ql/src/external/VCS.qll
+++ /dev/null
@@ -1,92 +0,0 @@
-import cpp
-
-class Commit extends @svnentry {
- Commit() {
- svnaffectedfiles(this, _, _) and
- exists(date svnDate, date snapshotDate |
- svnentries(this, _, _, svnDate, _) and
- snapshotDate(snapshotDate) and
- svnDate <= snapshotDate
- )
- }
-
- string toString() { result = this.getRevisionName() }
-
- string getRevisionName() { svnentries(this, result, _, _, _) }
-
- string getAuthor() { svnentries(this, _, result, _, _) }
-
- date getDate() { svnentries(this, _, _, result, _) }
-
- int getChangeSize() { svnentries(this, _, _, _, result) }
-
- string getMessage() { svnentrymsg(this, result) }
-
- string getAnAffectedFilePath(string action) {
- exists(File rawFile | svnaffectedfiles(this, unresolveElement(rawFile), action) |
- result = rawFile.getAbsolutePath()
- )
- }
-
- string getAnAffectedFilePath() { result = getAnAffectedFilePath(_) }
-
- File getAnAffectedFile(string action) {
- // Workaround for incorrect keys in SVN data
- exists(File svnFile | svnFile.getAbsolutePath() = result.getAbsolutePath() |
- svnaffectedfiles(this, unresolveElement(svnFile), action)
- ) and
- exists(result.getMetrics().getNumberOfLinesOfCode())
- }
-
- File getAnAffectedFile() { exists(string action | result = this.getAnAffectedFile(action)) }
-
- private predicate churnForFile(File f, int added, int deleted) {
- // Workaround for incorrect keys in SVN data
- exists(File svnFile | svnFile.getAbsolutePath() = f.getAbsolutePath() |
- svnchurn(this, unresolveElement(svnFile), added, deleted)
- ) and
- exists(f.getMetrics().getNumberOfLinesOfCode())
- }
-
- int getRecentChurnForFile(File f) {
- exists(int added, int deleted | churnForFile(f, added, deleted) and result = added + deleted)
- }
-
- int getRecentAdditionsForFile(File f) { churnForFile(f, result, _) }
-
- int getRecentDeletionsForFile(File f) { churnForFile(f, _, result) }
-
- predicate isRecent() { recentCommit(this) }
-
- int daysToNow() {
- exists(date now | snapshotDate(now) | result = getDate().daysTo(now) and result >= 0)
- }
-}
-
-class Author extends string {
- Author() { exists(Commit e | this = e.getAuthor()) }
-
- Commit getACommit() { result.getAuthor() = this }
-
- File getAnEditedFile() { result = this.getACommit().getAnAffectedFile() }
-}
-
-predicate recentCommit(Commit e) {
- exists(date snapshotDate, date commitDate, int days |
- snapshotDate(snapshotDate) and
- e.getDate() = commitDate and
- days = commitDate.daysTo(snapshotDate) and
- days >= 0 and
- days <= 60
- )
-}
-
-date firstChange(File f) {
- result = min(Commit e, date toMin | f = e.getAnAffectedFile() and toMin = e.getDate() | toMin)
-}
-
-predicate firstCommit(Commit e) {
- not exists(File f | f = e.getAnAffectedFile() | firstChange(f) < e.getDate())
-}
-
-predicate artificialChange(Commit e) { firstCommit(e) or e.getChangeSize() >= 50000 }
diff --git a/cpp/ql/src/external/tests/DefectFromSVN.ql b/cpp/ql/src/external/tests/DefectFromSVN.ql
deleted file mode 100644
index 64dd69148eb..00000000000
--- a/cpp/ql/src/external/tests/DefectFromSVN.ql
+++ /dev/null
@@ -1,20 +0,0 @@
-/**
- * @name Defect from SVN
- * @description A test case for creating a defect from SVN data.
- * @kind problem
- * @problem.severity warning
- * @tags external-data
- * @deprecated
- */
-
-import cpp
-import external.ExternalArtifact
-import external.VCS
-
-predicate numCommits(File f, int i) { i = count(Commit e | e.getAnAffectedFile() = f) }
-
-predicate maxCommits(int i) { i = max(File f, int j | numCommits(f, j) | j) }
-
-from File f, int i
-where numCommits(f, i) and maxCommits(i)
-select f, "This file has " + i + " commits."
diff --git a/cpp/ql/src/external/tests/MetricFromSVN.ql b/cpp/ql/src/external/tests/MetricFromSVN.ql
deleted file mode 100644
index e81eec18ea5..00000000000
--- a/cpp/ql/src/external/tests/MetricFromSVN.ql
+++ /dev/null
@@ -1,17 +0,0 @@
-/**
- * @name Metric from SVN
- * @description Find number of commits for a file
- * @treemap.warnOn lowValues
- * @metricType file
- * @tags external-data
- * @deprecated
- */
-
-import cpp
-import external.VCS
-
-predicate numCommits(File f, int i) { i = count(Commit e | e.getAnAffectedFile() = f) }
-
-from File f, int i
-where numCommits(f, i)
-select f, i
diff --git a/cpp/ql/src/filters/RecentDefects.ql b/cpp/ql/src/filters/RecentDefects.ql
deleted file mode 100644
index 4b742849fda..00000000000
--- a/cpp/ql/src/filters/RecentDefects.ql
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
- * @name Filter: exclude results from files that have not recently been
- * edited
- * @description Use this filter to return results only if they are
- * located in files that have been modified in the 60 days
- * before the date of the snapshot.
- * @kind problem
- * @id cpp/recent-defects-filter
- * @tags filter
- * external-data
- * @deprecated
- */
-
-import cpp
-import external.DefectFilter
-import external.VCS
-
-pragma[noopt]
-private predicate recent(File file) {
- exists(Commit e | file = e.getAnAffectedFile() | e.isRecent() and not artificialChange(e))
-}
-
-from DefectResult res
-where recent(res.getFile())
-select res, res.getMessage()
diff --git a/cpp/ql/src/filters/RecentDefectsForMetric.ql b/cpp/ql/src/filters/RecentDefectsForMetric.ql
deleted file mode 100644
index ee057fe71ca..00000000000
--- a/cpp/ql/src/filters/RecentDefectsForMetric.ql
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
- * @name Metric filter: exclude results from files that have not
- * recently been edited
- * @description Use this filter to return results only if they are
- * located in files that have been modified in the 60 days
- * before the snapshot.
- * @kind treemap
- * @id cpp/recent-defects-for-metric-filter
- * @tags filter
- * external-data
- * @deprecated
- */
-
-import cpp
-import external.MetricFilter
-import external.VCS
-
-pragma[noopt]
-private predicate recent(File file) {
- exists(Commit e | file = e.getAnAffectedFile() | e.isRecent() and not artificialChange(e))
-}
-
-from MetricResult res
-where recent(res.getFile())
-select res, res.getValue()
diff --git a/cpp/ql/src/localDefinitions.ql b/cpp/ql/src/localDefinitions.ql
new file mode 100644
index 00000000000..1e525d8e2dd
--- /dev/null
+++ b/cpp/ql/src/localDefinitions.ql
@@ -0,0 +1,16 @@
+/**
+ * @name Jump-to-definition links
+ * @description Generates use-definition pairs that provide the data
+ * for jump-to-definition in the code viewer.
+ * @kind definitions
+ * @id cpp/ide-jump-to-definition
+ * @tags ide-contextual-queries/local-definitions
+ */
+
+import definitions
+
+external string selectedSourceFile();
+
+from Top e, Top def, string kind
+where def = definitionOf(e, kind) and e.getFile() = getEncodedFile(selectedSourceFile())
+select e, def, kind
diff --git a/cpp/ql/src/localReferences.ql b/cpp/ql/src/localReferences.ql
new file mode 100644
index 00000000000..13ca4dd2561
--- /dev/null
+++ b/cpp/ql/src/localReferences.ql
@@ -0,0 +1,16 @@
+/**
+ * @name Find-references links
+ * @description Generates use-definition pairs that provide the data
+ * for find-references in the code viewer.
+ * @kind definitions
+ * @id cpp/ide-find-references
+ * @tags ide-contextual-queries/local-references
+ */
+
+import definitions
+
+external string selectedSourceFile();
+
+from Top e, Top def, string kind
+where def = definitionOf(e, kind) and def.getFile() = getEncodedFile(selectedSourceFile())
+select e, def, kind
diff --git a/cpp/ql/src/objc.qll b/cpp/ql/src/objc.qll
index 4996ace8452..58a9ec50ff7 100644
--- a/cpp/ql/src/objc.qll
+++ b/cpp/ql/src/objc.qll
@@ -1 +1,7 @@
+/**
+ * DEPRECATED: Objective C is no longer supported.
+ *
+ * Import `cpp` instead of `objc`.
+ */
+
import cpp
diff --git a/cpp/ql/src/semmle/code/cpp/ASTConsistency.ql b/cpp/ql/src/semmle/code/cpp/ASTConsistency.ql
new file mode 100644
index 00000000000..6d3c9f48457
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ASTConsistency.ql
@@ -0,0 +1,9 @@
+/**
+ * @name AST Consistency Check
+ * @description Performs consistency checks on the Abstract Syntax Tree. This query should have no results.
+ * @kind table
+ * @id cpp/ast-consistency-check
+ */
+
+import cpp
+import CastConsistency
diff --git a/cpp/ql/src/semmle/code/cpp/ASTSanity.ql b/cpp/ql/src/semmle/code/cpp/ASTSanity.ql
deleted file mode 100644
index aeb05379f8c..00000000000
--- a/cpp/ql/src/semmle/code/cpp/ASTSanity.ql
+++ /dev/null
@@ -1,9 +0,0 @@
-/**
- * @name AST Sanity Check
- * @description Performs sanity checks on the Abstract Syntax Tree. This query should have no results.
- * @kind table
- * @id cpp/ast-sanity-check
- */
-
-import cpp
-import CastSanity
diff --git a/cpp/ql/src/semmle/code/cpp/AutogeneratedFile.qll b/cpp/ql/src/semmle/code/cpp/AutogeneratedFile.qll
index 277d0e7b517..59447d983e2 100644
--- a/cpp/ql/src/semmle/code/cpp/AutogeneratedFile.qll
+++ b/cpp/ql/src/semmle/code/cpp/AutogeneratedFile.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides a class and predicate for recognizing files that are likely to have been generated
+ * automatically.
+ */
+
import semmle.code.cpp.Comments
import semmle.code.cpp.File
import semmle.code.cpp.Preprocessor
diff --git a/cpp/ql/src/semmle/code/cpp/Class.qll b/cpp/ql/src/semmle/code/cpp/Class.qll
index 67a2d11ff24..11ebef3e5ff 100644
--- a/cpp/ql/src/semmle/code/cpp/Class.qll
+++ b/cpp/ql/src/semmle/code/cpp/Class.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes representing C++ classes, including structs, unions, and template classes.
+ */
+
import semmle.code.cpp.Type
import semmle.code.cpp.UserType
import semmle.code.cpp.metrics.MetricClass
@@ -458,6 +462,15 @@ class Class extends UserType {
exists(ClassDerivation d | d.getDerivedClass() = this and d = result)
}
+ /**
+ * Gets class derivation number `index` of this class/struct, for example the
+ * `public B` is derivation 1 in the following code:
+ * ```
+ * class D : public A, public B, public C {
+ * ...
+ * };
+ * ```
+ */
ClassDerivation getDerivation(int index) {
exists(ClassDerivation d | d.getDerivedClass() = this and d.getIndex() = index and d = result)
}
@@ -900,6 +913,22 @@ class AbstractClass extends Class {
class TemplateClass extends Class {
TemplateClass() { usertypes(underlyingElement(this), _, 6) }
+ /**
+ * Gets a class instantiated from this template.
+ *
+ * For example for `MyTemplateClass` in the following code, the results are
+ * `MyTemplateClass` and `MyTemplateClass`:
+ * ```
+ * template
+ * class MyTemplateClass {
+ * ...
+ * };
+ *
+ * MyTemplateClass instance;
+ *
+ * MyTemplateClass instance;
+ * ```
+ */
Class getAnInstantiation() {
result.isConstructedFrom(this) and
exists(result.getATemplateArgument())
diff --git a/cpp/ql/src/semmle/code/cpp/Comments.qll b/cpp/ql/src/semmle/code/cpp/Comments.qll
index e517f8e7b0e..65e2af5fd22 100644
--- a/cpp/ql/src/semmle/code/cpp/Comments.qll
+++ b/cpp/ql/src/semmle/code/cpp/Comments.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes representing C and C++ comments.
+ */
+
import semmle.code.cpp.Location
import semmle.code.cpp.Element
@@ -13,8 +17,20 @@ class Comment extends Locatable, @comment {
override Location getLocation() { comments(underlyingElement(this), _, result) }
+ /**
+ * Gets the text of this comment, including the opening `//` or `/*`, and the closing `*``/` if
+ * present.
+ */
string getContents() { comments(underlyingElement(this), result, _) }
+ /**
+ * Gets the AST element this comment is associated with. For example, the comment in the
+ * following code is associated with the declaration of `j`.
+ * ```
+ * int i;
+ * int j; // Comment on j
+ * ```
+ */
Element getCommentedElement() {
commentbinding(underlyingElement(this), unresolveElement(result))
}
diff --git a/cpp/ql/src/semmle/code/cpp/Compilation.qll b/cpp/ql/src/semmle/code/cpp/Compilation.qll
index 02d962844c8..812c417dbdd 100644
--- a/cpp/ql/src/semmle/code/cpp/Compilation.qll
+++ b/cpp/ql/src/semmle/code/cpp/Compilation.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides a class representing individual compiler invocations that occurred during the build.
+ */
+
import semmle.code.cpp.File
/*
@@ -21,9 +25,9 @@ private predicate idOf(@compilation x, int y) = equivalenceRelation(id/2)(x, y)
* Three things happen to each file during a compilation:
*
* 1. The file is compiled by a real compiler, such as gcc or VC.
- * 2. The file is parsed by Semmle's C++ front-end.
+ * 2. The file is parsed by the CodeQL C++ front-end.
* 3. The parsed representation is converted to database tables by
- * Semmle's extractor.
+ * the CodeQL extractor.
*
* This class provides CPU and elapsed time information for steps 2 and 3,
* but not for step 1.
@@ -40,6 +44,7 @@ class Compilation extends @compilation {
/** Gets a file compiled during this invocation. */
File getAFileCompiled() { result = getFileCompiled(_) }
+ /** Gets the `i`th file compiled during this invocation */
File getFileCompiled(int i) { compilation_compiling_files(this, i, unresolveElement(result)) }
/**
diff --git a/cpp/ql/src/semmle/code/cpp/Declaration.qll b/cpp/ql/src/semmle/code/cpp/Declaration.qll
index 1d5603fe4f4..6245005fd41 100644
--- a/cpp/ql/src/semmle/code/cpp/Declaration.qll
+++ b/cpp/ql/src/semmle/code/cpp/Declaration.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes for working with C and C++ declarations.
+ */
+
import semmle.code.cpp.Element
import semmle.code.cpp.Specifier
import semmle.code.cpp.Namespace
@@ -25,7 +29,7 @@ private import semmle.code.cpp.internal.QualifiedName as Q
* `DeclarationEntry`, because they always have a unique source location.
* `EnumConstant` and `FriendDecl` are both examples of this.
*/
-abstract class Declaration extends Locatable, @declaration {
+class Declaration extends Locatable, @declaration {
/**
* Gets the innermost namespace which contains this declaration.
*
@@ -98,7 +102,12 @@ abstract class Declaration extends Locatable, @declaration {
this.hasQualifiedName(namespaceQualifier, "", baseName)
}
- override string toString() { result = this.getName() }
+ /**
+ * Gets a description of this `Declaration` for display purposes.
+ */
+ string getDescription() { result = this.getName() }
+
+ final override string toString() { result = this.getDescription() }
/**
* Gets the name of this declaration.
@@ -161,6 +170,7 @@ abstract class Declaration extends Locatable, @declaration {
/** Holds if the declaration has a definition. */
predicate hasDefinition() { exists(this.getDefinition()) }
+ /** DEPRECATED: Use `hasDefinition` instead. */
predicate isDefined() { hasDefinition() }
/** Gets the preferred location of this declaration, if any. */
@@ -303,7 +313,7 @@ abstract class DeclarationEntry extends Locatable {
* available), or the name declared by this entry otherwise.
*/
string getCanonicalName() {
- if getDeclaration().isDefined()
+ if getDeclaration().hasDefinition()
then result = getDeclaration().getDefinition().getName()
else result = getName()
}
diff --git a/cpp/ql/src/semmle/code/cpp/Diagnostics.qll b/cpp/ql/src/semmle/code/cpp/Diagnostics.qll
index f03f03783c1..79074fa8657 100644
--- a/cpp/ql/src/semmle/code/cpp/Diagnostics.qll
+++ b/cpp/ql/src/semmle/code/cpp/Diagnostics.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes representing warnings generated during compilation.
+ */
+
import semmle.code.cpp.Location
/** A compiler-generated error, warning or remark. */
@@ -11,6 +15,7 @@ class Diagnostic extends Locatable, @diagnostic {
/** Gets the error code for this compiler message. */
string getTag() { diagnostics(underlyingElement(this), _, result, _, _, _) }
+ /** Holds if `s` is the error code for this compiler message. */
predicate hasTag(string s) { this.getTag() = s }
/**
diff --git a/cpp/ql/src/semmle/code/cpp/Element.qll b/cpp/ql/src/semmle/code/cpp/Element.qll
index 2b236147484..50b72037ff7 100644
--- a/cpp/ql/src/semmle/code/cpp/Element.qll
+++ b/cpp/ql/src/semmle/code/cpp/Element.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides the `Element` class, which is the base class for all classes representing C or C++
+ * program elements.
+ */
+
import semmle.code.cpp.Location
private import semmle.code.cpp.Enclosing
private import semmle.code.cpp.internal.ResolveClass
@@ -261,8 +266,14 @@ private predicate isFromUninstantiatedTemplateRec(Element e, Element template) {
class StaticAssert extends Locatable, @static_assert {
override string toString() { result = "static_assert(..., \"" + getMessage() + "\")" }
+ /**
+ * Gets the expression which this static assertion ensures is true.
+ */
Expr getCondition() { static_asserts(underlyingElement(this), unresolveElement(result), _, _) }
+ /**
+ * Gets the message which will be reported by the compiler if this static assertion fails.
+ */
string getMessage() { static_asserts(underlyingElement(this), _, result, _) }
override Location getLocation() { static_asserts(underlyingElement(this), _, _, result) }
diff --git a/cpp/ql/src/semmle/code/cpp/Enclosing.qll b/cpp/ql/src/semmle/code/cpp/Enclosing.qll
index 07d39b10e83..d821589a76c 100644
--- a/cpp/ql/src/semmle/code/cpp/Enclosing.qll
+++ b/cpp/ql/src/semmle/code/cpp/Enclosing.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides predicates for finding the smallest element that encloses an expression or statement.
+ */
+
import cpp
/**
diff --git a/cpp/ql/src/semmle/code/cpp/Enum.qll b/cpp/ql/src/semmle/code/cpp/Enum.qll
index e3a2ef60ccc..2c51a5228d9 100644
--- a/cpp/ql/src/semmle/code/cpp/Enum.qll
+++ b/cpp/ql/src/semmle/code/cpp/Enum.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes representing C/C++ enums and enum constants.
+ */
+
import semmle.code.cpp.Type
private import semmle.code.cpp.internal.ResolveClass
@@ -19,6 +23,17 @@ class Enum extends UserType, IntegralOrEnumType {
/** Gets an enumerator of this enumeration. */
EnumConstant getAnEnumConstant() { result.getDeclaringEnum() = this }
+ /**
+ * Gets the enumerator of this enumeration that was declared at the zero-based position `index`.
+ * For example, `zero` is at index 2 in the following declaration:
+ * ```
+ * enum ReversedOrder {
+ * two = 2,
+ * one = 1,
+ * zero = 0
+ * };
+ * ```
+ */
EnumConstant getEnumConstant(int index) {
enumconstants(unresolveElement(result), underlyingElement(this), index, _, _, _)
}
diff --git a/cpp/ql/src/semmle/code/cpp/Field.qll b/cpp/ql/src/semmle/code/cpp/Field.qll
index eb864a16063..79c9b58dfea 100644
--- a/cpp/ql/src/semmle/code/cpp/Field.qll
+++ b/cpp/ql/src/semmle/code/cpp/Field.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes representing C structure members and C++ non-static member variables.
+ */
+
import semmle.code.cpp.Variable
import semmle.code.cpp.Enum
import semmle.code.cpp.exprs.Access
diff --git a/cpp/ql/src/semmle/code/cpp/File.qll b/cpp/ql/src/semmle/code/cpp/File.qll
index 60ef2d587ef..061e79c7d45 100644
--- a/cpp/ql/src/semmle/code/cpp/File.qll
+++ b/cpp/ql/src/semmle/code/cpp/File.qll
@@ -1,9 +1,13 @@
+/**
+ * Provides classes representing files and folders.
+ */
+
import semmle.code.cpp.Element
import semmle.code.cpp.Declaration
import semmle.code.cpp.metrics.MetricFile
/** A file or folder. */
-abstract class Container extends Locatable, @container {
+class Container extends Locatable, @container {
/**
* Gets the absolute, canonical path of this container, using forward slashes
* as path separator.
@@ -28,7 +32,7 @@ abstract class Container extends Locatable, @container {
* a bare root prefix, that is, the path has no path segments. A container
* whose absolute path has no segments is always a `Folder`, not a `File`.
*/
- abstract string getAbsolutePath();
+ string getAbsolutePath() { none() } // overridden by subclasses
/**
* DEPRECATED: Use `getLocation` instead.
@@ -36,7 +40,7 @@ abstract class Container extends Locatable, @container {
*
* For more information see [Providing URLs](https://help.semmle.com/QL/learn-ql/ql/locations.html#providing-urls).
*/
- abstract deprecated string getURL();
+ deprecated string getURL() { none() } // overridden by subclasses
/**
* Gets the relative path of this file or folder from the root folder of the
@@ -261,18 +265,6 @@ class File extends Container, @file {
/** Holds if this file was compiled as C++ (at any point). */
predicate compiledAsCpp() { fileannotations(underlyingElement(this), 1, "compiled as c++", "1") }
- /**
- * DEPRECATED: Objective-C is no longer supported.
- * Holds if this file was compiled as Objective C (at any point).
- */
- deprecated predicate compiledAsObjC() { none() }
-
- /**
- * DEPRECATED: Objective-C is no longer supported.
- * Holds if this file was compiled as Objective C++ (at any point).
- */
- deprecated predicate compiledAsObjCpp() { none() }
-
/**
* Holds if this file was compiled by a Microsoft compiler (at any point).
*
@@ -316,14 +308,6 @@ class File extends Container, @file {
exists(Include i | i.getFile() = this and i.getIncludedFile() = result)
}
- /**
- * DEPRECATED: use `getParentContainer` instead.
- * Gets the folder which contains this file.
- */
- deprecated Folder getParent() {
- containerparent(unresolveElement(result), underlyingElement(this))
- }
-
/**
* Holds if this file may be from source. This predicate holds for all files
* except the dummy file, whose name is the empty string, which contains
@@ -341,28 +325,6 @@ class File extends Container, @file {
/** Gets the metric file. */
MetricFile getMetrics() { result = this }
- /**
- * DEPRECATED: Use `getAbsolutePath` instead.
- * Gets the full name of this file, for example:
- * "/usr/home/me/myprogram.c".
- */
- deprecated string getName() { files(underlyingElement(this), result, _, _, _) }
-
- /**
- * DEPRECATED: Use `getAbsolutePath` instead.
- * Holds if this file has the specified full name.
- *
- * Example usage: `f.hasName("/usr/home/me/myprogram.c")`.
- */
- deprecated predicate hasName(string name) { name = this.getName() }
-
- /**
- * DEPRECATED: Use `getAbsolutePath` instead.
- * Gets the full name of this file, for example
- * "/usr/home/me/myprogram.c".
- */
- deprecated string getFullName() { result = this.getName() }
-
/**
* Gets the remainder of the base name after the first dot character. Note
* that the name of this predicate is in plural form, unlike `getExtension`,
@@ -377,22 +339,6 @@ class File extends Container, @file {
*/
string getExtensions() { files(underlyingElement(this), _, _, result, _) }
- /**
- * DEPRECATED: Use `getBaseName` instead.
- * Gets the name and extension(s), but not path, of a file. For example,
- * if the full name is "/path/to/filename.a.bcd" then the filename is
- * "filename.a.bcd".
- */
- deprecated string getFileName() {
- // [a/b.c/d/]fileName
- // ^ beginAfter
- exists(string fullName, int beginAfter |
- fullName = this.getName() and
- beginAfter = max(int i | i = -1 or fullName.charAt(i) = "/" | i) and
- result = fullName.suffix(beginAfter + 1)
- )
- }
-
/**
* Gets the short name of this file, that is, the prefix of its base name up
* to (but not including) the first dot character if there is one, or the
diff --git a/cpp/ql/src/semmle/code/cpp/FriendDecl.qll b/cpp/ql/src/semmle/code/cpp/FriendDecl.qll
index c788ea70058..e0a6b04b1fc 100644
--- a/cpp/ql/src/semmle/code/cpp/FriendDecl.qll
+++ b/cpp/ql/src/semmle/code/cpp/FriendDecl.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides a class representing C++ `friend` declarations.
+ */
+
import semmle.code.cpp.Declaration
private import semmle.code.cpp.internal.ResolveClass
diff --git a/cpp/ql/src/semmle/code/cpp/Function.qll b/cpp/ql/src/semmle/code/cpp/Function.qll
index 9ed4b27f562..979e94c2061 100644
--- a/cpp/ql/src/semmle/code/cpp/Function.qll
+++ b/cpp/ql/src/semmle/code/cpp/Function.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes for working with functions, including template functions.
+ */
+
import semmle.code.cpp.Location
import semmle.code.cpp.Member
import semmle.code.cpp.Class
@@ -103,6 +107,9 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
/**
* Holds if this function is declared to be `constexpr`.
+ *
+ * Note that this does not hold if the function has been declared
+ * `consteval`.
*/
predicate isDeclaredConstexpr() { this.hasSpecifier("declared_constexpr") }
@@ -115,9 +122,16 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
* template constexpr int g(T x) { return f(x); }
* ```
* `g` is declared constexpr, but is not constexpr.
+ *
+ * Will also hold if this function is `consteval`.
*/
predicate isConstexpr() { this.hasSpecifier("is_constexpr") }
+ /**
+ * Holds if this function is declared to be `consteval`.
+ */
+ predicate isConsteval() { this.hasSpecifier("is_consteval") }
+
/**
* Holds if this function is declared with `__attribute__((naked))` or
* `__declspec(naked)`.
@@ -174,17 +188,8 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
* For example: for a function `int Foo(int p1, int p2)` this would
* return `int p1, int p2`.
*/
- string getParameterString() { result = getParameterStringFrom(0) }
-
- private string getParameterStringFrom(int index) {
- index = getNumberOfParameters() and
- result = ""
- or
- index = getNumberOfParameters() - 1 and
- result = getParameter(index).getTypedName()
- or
- index < getNumberOfParameters() - 1 and
- result = getParameter(index).getTypedName() + ", " + getParameterStringFrom(index + 1)
+ string getParameterString() {
+ result = concat(int i | | min(getParameter(i).getTypedName()), ", " order by i)
}
/** Gets a call to this function. */
@@ -606,18 +611,8 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
* For example: for a function 'int Foo(int p1, int p2)' this would
* return 'int p1, int p2'.
*/
- string getParameterString() { result = getParameterStringFrom(0) }
-
- private string getParameterStringFrom(int index) {
- index = getNumberOfParameters() and
- result = ""
- or
- index = getNumberOfParameters() - 1 and
- result = getParameterDeclarationEntry(index).getTypedName()
- or
- index < getNumberOfParameters() - 1 and
- result =
- getParameterDeclarationEntry(index).getTypedName() + ", " + getParameterStringFrom(index + 1)
+ string getParameterString() {
+ result = concat(int i | | min(getParameterDeclarationEntry(i).getTypedName()), ", " order by i)
}
/**
@@ -707,427 +702,6 @@ class TopLevelFunction extends Function {
override string getCanonicalQLClass() { result = "TopLevelFunction" }
}
-/**
- * A C++ function declared as a member of a class [N4140 9.3]. This includes
- * static member functions. For example the functions `MyStaticMemberFunction`
- * and `MyMemberFunction` in:
- * ```
- * class MyClass {
- * public:
- * void MyMemberFunction() {
- * DoSomething();
- * }
- *
- * static void MyStaticMemberFunction() {
- * DoSomething();
- * }
- * };
- * ```
- */
-class MemberFunction extends Function {
- MemberFunction() { this.isMember() }
-
- override string getCanonicalQLClass() {
- not this instanceof CopyAssignmentOperator and
- not this instanceof MoveAssignmentOperator and
- result = "MemberFunction"
- }
-
- /**
- * Gets the number of parameters of this function, including any implicit
- * `this` parameter.
- */
- override int getEffectiveNumberOfParameters() {
- if isStatic() then result = getNumberOfParameters() else result = getNumberOfParameters() + 1
- }
-
- /** Holds if this member is private. */
- predicate isPrivate() { this.hasSpecifier("private") }
-
- /** Holds if this member is protected. */
- predicate isProtected() { this.hasSpecifier("protected") }
-
- /** Holds if this member is public. */
- predicate isPublic() { this.hasSpecifier("public") }
-
- /** Holds if this function overrides that function. */
- predicate overrides(MemberFunction that) {
- overrides(underlyingElement(this), unresolveElement(that))
- }
-
- /** Gets a directly overridden function. */
- MemberFunction getAnOverriddenFunction() { this.overrides(result) }
-
- /** Gets a directly overriding function. */
- MemberFunction getAnOverridingFunction() { result.overrides(this) }
-
- /**
- * Gets the declaration entry for this member function that is within the
- * class body.
- */
- FunctionDeclarationEntry getClassBodyDeclarationEntry() {
- if strictcount(getADeclarationEntry()) = 1
- then result = getDefinition()
- else (
- result = getADeclarationEntry() and result != getDefinition()
- )
- }
-}
-
-/**
- * A C++ virtual function. For example the two functions called
- * `myVirtualFunction` in the following code are each a
- * `VirtualFunction`:
- * ```
- * class A {
- * public:
- * virtual void myVirtualFunction() = 0;
- * };
- *
- * class B: public A {
- * public:
- * virtual void myVirtualFunction() {
- * doSomething();
- * }
- * };
- * ```
- */
-class VirtualFunction extends MemberFunction {
- VirtualFunction() { this.hasSpecifier("virtual") or purefunctions(underlyingElement(this)) }
-
- override string getCanonicalQLClass() { result = "VirtualFunction" }
-
- /** Holds if this virtual function is pure. */
- predicate isPure() { this instanceof PureVirtualFunction }
-
- /**
- * Holds if this function was declared with the `override` specifier
- * [N4140 10.3].
- */
- predicate isOverrideExplicit() { this.hasSpecifier("override") }
-}
-
-/**
- * A C++ pure virtual function [N4140 10.4]. For example the first function
- * called `myVirtualFunction` in the following code:
- * ```
- * class A {
- * public:
- * virtual void myVirtualFunction() = 0;
- * };
- *
- * class B: public A {
- * public:
- * virtual void myVirtualFunction() {
- * doSomething();
- * }
- * };
- * ```
- */
-class PureVirtualFunction extends VirtualFunction {
- PureVirtualFunction() { purefunctions(underlyingElement(this)) }
-
- override string getCanonicalQLClass() { result = "PureVirtualFunction" }
-}
-
-/**
- * A const C++ member function [N4140 9.3.1/4]. A const function has the
- * `const` specifier and does not modify the state of its class. For example
- * the member function `day` in the following code:
- * ```
- * class MyClass {
- * ...
- *
- * int day() const {
- * return d;
- * }
- *
- * ...
- * };
- * ```
- */
-class ConstMemberFunction extends MemberFunction {
- ConstMemberFunction() { this.hasSpecifier("const") }
-
- override string getCanonicalQLClass() { result = "ConstMemberFunction" }
-}
-
-/**
- * A C++ constructor [N4140 12.1]. For example the function `MyClass` in the
- * following code is a constructor:
- * ```
- * class MyClass {
- * public:
- * MyClass() {
- * ...
- * }
- * };
- * ```
- */
-class Constructor extends MemberFunction {
- Constructor() { functions(underlyingElement(this), _, 2) }
-
- override string getCanonicalQLClass() { result = "Constructor" }
-
- /**
- * Holds if this constructor serves as a default constructor.
- *
- * This holds for constructors with zero formal parameters. It also holds
- * for constructors which have a non-zero number of formal parameters,
- * provided that every parameter has a default value.
- */
- predicate isDefault() { forall(Parameter p | p = this.getAParameter() | p.hasInitializer()) }
-
- /**
- * Gets an entry in the constructor's initializer list, or a
- * compiler-generated action which initializes a base class or member
- * variable.
- */
- ConstructorInit getAnInitializer() { result = getInitializer(_) }
-
- /**
- * Gets an entry in the constructor's initializer list, or a
- * compiler-generated action which initializes a base class or member
- * variable. The index specifies the order in which the initializer is
- * to be evaluated.
- */
- ConstructorInit getInitializer(int i) {
- exprparents(unresolveElement(result), i, underlyingElement(this))
- }
-}
-
-/**
- * A function that defines an implicit conversion.
- */
-abstract class ImplicitConversionFunction extends MemberFunction {
- abstract Type getSourceType();
-
- abstract Type getDestType();
-}
-
-/**
- * A C++ constructor that also defines an implicit conversion. For example the
- * function `MyClass` in the following code is a `ConversionConstructor`:
- * ```
- * class MyClass {
- * public:
- * MyClass(const MyOtherClass &from) {
- * ...
- * }
- * };
- * ```
- */
-class ConversionConstructor extends Constructor, ImplicitConversionFunction {
- ConversionConstructor() {
- strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
- not hasSpecifier("explicit") and
- not this instanceof CopyConstructor
- }
-
- override string getCanonicalQLClass() {
- not this instanceof MoveConstructor and result = "ConversionConstructor"
- }
-
- /** Gets the type this `ConversionConstructor` takes as input. */
- override Type getSourceType() { result = this.getParameter(0).getType() }
-
- /** Gets the type this `ConversionConstructor` is a constructor of. */
- override Type getDestType() { result = this.getDeclaringType() }
-}
-
-private predicate hasCopySignature(MemberFunction f) {
- f.getParameter(0).getUnspecifiedType().(LValueReferenceType).getBaseType() = f.getDeclaringType()
-}
-
-private predicate hasMoveSignature(MemberFunction f) {
- f.getParameter(0).getUnspecifiedType().(RValueReferenceType).getBaseType() = f.getDeclaringType()
-}
-
-/**
- * A C++ copy constructor [N4140 12.8]. For example the function `MyClass` in
- * the following code is a `CopyConstructor`:
- * ```
- * class MyClass {
- * public:
- * MyClass(const MyClass &from) {
- * ...
- * }
- * };
- * ```
- *
- * As per the standard, a copy constructor of class `T` is a non-template
- * constructor whose first parameter has type `T&`, `const T&`, `volatile
- * T&`, or `const volatile T&`, and either there are no other parameters,
- * or the rest of the parameters all have default values.
- *
- * For template classes, it can generally not be determined until instantiation
- * whether a constructor is a copy constructor. For such classes, `CopyConstructor`
- * over-approximates the set of copy constructors; if an under-approximation is
- * desired instead, see the member predicate
- * `mayNotBeCopyConstructorInInstantiation`.
- */
-class CopyConstructor extends Constructor {
- CopyConstructor() {
- hasCopySignature(this) and
- (
- // The rest of the parameters all have default values
- forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer())
- or
- // or this is a template class, in which case the default values have
- // not been extracted even if they exist. In that case, we assume that
- // there are default values present since that is the most common case
- // in real-world code.
- getDeclaringType() instanceof TemplateClass
- ) and
- not exists(getATemplateArgument())
- }
-
- override string getCanonicalQLClass() { result = "CopyConstructor" }
-
- /**
- * Holds if we cannot determine that this constructor will become a copy
- * constructor in all instantiations. Depending on template parameters of the
- * enclosing class, this may become an ordinary constructor or a copy
- * constructor.
- */
- predicate mayNotBeCopyConstructorInInstantiation() {
- // In general, default arguments of template classes can only be
- // type-checked for each template instantiation; if an argument in an
- // instantiation fails to type-check then the corresponding parameter has
- // no default argument in the instantiation.
- getDeclaringType() instanceof TemplateClass and
- getNumberOfParameters() > 1
- }
-}
-
-/**
- * A C++ move constructor [N4140 12.8]. For example the function `MyClass` in
- * the following code is a `MoveConstructor`:
- * ```
- * class MyClass {
- * public:
- * MyClass(MyClass &&from) {
- * ...
- * }
- * };
- * ```
- *
- * As per the standard, a move constructor of class `T` is a non-template
- * constructor whose first parameter is `T&&`, `const T&&`, `volatile T&&`,
- * or `const volatile T&&`, and either there are no other parameters, or
- * the rest of the parameters all have default values.
- *
- * For template classes, it can generally not be determined until instantiation
- * whether a constructor is a move constructor. For such classes, `MoveConstructor`
- * over-approximates the set of move constructors; if an under-approximation is
- * desired instead, see the member predicate
- * `mayNotBeMoveConstructorInInstantiation`.
- */
-class MoveConstructor extends Constructor {
- MoveConstructor() {
- hasMoveSignature(this) and
- (
- // The rest of the parameters all have default values
- forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer())
- or
- // or this is a template class, in which case the default values have
- // not been extracted even if they exist. In that case, we assume that
- // there are default values present since that is the most common case
- // in real-world code.
- getDeclaringType() instanceof TemplateClass
- ) and
- not exists(getATemplateArgument())
- }
-
- override string getCanonicalQLClass() { result = "MoveConstructor" }
-
- /**
- * Holds if we cannot determine that this constructor will become a move
- * constructor in all instantiations. Depending on template parameters of the
- * enclosing class, this may become an ordinary constructor or a move
- * constructor.
- */
- predicate mayNotBeMoveConstructorInInstantiation() {
- // In general, default arguments of template classes can only be
- // type-checked for each template instantiation; if an argument in an
- // instantiation fails to type-check then the corresponding parameter has
- // no default argument in the instantiation.
- getDeclaringType() instanceof TemplateClass and
- getNumberOfParameters() > 1
- }
-}
-
-/**
- * A C++ constructor that takes no arguments ('default' constructor). This
- * is the constructor that is invoked when no initializer is given. For
- * example the function `MyClass` in the following code is a
- * `NoArgConstructor`:
- * ```
- * class MyClass {
- * public:
- * MyClass() {
- * ...
- * }
- * };
- * ```
- */
-class NoArgConstructor extends Constructor {
- NoArgConstructor() { this.getNumberOfParameters() = 0 }
-}
-
-/**
- * A C++ destructor [N4140 12.4]. For example the function `~MyClass` in the
- * following code is a destructor:
- * ```
- * class MyClass {
- * public:
- * ~MyClass() {
- * ...
- * }
- * };
- * ```
- */
-class Destructor extends MemberFunction {
- Destructor() { functions(underlyingElement(this), _, 3) }
-
- override string getCanonicalQLClass() { result = "Destructor" }
-
- /**
- * Gets a compiler-generated action which destructs a base class or member
- * variable.
- */
- DestructorDestruction getADestruction() { result = getDestruction(_) }
-
- /**
- * Gets a compiler-generated action which destructs a base class or member
- * variable. The index specifies the order in which the destruction should
- * be evaluated.
- */
- DestructorDestruction getDestruction(int i) {
- exprparents(unresolveElement(result), i, underlyingElement(this))
- }
-}
-
-/**
- * A C++ conversion operator [N4140 12.3.2]. For example the function
- * `operator int` in the following code is a `ConversionOperator`:
- * ```
- * class MyClass {
- * public:
- * operator int();
- * };
- * ```
- */
-class ConversionOperator extends MemberFunction, ImplicitConversionFunction {
- ConversionOperator() { functions(underlyingElement(this), _, 4) }
-
- override string getCanonicalQLClass() { result = "ConversionOperator" }
-
- override Type getSourceType() { result = this.getDeclaringType() }
-
- override Type getDestType() { result = this.getType() }
-}
-
/**
* A C++ user-defined operator [N4140 13.5].
*/
@@ -1139,64 +713,6 @@ class Operator extends Function {
}
}
-/**
- * A C++ copy assignment operator [N4140 12.8]. For example the function
- * `operator=` in the following code is a `CopyAssignmentOperator`:
- * ```
- * class MyClass {
- * public:
- * MyClass &operator=(const MyClass &other);
- * };
- * ```
- *
- * As per the standard, a copy assignment operator of class `T` is a
- * non-template non-static member function with the name `operator=` that
- * takes exactly one parameter of type `T`, `T&`, `const T&`, `volatile
- * T&`, or `const volatile T&`.
- */
-class CopyAssignmentOperator extends Operator {
- CopyAssignmentOperator() {
- hasName("operator=") and
- (
- hasCopySignature(this)
- or
- // Unlike CopyConstructor, this member allows a non-reference
- // parameter.
- getParameter(0).getUnspecifiedType() = getDeclaringType()
- ) and
- not exists(this.getParameter(1)) and
- not exists(getATemplateArgument())
- }
-
- override string getCanonicalQLClass() { result = "CopyAssignmentOperator" }
-}
-
-/**
- * A C++ move assignment operator [N4140 12.8]. For example the function
- * `operator=` in the following code is a `MoveAssignmentOperator`:
- * ```
- * class MyClass {
- * public:
- * MyClass &operator=(MyClass &&other);
- * };
- * ```
- *
- * As per the standard, a move assignment operator of class `T` is a
- * non-template non-static member function with the name `operator=` that
- * takes exactly one parameter of type `T&&`, `const T&&`, `volatile T&&`,
- * or `const volatile T&&`.
- */
-class MoveAssignmentOperator extends Operator {
- MoveAssignmentOperator() {
- hasName("operator=") and
- hasMoveSignature(this) and
- not exists(this.getParameter(1)) and
- not exists(getATemplateArgument())
- }
-
- override string getCanonicalQLClass() { result = "MoveAssignmentOperator" }
-}
-
/**
* A C++ function which has a non-empty template argument list. For example
* the function `myTemplateFunction` in the following code:
diff --git a/cpp/ql/src/semmle/code/cpp/Include.qll b/cpp/ql/src/semmle/code/cpp/Include.qll
index f5e4fae619c..11702ce1bf6 100644
--- a/cpp/ql/src/semmle/code/cpp/Include.qll
+++ b/cpp/ql/src/semmle/code/cpp/Include.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides classes representing C/C++ `#include`, `#include_next`, and `#import` preprocessor
+ * directives.
+ */
+
import semmle.code.cpp.Preprocessor
/**
diff --git a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll
new file mode 100644
index 00000000000..0ccc63196ae
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll
@@ -0,0 +1,487 @@
+/**
+ * Provides classes for working with C++ member functions, constructors, destructors,
+ * and user-defined operators.
+ */
+
+import cpp
+
+/**
+ * A C++ function declared as a member of a class [N4140 9.3]. This includes
+ * static member functions. For example the functions `MyStaticMemberFunction`
+ * and `MyMemberFunction` in:
+ * ```
+ * class MyClass {
+ * public:
+ * void MyMemberFunction() {
+ * DoSomething();
+ * }
+ *
+ * static void MyStaticMemberFunction() {
+ * DoSomething();
+ * }
+ * };
+ * ```
+ */
+class MemberFunction extends Function {
+ MemberFunction() { this.isMember() }
+
+ override string getCanonicalQLClass() {
+ not this instanceof CopyAssignmentOperator and
+ not this instanceof MoveAssignmentOperator and
+ result = "MemberFunction"
+ }
+
+ /**
+ * Gets the number of parameters of this function, including any implicit
+ * `this` parameter.
+ */
+ override int getEffectiveNumberOfParameters() {
+ if isStatic() then result = getNumberOfParameters() else result = getNumberOfParameters() + 1
+ }
+
+ /** Holds if this member is private. */
+ predicate isPrivate() { this.hasSpecifier("private") }
+
+ /** Holds if this member is protected. */
+ predicate isProtected() { this.hasSpecifier("protected") }
+
+ /** Holds if this member is public. */
+ predicate isPublic() { this.hasSpecifier("public") }
+
+ /** Holds if this function overrides that function. */
+ predicate overrides(MemberFunction that) {
+ overrides(underlyingElement(this), unresolveElement(that))
+ }
+
+ /** Gets a directly overridden function. */
+ MemberFunction getAnOverriddenFunction() { this.overrides(result) }
+
+ /** Gets a directly overriding function. */
+ MemberFunction getAnOverridingFunction() { result.overrides(this) }
+
+ /**
+ * Gets the declaration entry for this member function that is within the
+ * class body.
+ */
+ FunctionDeclarationEntry getClassBodyDeclarationEntry() {
+ if strictcount(getADeclarationEntry()) = 1
+ then result = getDefinition()
+ else (
+ result = getADeclarationEntry() and result != getDefinition()
+ )
+ }
+}
+
+/**
+ * A C++ virtual function. For example the two functions called
+ * `myVirtualFunction` in the following code are each a
+ * `VirtualFunction`:
+ * ```
+ * class A {
+ * public:
+ * virtual void myVirtualFunction() = 0;
+ * };
+ *
+ * class B: public A {
+ * public:
+ * virtual void myVirtualFunction() {
+ * doSomething();
+ * }
+ * };
+ * ```
+ */
+class VirtualFunction extends MemberFunction {
+ VirtualFunction() { this.hasSpecifier("virtual") or purefunctions(underlyingElement(this)) }
+
+ override string getCanonicalQLClass() { result = "VirtualFunction" }
+
+ /** Holds if this virtual function is pure. */
+ predicate isPure() { this instanceof PureVirtualFunction }
+
+ /**
+ * Holds if this function was declared with the `override` specifier
+ * [N4140 10.3].
+ */
+ predicate isOverrideExplicit() { this.hasSpecifier("override") }
+}
+
+/**
+ * A C++ pure virtual function [N4140 10.4]. For example the first function
+ * called `myVirtualFunction` in the following code:
+ * ```
+ * class A {
+ * public:
+ * virtual void myVirtualFunction() = 0;
+ * };
+ *
+ * class B: public A {
+ * public:
+ * virtual void myVirtualFunction() {
+ * doSomething();
+ * }
+ * };
+ * ```
+ */
+class PureVirtualFunction extends VirtualFunction {
+ PureVirtualFunction() { purefunctions(underlyingElement(this)) }
+
+ override string getCanonicalQLClass() { result = "PureVirtualFunction" }
+}
+
+/**
+ * A const C++ member function [N4140 9.3.1/4]. A const function has the
+ * `const` specifier and does not modify the state of its class. For example
+ * the member function `day` in the following code:
+ * ```
+ * class MyClass {
+ * ...
+ *
+ * int day() const {
+ * return d;
+ * }
+ *
+ * ...
+ * };
+ * ```
+ */
+class ConstMemberFunction extends MemberFunction {
+ ConstMemberFunction() { this.hasSpecifier("const") }
+
+ override string getCanonicalQLClass() { result = "ConstMemberFunction" }
+}
+
+/**
+ * A C++ constructor [N4140 12.1]. For example the function `MyClass` in the
+ * following code is a constructor:
+ * ```
+ * class MyClass {
+ * public:
+ * MyClass() {
+ * ...
+ * }
+ * };
+ * ```
+ */
+class Constructor extends MemberFunction {
+ Constructor() { functions(underlyingElement(this), _, 2) }
+
+ override string getCanonicalQLClass() { result = "Constructor" }
+
+ /**
+ * Holds if this constructor serves as a default constructor.
+ *
+ * This holds for constructors with zero formal parameters. It also holds
+ * for constructors which have a non-zero number of formal parameters,
+ * provided that every parameter has a default value.
+ */
+ predicate isDefault() { forall(Parameter p | p = this.getAParameter() | p.hasInitializer()) }
+
+ /**
+ * Gets an entry in the constructor's initializer list, or a
+ * compiler-generated action which initializes a base class or member
+ * variable.
+ */
+ ConstructorInit getAnInitializer() { result = getInitializer(_) }
+
+ /**
+ * Gets an entry in the constructor's initializer list, or a
+ * compiler-generated action which initializes a base class or member
+ * variable. The index specifies the order in which the initializer is
+ * to be evaluated.
+ */
+ ConstructorInit getInitializer(int i) {
+ exprparents(unresolveElement(result), i, underlyingElement(this))
+ }
+}
+
+/**
+ * A function that defines an implicit conversion.
+ */
+abstract class ImplicitConversionFunction extends MemberFunction {
+ /** Gets the type this `ImplicitConversionFunction` takes as input. */
+ abstract Type getSourceType();
+
+ /** Gets the type this `ImplicitConversionFunction` converts to. */
+ abstract Type getDestType();
+}
+
+/**
+ * A C++ constructor that also defines an implicit conversion. For example the
+ * function `MyClass` in the following code is a `ConversionConstructor`:
+ * ```
+ * class MyClass {
+ * public:
+ * MyClass(const MyOtherClass &from) {
+ * ...
+ * }
+ * };
+ * ```
+ */
+class ConversionConstructor extends Constructor, ImplicitConversionFunction {
+ ConversionConstructor() {
+ strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
+ not hasSpecifier("explicit") and
+ not this instanceof CopyConstructor
+ }
+
+ override string getCanonicalQLClass() {
+ not this instanceof MoveConstructor and result = "ConversionConstructor"
+ }
+
+ /** Gets the type this `ConversionConstructor` takes as input. */
+ override Type getSourceType() { result = this.getParameter(0).getType() }
+
+ /** Gets the type this `ConversionConstructor` is a constructor of. */
+ override Type getDestType() { result = this.getDeclaringType() }
+}
+
+private predicate hasCopySignature(MemberFunction f) {
+ f.getParameter(0).getUnspecifiedType().(LValueReferenceType).getBaseType() = f.getDeclaringType()
+}
+
+private predicate hasMoveSignature(MemberFunction f) {
+ f.getParameter(0).getUnspecifiedType().(RValueReferenceType).getBaseType() = f.getDeclaringType()
+}
+
+/**
+ * A C++ copy constructor [N4140 12.8]. For example the function `MyClass` in
+ * the following code is a `CopyConstructor`:
+ * ```
+ * class MyClass {
+ * public:
+ * MyClass(const MyClass &from) {
+ * ...
+ * }
+ * };
+ * ```
+ *
+ * As per the standard, a copy constructor of class `T` is a non-template
+ * constructor whose first parameter has type `T&`, `const T&`, `volatile
+ * T&`, or `const volatile T&`, and either there are no other parameters,
+ * or the rest of the parameters all have default values.
+ *
+ * For template classes, it can generally not be determined until instantiation
+ * whether a constructor is a copy constructor. For such classes, `CopyConstructor`
+ * over-approximates the set of copy constructors; if an under-approximation is
+ * desired instead, see the member predicate
+ * `mayNotBeCopyConstructorInInstantiation`.
+ */
+class CopyConstructor extends Constructor {
+ CopyConstructor() {
+ hasCopySignature(this) and
+ (
+ // The rest of the parameters all have default values
+ forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer())
+ or
+ // or this is a template class, in which case the default values have
+ // not been extracted even if they exist. In that case, we assume that
+ // there are default values present since that is the most common case
+ // in real-world code.
+ getDeclaringType() instanceof TemplateClass
+ ) and
+ not exists(getATemplateArgument())
+ }
+
+ override string getCanonicalQLClass() { result = "CopyConstructor" }
+
+ /**
+ * Holds if we cannot determine that this constructor will become a copy
+ * constructor in all instantiations. Depending on template parameters of the
+ * enclosing class, this may become an ordinary constructor or a copy
+ * constructor.
+ */
+ predicate mayNotBeCopyConstructorInInstantiation() {
+ // In general, default arguments of template classes can only be
+ // type-checked for each template instantiation; if an argument in an
+ // instantiation fails to type-check then the corresponding parameter has
+ // no default argument in the instantiation.
+ getDeclaringType() instanceof TemplateClass and
+ getNumberOfParameters() > 1
+ }
+}
+
+/**
+ * A C++ move constructor [N4140 12.8]. For example the function `MyClass` in
+ * the following code is a `MoveConstructor`:
+ * ```
+ * class MyClass {
+ * public:
+ * MyClass(MyClass &&from) {
+ * ...
+ * }
+ * };
+ * ```
+ *
+ * As per the standard, a move constructor of class `T` is a non-template
+ * constructor whose first parameter is `T&&`, `const T&&`, `volatile T&&`,
+ * or `const volatile T&&`, and either there are no other parameters, or
+ * the rest of the parameters all have default values.
+ *
+ * For template classes, it can generally not be determined until instantiation
+ * whether a constructor is a move constructor. For such classes, `MoveConstructor`
+ * over-approximates the set of move constructors; if an under-approximation is
+ * desired instead, see the member predicate
+ * `mayNotBeMoveConstructorInInstantiation`.
+ */
+class MoveConstructor extends Constructor {
+ MoveConstructor() {
+ hasMoveSignature(this) and
+ (
+ // The rest of the parameters all have default values
+ forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer())
+ or
+ // or this is a template class, in which case the default values have
+ // not been extracted even if they exist. In that case, we assume that
+ // there are default values present since that is the most common case
+ // in real-world code.
+ getDeclaringType() instanceof TemplateClass
+ ) and
+ not exists(getATemplateArgument())
+ }
+
+ override string getCanonicalQLClass() { result = "MoveConstructor" }
+
+ /**
+ * Holds if we cannot determine that this constructor will become a move
+ * constructor in all instantiations. Depending on template parameters of the
+ * enclosing class, this may become an ordinary constructor or a move
+ * constructor.
+ */
+ predicate mayNotBeMoveConstructorInInstantiation() {
+ // In general, default arguments of template classes can only be
+ // type-checked for each template instantiation; if an argument in an
+ // instantiation fails to type-check then the corresponding parameter has
+ // no default argument in the instantiation.
+ getDeclaringType() instanceof TemplateClass and
+ getNumberOfParameters() > 1
+ }
+}
+
+/**
+ * A C++ constructor that takes no arguments ('default' constructor). This
+ * is the constructor that is invoked when no initializer is given. For
+ * example the function `MyClass` in the following code is a
+ * `NoArgConstructor`:
+ * ```
+ * class MyClass {
+ * public:
+ * MyClass() {
+ * ...
+ * }
+ * };
+ * ```
+ */
+class NoArgConstructor extends Constructor {
+ NoArgConstructor() { this.getNumberOfParameters() = 0 }
+}
+
+/**
+ * A C++ destructor [N4140 12.4]. For example the function `~MyClass` in the
+ * following code is a destructor:
+ * ```
+ * class MyClass {
+ * public:
+ * ~MyClass() {
+ * ...
+ * }
+ * };
+ * ```
+ */
+class Destructor extends MemberFunction {
+ Destructor() { functions(underlyingElement(this), _, 3) }
+
+ override string getCanonicalQLClass() { result = "Destructor" }
+
+ /**
+ * Gets a compiler-generated action which destructs a base class or member
+ * variable.
+ */
+ DestructorDestruction getADestruction() { result = getDestruction(_) }
+
+ /**
+ * Gets a compiler-generated action which destructs a base class or member
+ * variable. The index specifies the order in which the destruction should
+ * be evaluated.
+ */
+ DestructorDestruction getDestruction(int i) {
+ exprparents(unresolveElement(result), i, underlyingElement(this))
+ }
+}
+
+/**
+ * A C++ conversion operator [N4140 12.3.2]. For example the function
+ * `operator int` in the following code is a `ConversionOperator`:
+ * ```
+ * class MyClass {
+ * public:
+ * operator int();
+ * };
+ * ```
+ */
+class ConversionOperator extends MemberFunction, ImplicitConversionFunction {
+ ConversionOperator() { functions(underlyingElement(this), _, 4) }
+
+ override string getCanonicalQLClass() { result = "ConversionOperator" }
+
+ override Type getSourceType() { result = this.getDeclaringType() }
+
+ override Type getDestType() { result = this.getType() }
+}
+
+/**
+ * A C++ copy assignment operator [N4140 12.8]. For example the function
+ * `operator=` in the following code is a `CopyAssignmentOperator`:
+ * ```
+ * class MyClass {
+ * public:
+ * MyClass &operator=(const MyClass &other);
+ * };
+ * ```
+ *
+ * As per the standard, a copy assignment operator of class `T` is a
+ * non-template non-static member function with the name `operator=` that
+ * takes exactly one parameter of type `T`, `T&`, `const T&`, `volatile
+ * T&`, or `const volatile T&`.
+ */
+class CopyAssignmentOperator extends Operator {
+ CopyAssignmentOperator() {
+ hasName("operator=") and
+ (
+ hasCopySignature(this)
+ or
+ // Unlike CopyConstructor, this member allows a non-reference
+ // parameter.
+ getParameter(0).getUnspecifiedType() = getDeclaringType()
+ ) and
+ not exists(this.getParameter(1)) and
+ not exists(getATemplateArgument())
+ }
+
+ override string getCanonicalQLClass() { result = "CopyAssignmentOperator" }
+}
+
+/**
+ * A C++ move assignment operator [N4140 12.8]. For example the function
+ * `operator=` in the following code is a `MoveAssignmentOperator`:
+ * ```
+ * class MyClass {
+ * public:
+ * MyClass &operator=(MyClass &&other);
+ * };
+ * ```
+ *
+ * As per the standard, a move assignment operator of class `T` is a
+ * non-template non-static member function with the name `operator=` that
+ * takes exactly one parameter of type `T&&`, `const T&&`, `volatile T&&`,
+ * or `const volatile T&&`.
+ */
+class MoveAssignmentOperator extends Operator {
+ MoveAssignmentOperator() {
+ hasName("operator=") and
+ hasMoveSignature(this) and
+ not exists(this.getParameter(1)) and
+ not exists(getATemplateArgument())
+ }
+
+ override string getCanonicalQLClass() { result = "MoveAssignmentOperator" }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/Namespace.qll b/cpp/ql/src/semmle/code/cpp/Namespace.qll
index d53fda87c2f..9b9b12aaef0 100644
--- a/cpp/ql/src/semmle/code/cpp/Namespace.qll
+++ b/cpp/ql/src/semmle/code/cpp/Namespace.qll
@@ -79,7 +79,10 @@ class Namespace extends NameQualifyingElement, @namespace {
/** Gets the metric namespace. */
MetricNamespace getMetrics() { result = this }
- override string toString() { result = this.getQualifiedName() }
+ /** Gets a version of the `QualifiedName` that is more suitable for display purposes. */
+ string getFriendlyName() { result = this.getQualifiedName() }
+
+ final override string toString() { result = getFriendlyName() }
/** Gets a declaration of (part of) this namespace. */
NamespaceDeclarationEntry getADeclarationEntry() { result.getNamespace() = this }
@@ -104,7 +107,7 @@ class NamespaceDeclarationEntry extends Locatable, @namespace_decl {
namespace_decls(underlyingElement(this), unresolveElement(result), _, _)
}
- override string toString() { result = this.getNamespace().toString() }
+ override string toString() { result = this.getNamespace().getFriendlyName() }
/**
* Gets the location of the token preceding the namespace declaration
@@ -130,7 +133,7 @@ class NamespaceDeclarationEntry extends Locatable, @namespace_decl {
/**
* A C++ `using` directive or `using` declaration.
*/
-abstract class UsingEntry extends Locatable, @using {
+class UsingEntry extends Locatable, @using {
override Location getLocation() { usings(underlyingElement(this), _, result) }
}
@@ -150,7 +153,7 @@ class UsingDeclarationEntry extends UsingEntry {
*/
Declaration getDeclaration() { usings(underlyingElement(this), unresolveElement(result), _) }
- override string toString() { result = "using " + this.getDeclaration().toString() }
+ override string toString() { result = "using " + this.getDeclaration().getDescription() }
}
/**
@@ -169,7 +172,7 @@ class UsingDirectiveEntry extends UsingEntry {
*/
Namespace getNamespace() { usings(underlyingElement(this), unresolveElement(result), _) }
- override string toString() { result = "using namespace " + this.getNamespace().toString() }
+ override string toString() { result = "using namespace " + this.getNamespace().getFriendlyName() }
}
/**
@@ -204,7 +207,7 @@ class GlobalNamespace extends Namespace {
*/
deprecated string getFullName() { result = this.getName() }
- override string toString() { result = "(global namespace)" }
+ override string getFriendlyName() { result = "(global namespace)" }
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/Parameter.qll b/cpp/ql/src/semmle/code/cpp/Parameter.qll
index 1fbd8b0f071..91957f2d498 100644
--- a/cpp/ql/src/semmle/code/cpp/Parameter.qll
+++ b/cpp/ql/src/semmle/code/cpp/Parameter.qll
@@ -165,6 +165,7 @@ class Parameter extends LocalScopeVariable, @parameter {
class ParameterIndex extends int {
ParameterIndex() {
exists(Parameter p | this = p.getIndex()) or
- exists(Call c | exists(c.getArgument(this))) // permit indexing varargs
+ exists(Call c | exists(c.getArgument(this))) or // permit indexing varargs
+ this = -1 // used for `this`
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/Type.qll b/cpp/ql/src/semmle/code/cpp/Type.qll
index 38e5efba6df..55eb4f27d3d 100644
--- a/cpp/ql/src/semmle/code/cpp/Type.qll
+++ b/cpp/ql/src/semmle/code/cpp/Type.qll
@@ -376,6 +376,8 @@ private predicate isIntegralType(@builtintype type, int kind) {
kind = 43
or
kind = 44
+ or
+ kind = 51
)
}
@@ -463,6 +465,8 @@ private predicate integralTypeMapping(int original, int canonical, int unsigned,
original = 43 and canonical = 43 and unsigned = -1 and signed = -1 // char16_t
or
original = 44 and canonical = 44 and unsigned = -1 and signed = -1 // char32_t
+ or
+ original = 51 and canonical = 51 and unsigned = -1 and signed = -1 // char8_t
}
/**
@@ -697,28 +701,188 @@ class Int128Type extends IntegralType {
override string getCanonicalQLClass() { result = "Int128Type" }
}
+private newtype TTypeDomain =
+ TRealDomain() or
+ TComplexDomain() or
+ TImaginaryDomain()
+
/**
- * The C/C++ floating point types. See 4.5. This includes `float`,
- * `double` and `long double` types.
- * ```
- * float f;
- * double d;
- * long double ld;
- * ```
+ * The type domain of a floating-point type. One of `RealDomain`, `ComplexDomain`, or
+ * `ImaginaryDomain`.
+ */
+class TypeDomain extends TTypeDomain {
+ /** Gets a textual representation of this type domain. */
+ string toString() { none() }
+}
+
+/**
+ * The type domain of a floating-point type that represents a real number.
+ */
+class RealDomain extends TypeDomain, TRealDomain {
+ final override string toString() { result = "real" }
+}
+
+/**
+ * The type domain of a floating-point type that represents a complex number.
+ */
+class ComplexDomain extends TypeDomain, TComplexDomain {
+ final override string toString() { result = "complex" }
+}
+
+/**
+ * The type domain of a floating-point type that represents an imaginary number.
+ */
+class ImaginaryDomain extends TypeDomain, TImaginaryDomain {
+ final override string toString() { result = "imaginary" }
+}
+
+/**
+ * Data for floating-point types.
+ *
+ * kind: The original type kind. Can be any floating-point type kind.
+ * base: The numeric base of the number's representation. Can be 2 (binary) or 10 (decimal).
+ * domain: The type domain of the type. Can be `RealDomain`, `ComplexDomain`, or `ImaginaryDomain`.
+ * realKind: The type kind of the corresponding real type. For example, the corresponding real type
+ * of `_Complex double` is `double`.
+ * extended: `true` if the number is an extended-precision floating-point number, such as
+ * `_Float32x`.
+ */
+private predicate floatingPointTypeMapping(
+ int kind, int base, TTypeDomain domain, int realKind, boolean extended
+) {
+ // float
+ kind = 24 and base = 2 and domain = TRealDomain() and realKind = 24 and extended = false
+ or
+ // double
+ kind = 25 and base = 2 and domain = TRealDomain() and realKind = 25 and extended = false
+ or
+ // long double
+ kind = 26 and base = 2 and domain = TRealDomain() and realKind = 26 and extended = false
+ or
+ // _Complex float
+ kind = 27 and base = 2 and domain = TComplexDomain() and realKind = 24 and extended = false
+ or
+ // _Complex double
+ kind = 28 and base = 2 and domain = TComplexDomain() and realKind = 25 and extended = false
+ or
+ // _Complex long double
+ kind = 29 and base = 2 and domain = TComplexDomain() and realKind = 26 and extended = false
+ or
+ // _Imaginary float
+ kind = 30 and base = 2 and domain = TImaginaryDomain() and realKind = 24 and extended = false
+ or
+ // _Imaginary double
+ kind = 31 and base = 2 and domain = TImaginaryDomain() and realKind = 25 and extended = false
+ or
+ // _Imaginary long double
+ kind = 32 and base = 2 and domain = TImaginaryDomain() and realKind = 26 and extended = false
+ or
+ // __float128
+ kind = 38 and base = 2 and domain = TRealDomain() and realKind = 38 and extended = false
+ or
+ // _Complex __float128
+ kind = 39 and base = 2 and domain = TComplexDomain() and realKind = 38 and extended = false
+ or
+ // _Decimal32
+ kind = 40 and base = 10 and domain = TRealDomain() and realKind = 40 and extended = false
+ or
+ // _Decimal64
+ kind = 41 and base = 10 and domain = TRealDomain() and realKind = 41 and extended = false
+ or
+ // _Decimal128
+ kind = 42 and base = 10 and domain = TRealDomain() and realKind = 42 and extended = false
+ or
+ // _Float32
+ kind = 45 and base = 2 and domain = TRealDomain() and realKind = 45 and extended = false
+ or
+ // _Float32x
+ kind = 46 and base = 2 and domain = TRealDomain() and realKind = 46 and extended = true
+ or
+ // _Float64
+ kind = 47 and base = 2 and domain = TRealDomain() and realKind = 47 and extended = false
+ or
+ // _Float64x
+ kind = 48 and base = 2 and domain = TRealDomain() and realKind = 48 and extended = true
+ or
+ // _Float128
+ kind = 49 and base = 2 and domain = TRealDomain() and realKind = 49 and extended = false
+ or
+ // _Float128x
+ kind = 50 and base = 2 and domain = TRealDomain() and realKind = 50 and extended = true
+}
+
+/**
+ * The C/C++ floating point types. See 4.5. This includes `float`, `double` and `long double`, the
+ * fixed-size floating-point types like `_Float32`, the extended-precision floating-point types like
+ * `_Float64x`, and the decimal floating-point types like `_Decimal32`. It also includes the complex
+ * and imaginary versions of all of these types.
*/
class FloatingPointType extends ArithmeticType {
+ final int base;
+ final TypeDomain domain;
+ final int realKind;
+ final boolean extended;
+
FloatingPointType() {
exists(int kind |
builtintypes(underlyingElement(this), _, kind, _, _, _) and
- (
- kind >= 24 and kind <= 32
- or
- kind >= 38 and kind <= 42
- or
- kind >= 45 and kind <= 50
- )
+ floatingPointTypeMapping(kind, base, domain, realKind, extended)
)
}
+
+ /** Gets the numeric base of this type's representation: 2 (binary) or 10 (decimal). */
+ final int getBase() { result = base }
+
+ /**
+ * Gets the type domain of this type. Can be `RealDomain`, `ComplexDomain`, or `ImaginaryDomain`.
+ */
+ final TypeDomain getDomain() { result = domain }
+
+ /**
+ * Gets the corresponding real type of this type. For example, the corresponding real type of
+ * `_Complex double` is `double`.
+ */
+ final RealNumberType getRealType() {
+ builtintypes(unresolveElement(result), _, realKind, _, _, _)
+ }
+
+ /** Holds if this type is an extended precision floating-point type, such as `_Float32x`. */
+ final predicate isExtendedPrecision() { extended = true }
+}
+
+/**
+ * A floating-point type representing a real number.
+ */
+class RealNumberType extends FloatingPointType {
+ RealNumberType() { domain instanceof RealDomain }
+}
+
+/**
+ * A floating-point type representing a complex number.
+ */
+class ComplexNumberType extends FloatingPointType {
+ ComplexNumberType() { domain instanceof ComplexDomain }
+}
+
+/**
+ * A floating-point type representing an imaginary number.
+ */
+class ImaginaryNumberType extends FloatingPointType {
+ ImaginaryNumberType() { domain instanceof ImaginaryDomain }
+}
+
+/**
+ * A floating-point type whose representation is base 2.
+ */
+class BinaryFloatingPointType extends FloatingPointType {
+ BinaryFloatingPointType() { base = 2 }
+}
+
+/**
+ * A floating-point type whose representation is base 10.
+ */
+class DecimalFloatingPointType extends FloatingPointType {
+ DecimalFloatingPointType() { base = 10 }
}
/**
@@ -727,7 +891,7 @@ class FloatingPointType extends ArithmeticType {
* float f;
* ```
*/
-class FloatType extends FloatingPointType {
+class FloatType extends RealNumberType, BinaryFloatingPointType {
FloatType() { builtintypes(underlyingElement(this), _, 24, _, _, _) }
override string getCanonicalQLClass() { result = "FloatType" }
@@ -739,7 +903,7 @@ class FloatType extends FloatingPointType {
* double d;
* ```
*/
-class DoubleType extends FloatingPointType {
+class DoubleType extends RealNumberType, BinaryFloatingPointType {
DoubleType() { builtintypes(underlyingElement(this), _, 25, _, _, _) }
override string getCanonicalQLClass() { result = "DoubleType" }
@@ -751,7 +915,7 @@ class DoubleType extends FloatingPointType {
* long double ld;
* ```
*/
-class LongDoubleType extends FloatingPointType {
+class LongDoubleType extends RealNumberType, BinaryFloatingPointType {
LongDoubleType() { builtintypes(underlyingElement(this), _, 26, _, _, _) }
override string getCanonicalQLClass() { result = "LongDoubleType" }
@@ -763,7 +927,7 @@ class LongDoubleType extends FloatingPointType {
* __float128 f128;
* ```
*/
-class Float128Type extends FloatingPointType {
+class Float128Type extends RealNumberType, BinaryFloatingPointType {
Float128Type() { builtintypes(underlyingElement(this), _, 38, _, _, _) }
override string getCanonicalQLClass() { result = "Float128Type" }
@@ -775,7 +939,7 @@ class Float128Type extends FloatingPointType {
* _Decimal32 d32;
* ```
*/
-class Decimal32Type extends FloatingPointType {
+class Decimal32Type extends RealNumberType, DecimalFloatingPointType {
Decimal32Type() { builtintypes(underlyingElement(this), _, 40, _, _, _) }
override string getCanonicalQLClass() { result = "Decimal32Type" }
@@ -787,7 +951,7 @@ class Decimal32Type extends FloatingPointType {
* _Decimal64 d64;
* ```
*/
-class Decimal64Type extends FloatingPointType {
+class Decimal64Type extends RealNumberType, DecimalFloatingPointType {
Decimal64Type() { builtintypes(underlyingElement(this), _, 41, _, _, _) }
override string getCanonicalQLClass() { result = "Decimal64Type" }
@@ -799,7 +963,7 @@ class Decimal64Type extends FloatingPointType {
* _Decimal128 d128;
* ```
*/
-class Decimal128Type extends FloatingPointType {
+class Decimal128Type extends RealNumberType, DecimalFloatingPointType {
Decimal128Type() { builtintypes(underlyingElement(this), _, 42, _, _, _) }
override string getCanonicalQLClass() { result = "Decimal128Type" }
@@ -833,6 +997,18 @@ class WideCharType extends IntegralType {
override string getCanonicalQLClass() { result = "WideCharType" }
}
+/**
+ * The C/C++ `char8_t` type. This is available starting with C++20.
+ * ```
+ * char8_t c8;
+ * ```
+ */
+class Char8Type extends IntegralType {
+ Char8Type() { builtintypes(underlyingElement(this), _, 51, _, _, _) }
+
+ override string getCanonicalQLClass() { result = "Char8Type" }
+}
+
/**
* The C/C++ `char16_t` type. This is available starting with C11 and C++11.
* ```
diff --git a/cpp/ql/src/semmle/code/cpp/UserType.qll b/cpp/ql/src/semmle/code/cpp/UserType.qll
index cbb7f39adbd..4484cde84de 100644
--- a/cpp/ql/src/semmle/code/cpp/UserType.qll
+++ b/cpp/ql/src/semmle/code/cpp/UserType.qll
@@ -38,7 +38,7 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @
override Specifier getASpecifier() { result = Type.super.getASpecifier() }
override Location getLocation() {
- if isDefined()
+ if hasDefinition()
then result = this.getDefinitionLocation()
else result = this.getADeclarationLocation()
}
diff --git a/cpp/ql/src/semmle/code/cpp/Variable.qll b/cpp/ql/src/semmle/code/cpp/Variable.qll
index a4bbbdbb9ab..54f39ab2bb6 100644
--- a/cpp/ql/src/semmle/code/cpp/Variable.qll
+++ b/cpp/ql/src/semmle/code/cpp/Variable.qll
@@ -126,10 +126,7 @@ class Variable extends Declaration, @variable {
or
exists(AssignExpr ae | ae.getLValue().(Access).getTarget() = this and result = ae.getRValue())
or
- exists(AggregateLiteral l |
- this.getDeclaringType() = l.getType() and
- result = l.getChild(this.(Field).getInitializationOrder())
- )
+ exists(ClassAggregateLiteral l | result = l.getFieldExpr(this))
}
/**
@@ -263,24 +260,33 @@ class ParameterDeclarationEntry extends VariableDeclarationEntry {
*/
int getIndex() { param_decl_bind(underlyingElement(this), result, _) }
+ private string getAnonymousParameterDescription() {
+ not exists(getName()) and
+ exists(string idx |
+ idx =
+ ((getIndex() + 1).toString() + "th")
+ .replaceAll("1th", "1st")
+ .replaceAll("2th", "2nd")
+ .replaceAll("3th", "3rd")
+ .replaceAll("11st", "11th")
+ .replaceAll("12nd", "12th")
+ .replaceAll("13rd", "13th") and
+ if exists(getCanonicalName())
+ then result = "declaration of " + getCanonicalName() + " as anonymous " + idx + " parameter"
+ else result = "declaration of " + idx + " parameter"
+ )
+ }
+
override string toString() {
- if exists(getName())
- then result = super.toString()
- else
- exists(string idx |
- idx =
- ((getIndex() + 1).toString() + "th")
- .replaceAll("1th", "1st")
- .replaceAll("2th", "2nd")
- .replaceAll("3th", "3rd")
- .replaceAll("11st", "11th")
- .replaceAll("12nd", "12th")
- .replaceAll("13rd", "13th")
- |
- if exists(getCanonicalName())
- then result = "declaration of " + getCanonicalName() + " as anonymous " + idx + " parameter"
- else result = "declaration of " + idx + " parameter"
- )
+ isDefinition() and
+ result = "definition of " + getName()
+ or
+ not isDefinition() and
+ if getName() = getCanonicalName()
+ then result = "declaration of " + getName()
+ else result = "declaration of " + getCanonicalName() + " as " + getName()
+ or
+ result = getAnonymousParameterDescription()
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/XML.qll b/cpp/ql/src/semmle/code/cpp/XML.qll
index dc7836aaabe..713903b63e6 100755
--- a/cpp/ql/src/semmle/code/cpp/XML.qll
+++ b/cpp/ql/src/semmle/code/cpp/XML.qll
@@ -116,7 +116,7 @@ class XMLFile extends XMLParent, File {
XMLFile() { xmlEncoding(this, _) }
/** Gets a printable representation of this XML file. */
- override string toString() { result = XMLParent.super.toString() }
+ override string toString() { result = getName() }
/** Gets the name of this XML file. */
override string getName() { result = File.super.getAbsolutePath() }
@@ -236,7 +236,7 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
string getAttributeValue(string name) { result = this.getAttribute(name).getValue() }
/** Gets a printable representation of this XML element. */
- override string toString() { result = XMLParent.super.toString() }
+ override string toString() { result = getName() }
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/commons/Assertions.qll b/cpp/ql/src/semmle/code/cpp/commons/Assertions.qll
index a4d3669d2ea..9d466387fff 100644
--- a/cpp/ql/src/semmle/code/cpp/commons/Assertions.qll
+++ b/cpp/ql/src/semmle/code/cpp/commons/Assertions.qll
@@ -12,12 +12,12 @@ abstract class Assertion extends Locatable {
}
/**
- * A libc assert, as defined in assert.h. A macro with the head
- * "assert(expr)" that expands to a conditional expression which
- * may terminate the program.
+ * A libc assert, as defined in assert.h. A macro with a head
+ * that matches the prefix "assert(", and expands to a conditional
+ * expression which may terminate the program.
*/
class LibcAssert extends MacroInvocation, Assertion {
- LibcAssert() { this.getMacro().getHead() = "assert(expr)" }
+ LibcAssert() { this.getMacro().getHead().matches("assert(%") }
override Expr getAsserted() {
exists(ConditionalExpr ce | this.getAGeneratedElement() = ce | result = ce.getCondition())
diff --git a/cpp/ql/src/semmle/code/cpp/commons/Buffer.qll b/cpp/ql/src/semmle/code/cpp/commons/Buffer.qll
index c884038a1c8..88e6a48ff55 100644
--- a/cpp/ql/src/semmle/code/cpp/commons/Buffer.qll
+++ b/cpp/ql/src/semmle/code/cpp/commons/Buffer.qll
@@ -92,13 +92,7 @@ int getBufferSize(Expr bufferExpr, Element why) {
// dataflow (all sources must be the same size)
bufferExprNode = DataFlow::exprNode(bufferExpr) and
result =
- min(Expr def |
- DataFlow::localFlowStep(DataFlow::exprNode(def), bufferExprNode)
- |
- getBufferSize(def, _)
- ) and
- result =
- max(Expr def |
+ unique(Expr def |
DataFlow::localFlowStep(DataFlow::exprNode(def), bufferExprNode)
|
getBufferSize(def, _)
diff --git a/cpp/ql/src/semmle/code/cpp/commons/unix/Constants.qll b/cpp/ql/src/semmle/code/cpp/commons/unix/Constants.qll
index 3478eb87e28..f9f854b1aab 100644
--- a/cpp/ql/src/semmle/code/cpp/commons/unix/Constants.qll
+++ b/cpp/ql/src/semmle/code/cpp/commons/unix/Constants.qll
@@ -4,6 +4,11 @@
import cpp
+/**
+ * Gets the number corresponding to the contents of `input` in base-8.
+ * Note: the first character of `input` must be `0`. For example:
+ * `parseOctal("012345") = 5349`.
+ */
bindingset[input]
int parseOctal(string input) {
input.charAt(0) = "0" and
@@ -15,44 +20,77 @@ int parseOctal(string input) {
)
}
+/** Gets the number corresponding to the "set-user-ID on execute bit" in Unix. */
int s_isuid() { result = parseOctal("04000") }
+/** Gets the number corresponding to the "set-group-ID on execute bit" in Unix. */
int s_isgid() { result = parseOctal("02000") }
+/** Gets the number corresponding to the sticky bit in Unix. */
int s_isvtx() { result = parseOctal("01000") }
+/** Gets the number corresponding to the read permission bit for owner of the file in Unix. */
int s_irusr() { result = parseOctal("0400") }
+/** Gets the number corresponding to the write permission bit for owner of the file in Unix. */
int s_iwusr() { result = parseOctal("0200") }
+/** Gets the number corresponding to the execute permission bit for owner of the file in Unix. */
int s_ixusr() { result = parseOctal("0100") }
+/** Gets the number corresponding to the permissions `S_IRUSR | S_IWUSR | S_IXUSR` in Unix. */
int s_irwxu() { result = s_irusr().bitOr(s_iwusr()).bitOr(s_ixusr()) }
+/**
+ * Gets the number corresponding to the read permission bit for the group
+ * owner of the file in Unix.
+ */
int s_irgrp() { result = s_irusr().bitShiftRight(3) }
+/**
+ * Gets the number corresponding to the write permission bit for the group
+ * owner of the file in Unix.
+ */
int s_iwgrp() { result = s_iwusr().bitShiftRight(3) }
+/**
+ * Gets the number corresponding to the execute permission bit for the group
+ * owner of the file in Unix.
+ */
int s_ixgrp() { result = s_ixusr().bitShiftRight(3) }
+/** Gets the number corresponding to the permissions `S_IRGRP | S_IWGRP | S_IXGRP` in Unix. */
int s_irwxg() { result = s_irwxu().bitShiftRight(3) }
+/** Gets the number corresponding to the read permission bit for other users in Unix. */
int s_iroth() { result = s_irgrp().bitShiftRight(3) }
+/** Gets the number corresponding to the write permission bit for other users in Unix. */
int s_iwoth() { result = s_iwgrp().bitShiftRight(3) }
+/** Gets the number corresponding to the execute-or-search permission bit for other users in Unix. */
int s_ixoth() { result = s_ixgrp().bitShiftRight(3) }
+/** Gets the number corresponding to the permissions `S_IROTH | S_IWOTH | S_IXOTH` in Unix. */
int s_irwxo() { result = s_irwxg().bitShiftRight(3) }
+/**
+ * Gets the number that can be used in a bitwise and with the file status flag
+ * to produce a number representing the file access mode.
+ */
int o_accmode() { result = parseOctal("0003") }
+/** Gets the number corresponding to the read-only file access mode. */
int o_rdonly() { result = parseOctal("00") }
+/** Gets the number corresponding to the write-only file access mode. */
int o_wronly() { result = parseOctal("01") }
+/** Gets the number corresponding to the read-and-write file access mode. */
int o_rdwr() { result = parseOctal("02") }
+/** Gets the number corresponding to the file creation flag O_CREAT on Linux. */
int o_creat() { result = parseOctal("0100") }
+/** Gets the number corresponding to the file creation flag O_EXCL on Linux. */
int o_excl() { result = parseOctal("0200") }
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/BasicBlocks.qll b/cpp/ql/src/semmle/code/cpp/controlflow/BasicBlocks.qll
index 681d0b710f6..16947019f54 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/BasicBlocks.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/BasicBlocks.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides a library for reasoning about control flow at the granularity of basic blocks.
+ * This is usually much more efficient than reasoning directly at the level of `ControlFlowNode`s.
+ */
+
import cpp
private import internal.PrimitiveBasicBlocks
private import internal.ConstantExprs
@@ -148,22 +153,37 @@ predicate bb_successor = bb_successor_cached/2;
class BasicBlock extends ControlFlowNodeBase {
BasicBlock() { basic_block_entry_node(this) }
+ /** Holds if this basic block contains `node`. */
predicate contains(ControlFlowNode node) { basic_block_member(node, this, _) }
+ /** Gets the `ControlFlowNode` at position `pos` in this basic block. */
ControlFlowNode getNode(int pos) { basic_block_member(result, this, pos) }
+ /** Gets a `ControlFlowNode` in this basic block. */
ControlFlowNode getANode() { basic_block_member(result, this, _) }
+ /** Gets a `BasicBlock` that is a direct successor of this basic block. */
BasicBlock getASuccessor() { bb_successor(this, result) }
+ /** Gets a `BasicBlock` that is a direct predecessor of this basic block. */
BasicBlock getAPredecessor() { bb_successor(result, this) }
+ /**
+ * Gets a `BasicBlock` such that the control-flow edge `(this, result)` may be taken
+ * when the outgoing edge of this basic block is an expression that is true.
+ */
BasicBlock getATrueSuccessor() { result.getStart() = this.getEnd().getATrueSuccessor() }
+ /**
+ * Gets a `BasicBlock` such that the control-flow edge `(this, result)` may be taken
+ * when the outgoing edge of this basic block is an expression that is false.
+ */
BasicBlock getAFalseSuccessor() { result.getStart() = this.getEnd().getAFalseSuccessor() }
+ /** Gets the final `ControlFlowNode` of this basic block. */
ControlFlowNode getEnd() { basic_block_member(result, this, bb_length(this) - 1) }
+ /** Gets the first `ControlFlowNode` of this basic block. */
ControlFlowNode getStart() { result = this }
/** Gets the number of `ControlFlowNode`s in this basic block. */
@@ -192,6 +212,7 @@ class BasicBlock extends ControlFlowNodeBase {
this.getEnd().getLocation().hasLocationInfo(endf, _, _, endl, endc)
}
+ /** Gets the function containing this basic block. */
Function getEnclosingFunction() { result = this.getStart().getControlFlowScope() }
/**
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll b/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll
index 9174f474a8f..1f79d6c5bc2 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides a library for reasoning about control flow at the granularity of
+ * individual nodes in the control-flow graph.
+ */
+
import cpp
import BasicBlocks
private import semmle.code.cpp.controlflow.internal.ConstantExprs
@@ -29,8 +34,10 @@ private import semmle.code.cpp.controlflow.internal.CFG
* `Handler`. There are no edges from function calls to `Handler`s.
*/
class ControlFlowNode extends Locatable, ControlFlowNodeBase {
+ /** Gets a direct successor of this control-flow node, if any. */
ControlFlowNode getASuccessor() { successors_adapted(this, result) }
+ /** Gets a direct predecessor of this control-flow node, if any. */
ControlFlowNode getAPredecessor() { this = result.getASuccessor() }
/** Gets the function containing this control-flow node. */
@@ -71,6 +78,7 @@ class ControlFlowNode extends Locatable, ControlFlowNodeBase {
result = getASuccessor()
}
+ /** Gets the `BasicBlock` containing this control-flow node. */
BasicBlock getBasicBlock() { result.getANode() = this }
}
@@ -86,10 +94,18 @@ import ControlFlowGraphPublic
*/
class ControlFlowNodeBase extends ElementBase, @cfgnode { }
+/**
+ * Holds when `n2` is a control-flow node such that the control-flow
+ * edge `(n1, n2)` may be taken when `n1` is an expression that is true.
+ */
predicate truecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
qlCFGTrueSuccessor(n1, n2)
}
+/**
+ * Holds when `n2` is a control-flow node such that the control-flow
+ * edge `(n1, n2)` may be taken when `n1` is an expression that is false.
+ */
predicate falsecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
qlCFGFalseSuccessor(n1, n2)
}
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/Dataflow.qll b/cpp/ql/src/semmle/code/cpp/controlflow/Dataflow.qll
index 99fb6966099..1a5d81ee9f3 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/Dataflow.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/Dataflow.qll
@@ -15,14 +15,25 @@ import Dereferenced
abstract class DataflowAnnotation extends string {
DataflowAnnotation() { this = "pointer-null" or this = "pointer-valid" }
+ /** Holds if this annotation is the default annotation. */
abstract predicate isDefault();
+ /** Holds if this annotation is generated when analyzing expression `e`. */
abstract predicate generatedOn(Expr e);
+ /**
+ * Holds if this annotation is generated for the variable `v` when
+ * the control-flow edge `(src, dest)` is taken.
+ */
abstract predicate generatedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest);
+ /**
+ * Holds if this annotation is removed for the variable `v` when
+ * the control-flow edge `(src, dest)` is taken.
+ */
abstract predicate killedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest);
+ /** Holds if expression `e` is given this annotation. */
predicate marks(Expr e) {
this.generatedOn(e) and reachable(e)
or
@@ -31,6 +42,7 @@ abstract class DataflowAnnotation extends string {
exists(LocalScopeVariable v | this.marks(v, e) and e = v.getAnAccess())
}
+ /** Holds if the variable `v` accessed in control-flow node `n` is given this annotation. */
predicate marks(LocalScopeVariable v, ControlFlowNode n) {
v.getAnAccess().getEnclosingFunction().getBlock() = n and
this.isDefault()
@@ -57,6 +69,10 @@ abstract class DataflowAnnotation extends string {
)
}
+ /**
+ * Holds if the variable `v` preserves this annotation when the control-flow
+ * edge `(src, dest)` is taken.
+ */
predicate preservedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest) {
this.marks(v, src) and
src.getASuccessor() = dest and
@@ -64,6 +80,10 @@ abstract class DataflowAnnotation extends string {
not v.getAnAssignment() = src
}
+ /**
+ * Holds if the variable `v` is assigned this annotation when `src` is an assignment
+ * expression that assigns to `v` and the control-flow edge `(src, dest)` is taken.
+ */
predicate assignedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest) {
this.marks(src.(AssignExpr).getRValue()) and
src = v.getAnAssignment() and
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/DefinitionsAndUses.qll b/cpp/ql/src/semmle/code/cpp/controlflow/DefinitionsAndUses.qll
index e7f304af382..f6eb0a8a645 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/DefinitionsAndUses.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/DefinitionsAndUses.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes and predicates for reasoning about definitions and uses of variables.
+ */
+
import cpp
private import semmle.code.cpp.controlflow.StackVariableReachability
private import semmle.code.cpp.dataflow.EscapesTree
@@ -135,6 +139,7 @@ library class DefOrUse extends ControlFlowNodeBase {
}
}
+/** A definition of a stack variable. */
library class Def extends DefOrUse {
Def() { definition(_, this) }
@@ -149,6 +154,7 @@ private predicate parameterIsOverwritten(Function f, Parameter p) {
definitionBarrier(p, _)
}
+/** A definition of a parameter. */
library class ParameterDef extends DefOrUse {
ParameterDef() {
// Optimization: parameters that are not overwritten do not require
@@ -162,6 +168,7 @@ library class ParameterDef extends DefOrUse {
}
}
+/** A use of a stack variable. */
library class Use extends DefOrUse {
Use() { useOfVar(_, this) }
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/Dereferenced.qll b/cpp/ql/src/semmle/code/cpp/controlflow/Dereferenced.qll
index 69c5963af30..a667c39943f 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/Dereferenced.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/Dereferenced.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides predicates for detecting whether an expression dereferences a pointer.
+ */
+
import cpp
import Nullness
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/Guards.qll b/cpp/ql/src/semmle/code/cpp/controlflow/Guards.qll
index 007c1f2ecfc..e7809358bce 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/Guards.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/Guards.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides classes and predicates for reasoning about guards and the control
+ * flow elements controlled by those guards.
+ */
+
import cpp
import semmle.code.cpp.controlflow.BasicBlocks
import semmle.code.cpp.controlflow.SSA
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll b/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll
index 1f7b3b09946..656496325af 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides classes and predicates for reasoning about guards and the control
+ * flow elements controlled by those guards.
+ */
+
import cpp
import semmle.code.cpp.ir.IR
@@ -8,6 +13,7 @@ import semmle.code.cpp.ir.IR
* has the AST for the `Function` itself, which tends to confuse mapping between the AST `BasicBlock`
* and the `IRBlock`.
*/
+pragma[noinline]
private predicate isUnreachedBlock(IRBlock block) {
block.getFirstInstruction() instanceof UnreachedInstruction
}
@@ -32,7 +38,7 @@ class GuardCondition extends Expr {
}
/**
- * Holds if this condition controls `block`, meaning that `block` is only
+ * Holds if this condition controls `controlled`, meaning that `controlled` is only
* entered if the value of this condition is `testIsTrue`.
*
* Illustration:
@@ -253,7 +259,7 @@ class IRGuardCondition extends Instruction {
IRGuardCondition() { branch = get_branch_for_condition(this) }
/**
- * Holds if this condition controls `block`, meaning that `block` is only
+ * Holds if this condition controls `controlled`, meaning that `controlled` is only
* entered if the value of this condition is `testIsTrue`.
*
* Illustration:
@@ -290,18 +296,22 @@ class IRGuardCondition extends Instruction {
)
}
+ /**
+ * Holds if the control-flow edge `(pred, succ)` may be taken only if
+ * the value of this condition is `testIsTrue`.
+ */
cached
predicate controlsEdge(IRBlock pred, IRBlock succ, boolean testIsTrue) {
pred.getASuccessor() = succ and
controls(pred, testIsTrue)
or
- hasBranchEdge(succ, testIsTrue) and
+ succ = getBranchSuccessor(testIsTrue) and
branch.getCondition() = this and
branch.getBlock() = pred
}
/**
- * Holds if `branch` jumps directly to `succ` when this condition is `testIsTrue`.
+ * Gets the block to which `branch` jumps directly when this condition is `testIsTrue`.
*
* This predicate is intended to help with situations in which an inference can only be made
* based on an edge between a block with multiple successors and a block with multiple
@@ -315,14 +325,14 @@ class IRGuardCondition extends Instruction {
* return x;
* ```
*/
- private predicate hasBranchEdge(IRBlock succ, boolean testIsTrue) {
+ private IRBlock getBranchSuccessor(boolean testIsTrue) {
branch.getCondition() = this and
(
testIsTrue = true and
- succ.getFirstInstruction() = branch.getTrueSuccessor()
+ result.getFirstInstruction() = branch.getTrueSuccessor()
or
testIsTrue = false and
- succ.getFirstInstruction() = branch.getFalseSuccessor()
+ result.getFirstInstruction() = branch.getFalseSuccessor()
)
}
@@ -396,20 +406,78 @@ class IRGuardCondition extends Instruction {
*/
private predicate controlsBlock(IRBlock controlled, boolean testIsTrue) {
not isUnreachedBlock(controlled) and
- exists(IRBlock branchBlock | branchBlock.getAnInstruction() = branch |
- exists(IRBlock succ |
- testIsTrue = true and succ.getFirstInstruction() = branch.getTrueSuccessor()
+ //
+ // For this block to control the block `controlled` with `testIsTrue` the
+ // following must hold: Execution must have passed through the test; that
+ // is, `this` must strictly dominate `controlled`. Execution must have
+ // passed through the `testIsTrue` edge leaving `this`.
+ //
+ // Although "passed through the true edge" implies that
+ // `getBranchSuccessor(true)` dominates `controlled`, the reverse is not
+ // true, as flow may have passed through another edge to get to
+ // `getBranchSuccessor(true)`, so we need to assert that
+ // `getBranchSuccessor(true)` dominates `controlled` *and* that all
+ // predecessors of `getBranchSuccessor(true)` are either `this` or
+ // dominated by `getBranchSuccessor(true)`.
+ //
+ // For example, in the following snippet:
+ //
+ // if (x)
+ // controlled;
+ // false_successor;
+ // uncontrolled;
+ //
+ // `false_successor` dominates `uncontrolled`, but not all of its
+ // predecessors are `this` (`if (x)`) or dominated by itself. Whereas in
+ // the following code:
+ //
+ // if (x)
+ // while (controlled)
+ // also_controlled;
+ // false_successor;
+ // uncontrolled;
+ //
+ // the block `while (controlled)` is controlled because all of its
+ // predecessors are `this` (`if (x)`) or (in the case of `also_controlled`)
+ // dominated by itself.
+ //
+ // The additional constraint on the predecessors of the test successor implies
+ // that `this` strictly dominates `controlled` so that isn't necessary to check
+ // directly.
+ exists(IRBlock succ |
+ succ = this.getBranchSuccessor(testIsTrue) and
+ this.hasDominatingEdgeTo(succ) and
+ succ.dominates(controlled)
+ )
+ }
+
+ /**
+ * Holds if `(this, succ)` is an edge that dominates `succ`, that is, all other
+ * predecessors of `succ` are dominated by `succ`. This implies that `this` is the
+ * immediate dominator of `succ`.
+ *
+ * This is a necessary and sufficient condition for an edge to dominate anything,
+ * and in particular `bb1.hasDominatingEdgeTo(bb2) and bb2.dominates(bb3)` means
+ * that the edge `(bb1, bb2)` dominates `bb3`.
+ */
+ private predicate hasDominatingEdgeTo(IRBlock succ) {
+ exists(IRBlock branchBlock | branchBlock = this.getBranchBlock() |
+ branchBlock.immediatelyDominates(succ) and
+ branchBlock.getASuccessor() = succ and
+ forall(IRBlock pred | pred = succ.getAPredecessor() and pred != branchBlock |
+ succ.dominates(pred)
or
- testIsTrue = false and succ.getFirstInstruction() = branch.getFalseSuccessor()
- |
- branch.getCondition() = this and
- succ.dominates(controlled) and
- forall(IRBlock pred | pred.getASuccessor() = succ |
- pred = branchBlock or succ.dominates(pred) or not pred.isReachableFromFunctionEntry()
- )
+ // An unreachable `pred` is vacuously dominated by `succ` since all
+ // paths from the entry to `pred` go through `succ`. Such vacuous
+ // dominance is not included in the `dominates` predicate since that
+ // could cause quadratic blow-up.
+ not pred.isReachableFromFunctionEntry()
)
)
}
+
+ pragma[noinline]
+ private IRBlock getBranchBlock() { result = branch.getBlock() }
}
private ConditionalBranchInstruction get_branch_for_condition(Instruction guard) {
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/Nullness.qll b/cpp/ql/src/semmle/code/cpp/controlflow/Nullness.qll
index caaa5b54e8c..69d2166a1d2 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/Nullness.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/Nullness.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes and predicates for working with null values and checks for nullness.
+ */
+
import cpp
import DefinitionsAndUses
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/SSA.qll b/cpp/ql/src/semmle/code/cpp/controlflow/SSA.qll
index 4e2e018eda3..5c0f6b3ac14 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/SSA.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/SSA.qll
@@ -1,7 +1,15 @@
+/**
+ * Provides classes and predicates for SSA representation (Static Single Assignment form).
+ */
+
import cpp
import semmle.code.cpp.controlflow.Dominance
import SSAUtils
+/**
+ * The SSA logic comes in two versions: the standard SSA and range-analysis RangeSSA.
+ * This class provides the standard SSA logic.
+ */
library class StandardSSA extends SSAHelper {
StandardSSA() { this = 0 }
}
@@ -50,11 +58,13 @@ class SsaDefinition extends ControlFlowNodeBase {
*/
ControlFlowNode getDefinition() { result = this }
+ /** Gets the `BasicBlock` containing this definition. */
BasicBlock getBasicBlock() { result.contains(getDefinition()) }
/** Holds if this definition is a phi node for variable `v`. */
predicate isPhiNode(StackVariable v) { exists(StandardSSA x | x.phi_node(v, this.(BasicBlock))) }
+ /** Gets the location of this definition. */
Location getLocation() { result = this.(ControlFlowNode).getLocation() }
/** Holds if the SSA variable `(this, p)` is defined by parameter `p`. */
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/SSAUtils.qll b/cpp/ql/src/semmle/code/cpp/controlflow/SSAUtils.qll
index b9b34a1d68c..3ae1ed11e6d 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/SSAUtils.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/SSAUtils.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes and predicates for use in the SSA library.
+ */
+
import cpp
import semmle.code.cpp.controlflow.Dominance
import semmle.code.cpp.controlflow.SSA // must be imported for proper caching of SSAHelper
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/StackVariableReachability.qll b/cpp/ql/src/semmle/code/cpp/controlflow/StackVariableReachability.qll
index 696184620e3..6c50d254faa 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/StackVariableReachability.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/StackVariableReachability.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides a library for working with local (intra-procedural) control-flow
+ * reachability involving stack variables.
+ */
+
import cpp
/**
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/SubBasicBlocks.qll b/cpp/ql/src/semmle/code/cpp/controlflow/SubBasicBlocks.qll
index dafcd1fdd97..fa9d2e94081 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/SubBasicBlocks.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/SubBasicBlocks.qll
@@ -168,6 +168,7 @@ class SubBasicBlock extends ControlFlowNodeBase {
/** Gets the first control-flow node in this `SubBasicBlock`. */
ControlFlowNode getStart() { result = this }
+ /** Gets the function that contains this `SubBasicBlock`. */
pragma[noinline]
Function getEnclosingFunction() { result = this.getStart().getControlFlowScope() }
}
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll b/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll
index f0af3d219b5..c473f156088 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll
@@ -532,13 +532,7 @@ library class ExprEvaluator extends int {
interestingVariableAccess(e, va, v, true) and
// All assignments must have the same int value
result =
- min(Expr value |
- value = v.getAnAssignedValue() and not ignoreVariableAssignment(e, v, value)
- |
- getValueInternalNonSubExpr(value)
- ) and
- result =
- max(Expr value |
+ unique(Expr value |
value = v.getAnAssignedValue() and not ignoreVariableAssignment(e, v, value)
|
getValueInternalNonSubExpr(value)
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/EscapesTree.qll b/cpp/ql/src/semmle/code/cpp/dataflow/EscapesTree.qll
index 8a99a71f6af..eaa1581078b 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/EscapesTree.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/EscapesTree.qll
@@ -4,6 +4,14 @@
* passed to a function, or similar.
*/
+/*
+ * Maintainer note: this file is one of several files that are similar but not
+ * identical. Many changes to this file will also apply to the others:
+ * - AddressConstantExpression.qll
+ * - AddressFlow.qll
+ * - EscapesTree.qll
+ */
+
private import cpp
/**
@@ -11,15 +19,13 @@ private import cpp
* template functions, these functions are essentially casts, so we treat them
* as such.
*/
-private predicate stdIdentityFunction(Function f) {
- f.getNamespace().getParentNamespace() instanceof GlobalNamespace and
- f.getNamespace().getName() = "std" and
- (
- f.getName() = "move"
- or
- f.getName() = "forward"
- )
-}
+private predicate stdIdentityFunction(Function f) { f.hasQualifiedName("std", ["move", "forward"]) }
+
+/**
+ * Holds if `f` is an instantiation of `std::addressof`, which effectively
+ * converts a reference to a pointer.
+ */
+private predicate stdAddressOf(Function f) { f.hasQualifiedName("std", "addressof") }
private predicate lvalueToLvalueStepPure(Expr lvalueIn, Expr lvalueOut) {
lvalueIn = lvalueOut.(DotFieldAccess).getQualifier().getFullyConverted()
@@ -91,12 +97,17 @@ private predicate lvalueToReferenceStep(Expr lvalueIn, Expr referenceOut) {
}
private predicate referenceToLvalueStep(Expr referenceIn, Expr lvalueOut) {
- // This probably cannot happen. It would require an expression to be
- // converted to a reference and back again without an intermediate variable
- // assignment.
referenceIn.getConversion() = lvalueOut.(ReferenceDereferenceExpr)
}
+private predicate referenceToPointerStep(Expr referenceIn, Expr pointerOut) {
+ pointerOut =
+ any(FunctionCall call |
+ stdAddressOf(call.getTarget()) and
+ referenceIn = call.getArgument(0).getFullyConverted()
+ )
+}
+
private predicate referenceToReferenceStep(Expr referenceIn, Expr referenceOut) {
referenceOut =
any(FunctionCall call |
@@ -145,6 +156,12 @@ private predicate pointerFromVariableAccess(VariableAccess va, Expr pointer) {
pointerToPointerStep(prev, pointer)
)
or
+ // reference -> pointer
+ exists(Expr prev |
+ referenceFromVariableAccess(va, prev) and
+ referenceToPointerStep(prev, pointer)
+ )
+ or
// lvalue -> pointer
exists(Expr prev |
lvalueFromVariableAccess(va, prev) and
@@ -166,10 +183,14 @@ private predicate referenceFromVariableAccess(VariableAccess va, Expr reference)
)
}
-private predicate valueMayEscapeAt(Expr e) {
+private predicate addressMayEscapeAt(Expr e) {
exists(Call call |
e = call.getAnArgument().getFullyConverted() and
- not stdIdentityFunction(call.getTarget())
+ not stdIdentityFunction(call.getTarget()) and
+ not stdAddressOf(call.getTarget())
+ or
+ e = call.getQualifier().getFullyConverted() and
+ e.getUnderlyingType() instanceof PointerType
)
or
exists(AssignExpr assign | e = assign.getRValue().getFullyConverted())
@@ -187,8 +208,8 @@ private predicate valueMayEscapeAt(Expr e) {
exists(AsmStmt asm | e = asm.getAChild().(Expr).getFullyConverted())
}
-private predicate valueMayEscapeMutablyAt(Expr e) {
- valueMayEscapeAt(e) and
+private predicate addressMayEscapeMutablyAt(Expr e) {
+ addressMayEscapeAt(e) and
exists(Type t | t = e.getType().getUnderlyingType() |
exists(PointerType pt |
pt = t
@@ -207,6 +228,22 @@ private predicate valueMayEscapeMutablyAt(Expr e) {
)
}
+private predicate lvalueMayEscapeAt(Expr e) {
+ // A call qualifier, like `q` in `q.f()`, is special in that the address of
+ // `q` escapes even though `q` is not a pointer or a reference.
+ exists(Call call |
+ e = call.getQualifier().getFullyConverted() and
+ e.getType().getUnspecifiedType() instanceof Class
+ )
+}
+
+private predicate lvalueMayEscapeMutablyAt(Expr e) {
+ lvalueMayEscapeAt(e) and
+ // A qualifier of a call to a const member function is converted to a const
+ // class type.
+ not e.getType().isConst()
+}
+
private predicate addressFromVariableAccess(VariableAccess va, Expr e) {
pointerFromVariableAccess(va, e)
or
@@ -253,8 +290,11 @@ private module EscapesTree_Cached {
*/
cached
predicate variableAddressEscapesTree(VariableAccess va, Expr e) {
- valueMayEscapeAt(e) and
+ addressMayEscapeAt(e) and
addressFromVariableAccess(va, e)
+ or
+ lvalueMayEscapeAt(e) and
+ lvalueFromVariableAccess(va, e)
}
/**
@@ -283,8 +323,11 @@ private module EscapesTree_Cached {
*/
cached
predicate variableAddressEscapesTreeNonConst(VariableAccess va, Expr e) {
- valueMayEscapeMutablyAt(e) and
+ addressMayEscapeMutablyAt(e) and
addressFromVariableAccess(va, e)
+ or
+ lvalueMayEscapeMutablyAt(e) and
+ lvalueFromVariableAccess(va, e)
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/RecursionPrevention.qll b/cpp/ql/src/semmle/code/cpp/dataflow/RecursionPrevention.qll
index 626e50925f9..2d8b52f8622 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/RecursionPrevention.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/RecursionPrevention.qll
@@ -1,6 +1,6 @@
/**
* DEPRECATED: Recursion through `DataFlow::Configuration` is impossible in
- * Semmle Core 1.17 and above. There is no need for this module because it's
+ * any supported tooling. There is no need for this module because it's
* impossible to accidentally depend on recursion through
* `DataFlow::Configuration` in current releases.
*
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/AddressFlow.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/AddressFlow.qll
new file mode 100644
index 00000000000..b3f8cd02828
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/AddressFlow.qll
@@ -0,0 +1,247 @@
+/**
+ * Provides a local analysis for identifying where a variable address
+ * is effectively taken. Array-like offsets are allowed to pass through but
+ * not field-like offsets.
+ *
+ * This library is specialized to meet the needs of `FlowVar.qll`.
+ */
+
+/*
+ * Maintainer note: this file is one of several files that are similar but not
+ * identical. Many changes to this file will also apply to the others:
+ * - AddressConstantExpression.qll
+ * - AddressFlow.qll
+ * - EscapesTree.qll
+ */
+
+private import cpp
+
+/**
+ * Holds if `f` is an instantiation of the `std::move` or `std::forward`
+ * template functions, these functions are essentially casts, so we treat them
+ * as such.
+ */
+private predicate stdIdentityFunction(Function f) { f.hasQualifiedName("std", ["move", "forward"]) }
+
+/**
+ * Holds if `f` is an instantiation of `std::addressof`, which effectively
+ * converts a reference to a pointer.
+ */
+private predicate stdAddressOf(Function f) { f.hasQualifiedName("std", "addressof") }
+
+private predicate lvalueToLvalueStep(Expr lvalueIn, Expr lvalueOut) {
+ lvalueIn.getConversion() = lvalueOut.(ParenthesisExpr)
+ or
+ // When an object is implicitly converted to a reference to one of its base
+ // classes, it gets two `Conversion`s: there is first an implicit
+ // `CStyleCast` to its base class followed by a `ReferenceToExpr` to a
+ // reference to its base class. Whereas an explicit cast to the base class
+ // would produce an rvalue, which would not be convertible to an lvalue
+ // reference, this implicit cast instead produces an lvalue. The following
+ // case ensures that we propagate the property of being an lvalue through
+ // such casts.
+ lvalueIn.getConversion() = lvalueOut and
+ lvalueOut.(CStyleCast).isImplicit()
+ or
+ // C++ only
+ lvalueIn = lvalueOut.(PrefixCrementOperation).getOperand().getFullyConverted()
+ or
+ // C++ only
+ lvalueIn = lvalueOut.(Assignment).getLValue().getFullyConverted()
+}
+
+private predicate pointerToLvalueStep(Expr pointerIn, Expr lvalueOut) {
+ pointerIn = lvalueOut.(ArrayExpr).getArrayBase().getFullyConverted()
+ or
+ pointerIn = lvalueOut.(PointerDereferenceExpr).getOperand().getFullyConverted()
+}
+
+private predicate lvalueToPointerStep(Expr lvalueIn, Expr pointerOut) {
+ lvalueIn.getConversion() = pointerOut.(ArrayToPointerConversion)
+ or
+ lvalueIn = pointerOut.(AddressOfExpr).getOperand().getFullyConverted()
+}
+
+private predicate pointerToPointerStep(Expr pointerIn, Expr pointerOut) {
+ (
+ pointerOut instanceof PointerAddExpr
+ or
+ pointerOut instanceof PointerSubExpr
+ ) and
+ pointerIn = pointerOut.getAChild().getFullyConverted() and
+ pointerIn.getUnspecifiedType() instanceof PointerType
+ or
+ pointerIn = pointerOut.(UnaryPlusExpr).getOperand().getFullyConverted()
+ or
+ pointerIn.getConversion() = pointerOut.(Cast)
+ or
+ pointerIn.getConversion() = pointerOut.(ParenthesisExpr)
+ or
+ pointerIn = pointerOut.(ConditionalExpr).getThen().getFullyConverted()
+ or
+ pointerIn = pointerOut.(ConditionalExpr).getElse().getFullyConverted()
+ or
+ pointerIn = pointerOut.(CommaExpr).getRightOperand().getFullyConverted()
+ or
+ pointerIn = pointerOut.(StmtExpr).getResultExpr().getFullyConverted()
+}
+
+private predicate lvalueToReferenceStep(Expr lvalueIn, Expr referenceOut) {
+ lvalueIn.getConversion() = referenceOut.(ReferenceToExpr)
+}
+
+private predicate referenceToLvalueStep(Expr referenceIn, Expr lvalueOut) {
+ referenceIn.getConversion() = lvalueOut.(ReferenceDereferenceExpr)
+}
+
+private predicate referenceToPointerStep(Expr referenceIn, Expr pointerOut) {
+ pointerOut =
+ any(FunctionCall call |
+ stdAddressOf(call.getTarget()) and
+ referenceIn = call.getArgument(0).getFullyConverted()
+ )
+}
+
+private predicate referenceToReferenceStep(Expr referenceIn, Expr referenceOut) {
+ referenceOut =
+ any(FunctionCall call |
+ stdIdentityFunction(call.getTarget()) and
+ referenceIn = call.getArgument(0).getFullyConverted()
+ )
+ or
+ referenceIn.getConversion() = referenceOut.(Cast)
+ or
+ referenceIn.getConversion() = referenceOut.(ParenthesisExpr)
+}
+
+private predicate assignmentTo(Expr updated, ControlFlowNode node) {
+ updated = node.(Assignment).getLValue().getFullyConverted()
+ or
+ updated = node.(CrementOperation).getOperand().getFullyConverted()
+}
+
+private predicate lvalueToUpdate(Expr lvalue, Expr outer, ControlFlowNode node) {
+ (
+ exists(Call call | node = call |
+ outer = call.getQualifier().getFullyConverted() and
+ outer.getUnspecifiedType() instanceof Class and
+ not call.getTarget().hasSpecifier("const")
+ )
+ or
+ assignmentTo(outer, node)
+ or
+ exists(DotFieldAccess fa |
+ // `fa.otherField = ...` or `f(&fa)` or similar
+ outer = fa.getQualifier().getFullyConverted() and
+ valueToUpdate(fa, _, node)
+ )
+ ) and
+ lvalue = outer
+ or
+ exists(Expr lvalueMid |
+ lvalueToLvalueStep(lvalue, lvalueMid) and
+ lvalueToUpdate(lvalueMid, outer, node)
+ )
+ or
+ exists(Expr pointerMid |
+ lvalueToPointerStep(lvalue, pointerMid) and
+ pointerToUpdate(pointerMid, outer, node)
+ )
+ or
+ exists(Expr referenceMid |
+ lvalueToReferenceStep(lvalue, referenceMid) and
+ referenceToUpdate(referenceMid, outer, node)
+ )
+}
+
+private predicate pointerToUpdate(Expr pointer, Expr outer, ControlFlowNode node) {
+ (
+ exists(Call call | node = call |
+ outer = call.getAnArgument().getFullyConverted() and
+ exists(PointerType pt | pt = outer.getType().stripTopLevelSpecifiers() |
+ not pt.getBaseType().isConst()
+ )
+ or
+ outer = call.getQualifier().getFullyConverted() and
+ outer.getUnspecifiedType() instanceof PointerType and
+ not call.getTarget().hasSpecifier("const")
+ )
+ or
+ exists(PointerFieldAccess fa |
+ // `fa.otherField = ...` or `f(&fa)` or similar
+ outer = fa.getQualifier().getFullyConverted() and
+ valueToUpdate(fa, _, node)
+ )
+ ) and
+ pointer = outer
+ or
+ exists(Expr lvalueMid |
+ pointerToLvalueStep(pointer, lvalueMid) and
+ lvalueToUpdate(lvalueMid, outer, node)
+ )
+ or
+ exists(Expr pointerMid |
+ pointerToPointerStep(pointer, pointerMid) and
+ pointerToUpdate(pointerMid, outer, node)
+ )
+}
+
+private predicate referenceToUpdate(Expr reference, Expr outer, ControlFlowNode node) {
+ exists(Call call |
+ node = call and
+ outer = call.getAnArgument().getFullyConverted() and
+ not stdIdentityFunction(call.getTarget()) and
+ not stdAddressOf(call.getTarget()) and
+ exists(ReferenceType rt | rt = outer.getType().stripTopLevelSpecifiers() |
+ not rt.getBaseType().isConst()
+ )
+ ) and
+ reference = outer
+ or
+ exists(Expr lvalueMid |
+ referenceToLvalueStep(reference, lvalueMid) and
+ lvalueToUpdate(lvalueMid, outer, node)
+ )
+ or
+ exists(Expr pointerMid |
+ referenceToPointerStep(reference, pointerMid) and
+ pointerToUpdate(pointerMid, outer, node)
+ )
+ or
+ exists(Expr referenceMid |
+ referenceToReferenceStep(reference, referenceMid) and
+ referenceToUpdate(referenceMid, outer, node)
+ )
+}
+
+/**
+ * Holds if `node` is a control-flow node that may modify `inner` (or what it
+ * points to) through `outer`. The two expressions may be `Conversion`s. Plain
+ * assignments to variables are not included in this predicate since they are
+ * assumed to be analyzed by SSA or similar means.
+ *
+ * For example, in `f(& (*a).x)`, there are two results:
+ * - `inner` = `... .x`, `outer` = `&...`, `node` = `f(...)`.
+ * - `inner` = `a`, `outer` = `(...)`, `node` = `f(...)`.
+ */
+cached
+predicate valueToUpdate(Expr inner, Expr outer, ControlFlowNode node) {
+ (
+ lvalueToUpdate(inner, outer, node)
+ or
+ pointerToUpdate(inner, outer, node)
+ or
+ referenceToUpdate(inner, outer, node)
+ ) and
+ (
+ inner instanceof VariableAccess and
+ // Don't track non-field assignments
+ (assignmentTo(outer, _) implies inner instanceof FieldAccess)
+ or
+ inner instanceof ThisExpr
+ or
+ inner instanceof Call
+ // `inner` could also be `*` or `ReferenceDereferenceExpr`, but we
+ // can't do anything useful with those at the moment.
+ )
+}
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 a1daeb66411..1aeedf717f7 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
*/
predicate isBarrier(Node node) { none() }
- /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */
- deprecated predicate isBarrierEdge(Node node1, Node node2) { none() }
-
/** Holds if data flow into `node` is prohibited. */
predicate isBarrierIn(Node node) { none() }
@@ -251,15 +248,11 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
-pragma[noinline]
-private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
- viableCallable(call) = result.getCallable() and
- kind = result.getKind()
-}
-
/**
- * Holds if `node` is reachable from a source in the given configuration
- * taking simple call contexts into consideration.
+ * Holds if `node` is reachable from a source in the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call.
*/
private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) {
not fullBarrier(node, config) and
@@ -293,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
exists(Node mid |
useFieldFlow(config) and
nodeCandFwd1(mid, fromArg, config) and
- storeDirect(mid, _, node) and
+ store(mid, _, node, _) and
not outBarrier(mid, config)
)
or
// read
- exists(Content f |
- nodeCandFwd1Read(f, node, fromArg, config) and
- storeCandFwd1(f, config) and
+ exists(Content c |
+ nodeCandFwd1Read(c, node, fromArg, config) and
+ nodeCandFwd1IsStored(c, config) and
not inBarrier(node, config)
)
or
@@ -317,13 +310,35 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
fromArg = false
or
nodeCandFwd1OutFromArg(call, node, config) and
- flowOutCandFwd1(call, fromArg, config)
+ nodeCandFwd1IsEntered(call, fromArg, config)
)
)
}
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
+pragma[nomagic]
+private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
+ exists(Node mid |
+ nodeCandFwd1(mid, fromArg, config) and
+ read(mid, c, node)
+ )
+}
+
+/**
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
+ exists(Node mid, Node node, TypedContent tc |
+ not fullBarrier(node, config) and
+ useFieldFlow(config) and
+ nodeCandFwd1(mid, config) and
+ store(mid, tc, node, _) and
+ c = tc.getContent()
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1ReturnPosition(
ReturnPosition pos, boolean fromArg, Configuration config
@@ -335,43 +350,10 @@ private predicate nodeCandFwd1ReturnPosition(
}
pragma[nomagic]
-private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
- exists(Node mid |
- nodeCandFwd1(mid, fromArg, config) and
- readDirect(mid, f, node)
- )
-}
-
-/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
- */
-pragma[nomagic]
-private predicate storeCandFwd1(Content f, Configuration config) {
- exists(Node mid, Node node |
- not fullBarrier(node, config) and
- useFieldFlow(config) and
- nodeCandFwd1(mid, config) and
- storeDirect(mid, f, node)
- )
-}
-
-pragma[nomagic]
-private predicate nodeCandFwd1ReturnKind(
- DataFlowCall call, ReturnKindExt kind, boolean fromArg, Configuration config
-) {
+private predicate nodeCandFwd1Out(DataFlowCall call, Node out, boolean fromArg, Configuration config) {
exists(ReturnPosition pos |
nodeCandFwd1ReturnPosition(pos, fromArg, config) and
- pos = viableReturnPos(call, kind)
- )
-}
-
-pragma[nomagic]
-private predicate nodeCandFwd1Out(
- DataFlowCall call, Node node, boolean fromArg, Configuration config
-) {
- exists(ReturnKindExt kind |
- nodeCandFwd1ReturnKind(call, kind, fromArg, config) and
- node = kind.getAnOutNode(call)
+ viableReturnPosOut(call, pos, out)
)
}
@@ -384,7 +366,7 @@ private predicate nodeCandFwd1OutFromArg(DataFlowCall call, Node node, Configura
* Holds if an argument to `call` is reached in the flow covered by `nodeCandFwd1`.
*/
pragma[nomagic]
-private predicate flowOutCandFwd1(DataFlowCall call, boolean fromArg, Configuration config) {
+private predicate nodeCandFwd1IsEntered(DataFlowCall call, boolean fromArg, Configuration config) {
exists(ArgumentNode arg |
nodeCandFwd1(arg, fromArg, config) and
viableParamArg(call, _, arg)
@@ -395,8 +377,11 @@ bindingset[result, b]
private boolean unbindBool(boolean b) { result != b.booleanNot() }
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is part of a path from a source to a sink in the
+ * configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink.
*/
pragma[nomagic]
private predicate nodeCand1(Node node, boolean toReturn, Configuration config) {
@@ -433,81 +418,86 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
)
or
// store
- exists(Content f |
- nodeCand1Store(f, node, toReturn, config) and
- readCand1(f, config)
+ exists(Content c |
+ nodeCand1Store(c, node, toReturn, config) and
+ nodeCand1IsRead(c, config)
)
or
// read
- exists(Node mid, Content f |
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ exists(Node mid, Content c |
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, toReturn, config)
)
or
// flow into a callable
exists(DataFlowCall call |
- nodeCand1Arg(call, node, false, config) and
+ nodeCand1In(call, node, false, config) and
toReturn = false
or
- nodeCand1ArgToReturn(call, node, config) and
- flowInCand1(call, toReturn, config)
+ nodeCand1InToReturn(call, node, config) and
+ nodeCand1IsReturned(call, toReturn, config)
)
or
// flow out of a callable
exists(ReturnPosition pos |
- nodeCand1ReturnPosition(pos, config) and
+ nodeCand1Out(pos, config) and
getReturnPosition(node) = pos and
toReturn = true
)
}
-pragma[nomagic]
-private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _, config) }
-
-pragma[nomagic]
-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`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate readCand1(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content c, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd1(node, unbind(config)) and
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, _, config)
)
}
pragma[nomagic]
-private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
- exists(Node mid |
+private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
+ exists(Node mid, TypedContent tc |
nodeCand1(mid, toReturn, config) and
- storeCandFwd1(f, unbind(config)) and
- storeDirect(node, f, mid)
+ nodeCandFwd1IsStored(c, unbind(config)) and
+ store(node, tc, mid, _) and
+ c = tc.getContent()
)
}
/**
- * Holds if `f` is the target of both a read and a store in the flow covered
+ * Holds if `c` is the target of both a read and a store in the flow covered
* by `nodeCand1`.
*/
-private predicate readStoreCand1(Content f, Configuration conf) {
- readCand1(f, conf) and
- nodeCand1Store(f, _, _, conf)
+private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
+ nodeCand1IsRead(c, conf) and
+ nodeCand1Store(c, _, _, conf)
}
pragma[nomagic]
-private predicate viableParamArgCandFwd1(
+private predicate viableReturnPosOutNodeCandFwd1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+) {
+ nodeCandFwd1ReturnPosition(pos, _, config) and
+ viableReturnPosOut(call, pos, out)
+}
+
+pragma[nomagic]
+private predicate nodeCand1Out(ReturnPosition pos, Configuration config) {
+ exists(DataFlowCall call, Node out |
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, config)
+ )
+}
+
+pragma[nomagic]
+private predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
) {
viableParamArg(call, p, arg) and
@@ -515,32 +505,35 @@ private predicate viableParamArgCandFwd1(
}
pragma[nomagic]
-private predicate nodeCand1Arg(
+private predicate nodeCand1In(
DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config
) {
exists(ParameterNode p |
nodeCand1(p, toReturn, config) and
- viableParamArgCandFwd1(call, p, arg, config)
+ viableParamArgNodeCandFwd1(call, p, arg, config)
)
}
pragma[nomagic]
-private predicate nodeCand1ArgToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
- nodeCand1Arg(call, arg, true, config)
+private predicate nodeCand1InToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
+ nodeCand1In(call, arg, true, config)
}
/**
* Holds if an output from `call` is reached in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate flowInCand1(DataFlowCall call, boolean toReturn, Configuration config) {
+private predicate nodeCand1IsReturned(DataFlowCall call, boolean toReturn, Configuration config) {
exists(Node out |
nodeCand1(out, toReturn, config) and
nodeCandFwd1OutFromArg(call, out, config)
)
}
-private predicate throughFlowNodeCand(Node node, Configuration config) {
+pragma[nomagic]
+private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _, config) }
+
+private predicate throughFlowNodeCand1(Node node, Configuration config) {
nodeCand1(node, true, config) and
not fullBarrier(node, config) and
not inBarrier(node, config) and
@@ -549,11 +542,11 @@ private predicate throughFlowNodeCand(Node node, Configuration config) {
/** Holds if flow may return from `callable`. */
pragma[nomagic]
-private predicate returnFlowCallableCand(
+private predicate returnFlowCallableNodeCand1(
DataFlowCallable callable, ReturnKindExt kind, Configuration config
) {
exists(ReturnNodeExt ret |
- throughFlowNodeCand(ret, config) and
+ throughFlowNodeCand1(ret, config) and
callable = ret.getEnclosingCallable() and
kind = ret.getKind()
)
@@ -563,10 +556,10 @@ private predicate returnFlowCallableCand(
* Holds if flow may enter through `p` and reach a return node making `p` a
* candidate for the origin of a summary.
*/
-private predicate parameterThroughFlowCand(ParameterNode p, Configuration config) {
+private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration config) {
exists(ReturnKindExt kind |
- throughFlowNodeCand(p, config) and
- returnFlowCallableCand(p.getEnclosingCallable(), kind, config) and
+ throughFlowNodeCand1(p, config) and
+ returnFlowCallableNodeCand1(p.getEnclosingCallable(), kind, config) and
// we don't expect a parameter to return stored in itself
not exists(int pos |
kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos)
@@ -575,420 +568,77 @@ private predicate parameterThroughFlowCand(ParameterNode p, Configuration config
}
pragma[nomagic]
-private predicate store(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
- nodeCand1(n2, unbind(config)) and
- (
- storeDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentSome(f), n2)
- )
-}
-
-pragma[nomagic]
-private predicate read(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
- nodeCand1(n2, unbind(config)) and
- (
- readDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentSome(f), TContentNone(), n2)
- )
-}
-
-/**
- * Holds if `p` can flow to `node` in the same callable with `summary`
- * representing the flow path. The type of the tracked object is `t2`, and if
- * the summary includes a store step, `t1` is the tracked type just prior to the
- * store, that is, the type of the stored object, otherwise `t1` is equal to `t2`.
- */
-pragma[nomagic]
-private predicate parameterFlow(
- ParameterNode p, Node node, DataFlowType t1, DataFlowType t2, Summary summary,
- Configuration config
-) {
- parameterThroughFlowCand(p, config) and
- p = node and
- t1 = getErasedNodeTypeBound(node) and
- t1 = t2 and
- summary = TSummaryVal()
- or
- throughFlowNodeCand(node, unbind(config)) and
- (
- exists(Node mid |
- parameterFlow(p, mid, t1, t2, summary, config) and
- localFlowStep(mid, node, config) and
- compatibleTypes(t2, getErasedNodeTypeBound(node))
- )
- or
- exists(Node mid, Summary midsum |
- parameterFlow(p, mid, _, _, midsum, config) and
- additionalLocalFlowStep(mid, node, config) and
- t1 = getErasedNodeTypeBound(node) and
- t1 = t2 and
- summary = midsum.additionalStep()
- )
- or
- // read step
- exists(Node mid, Content f, Summary midsum |
- parameterFlow(p, mid, _, _, midsum, config) and
- read(mid, f, node, config) and
- summary = midsum.readStep(f) and
- t1 = f.getType() and
- t1 = t2
- )
- or
- // store step
- exists(Node mid, Content f, Summary midsum |
- parameterFlow(p, mid, t1, /* t1 */ _, midsum, config) and
- store(mid, f, node, config) and
- summary = midsum.storeStep(f) and
- compatibleTypes(t1, f.getType()) and
- t2 = f.getContainerType()
- )
- or
- // value flow through a callable
- exists(Node arg |
- parameterFlow(p, arg, t1, t2, summary, config) and
- argumentValueFlowsThrough(_, arg, TContentNone(), TContentNone(), node) and
- compatibleTypes(t2, getErasedNodeTypeBound(node))
- )
- or
- // flow through a callable
- exists(Node arg, Summary s1, Summary s2 |
- parameterFlow(p, arg, _, _, s1, config) and
- argumentFlowsThrough(arg, node, t1, t2, s2, config) and
- summary = s1.compose(s2)
- )
- )
-}
-
-private predicate viableParamArgCand(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
-) {
- viableParamArg(call, p, arg) and
- nodeCand1(arg, unbind(config)) and
- nodeCand1(p, config) and
- not outBarrier(arg, config) and
- not inBarrier(p, config)
-}
-
-pragma[nomagic]
-private predicate parameterFlowReturn(
- ParameterNode p, ReturnNodeExt ret, ReturnKindExt kind, DataFlowType t1, DataFlowType t2,
- Summary summary, Configuration config
-) {
- parameterFlow(p, ret, t1, t2, summary, config) and
- kind = ret.getKind() and
- not summary.isPartial() and
- not exists(int pos | kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos))
-}
-
-pragma[nomagic]
-private predicate argumentFlowsThrough0(
- DataFlowCall call, ArgumentNode arg, ReturnKindExt kind, DataFlowType t1, DataFlowType t2,
- Summary summary, Configuration config
-) {
- exists(ParameterNode p |
- viableParamArgCand(call, p, arg, config) and
- parameterFlowReturn(p, _, kind, t1, t2, summary, config)
- )
-}
-
-/**
- * Holds if data can flow from `arg` to `out` through a call with `summary`
- * representing the flow path. The type of the tracked object is `t2`, and if
- * the summary includes a store step, `t1` is the tracked type just prior to the
- * store, that is, the type of the stored object, otherwise `t1` is equal to `t2`.
- */
-private predicate argumentFlowsThrough(
- ArgumentNode arg, Node out, DataFlowType t1, DataFlowType t2, Summary summary,
- Configuration config
-) {
- nodeCand1(out, unbind(config)) and
- not inBarrier(out, config) and
- compatibleTypes(t2, getErasedNodeTypeBound(out)) and
- exists(DataFlowCall call, ReturnKindExt kind |
- argumentFlowsThrough0(call, arg, kind, t1, t2, summary, config) and
- out = kind.getAnOutNode(call)
- )
-}
-
-pragma[noinline]
-private predicate readStoreNode(
- DataFlowCall call, ArgumentNode arg, Content f1, Configuration config
-) {
- exists(Content f2, Node out |
- argumentValueFlowsThrough(call, arg, TContentSome(f1), TContentSome(f2), out) and
- nodeCand1(out, config) and
- readStoreCand1(f2, unbind(config))
- )
-}
-
-private newtype TNodeExt =
- TNormalNode(Node node) { nodeCand1(node, _) } or
- TReadStoreNode(DataFlowCall call, ArgumentNode arg, Content f1, Configuration config) {
- nodeCand1(arg, config) and
- readStoreNode(call, arg, f1, config) and
- readStoreCand1(f1, unbind(config))
- } or
- TReadTaintNode(ArgumentNode arg, Content f, Configuration config) {
- argumentFlowsThrough(arg, _, _, _, TSummaryReadTaint(f), config)
- } or
- TTaintStoreNode(ArgumentNode arg, DataFlowType t, Configuration config) {
- argumentFlowsThrough(arg, _, t, _, TSummaryTaintStore(_), config)
- }
-
-/**
- * An extended data flow node. Either a normal node, or an intermediate node
- * used to split up a summarized flow steps.
- *
- * This is purely an internal implementation detail.
- */
-abstract private class NodeExt extends TNodeExt {
- /** Gets the underlying (normal) node, if any. */
- abstract Node getNode();
-
- abstract DataFlowType getErasedNodeTypeBound();
-
- abstract DataFlowCallable getEnclosingCallable();
-
- abstract predicate isCand1(Configuration config);
-
- abstract string toString();
-
- abstract predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- );
-}
-
-/** A `Node` at which a cast can occur such that the type should be checked. */
-abstract private class CastingNodeExt extends NodeExt { }
-
-private class NormalNodeExt extends NodeExt, TNormalNode {
- override Node getNode() { this = TNormalNode(result) }
-
- override DataFlowType getErasedNodeTypeBound() {
- result = getErasedRepr(this.getNode().getTypeBound())
- }
-
- override DataFlowCallable getEnclosingCallable() {
- result = this.getNode().getEnclosingCallable()
- }
-
- override predicate isCand1(Configuration config) { nodeCand1(this.getNode(), config) }
-
- override string toString() { result = this.getNode().toString() }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class NormalCastingNodeExt extends CastingNodeExt, NormalNodeExt {
- NormalCastingNodeExt() { this.getNode() instanceof CastingNode }
-}
-
-private class ReadStoreNodeExt extends CastingNodeExt, TReadStoreNode {
- private DataFlowCall call;
- private ArgumentNode arg;
- private Content f1;
- private Configuration config0;
-
- ReadStoreNodeExt() { this = TReadStoreNode(call, arg, f1, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = f1.getType() }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = "(inside) " + call.toString() + " [read " + f1 + "]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class ReadTaintNode extends NodeExt, TReadTaintNode {
- private ArgumentNode arg;
- private Content f;
- private Configuration config0;
-
- ReadTaintNode() { this = TReadTaintNode(arg, f, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = f.getType() }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = arg.toString() + " [read taint " + f + "]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class TaintStoreNode extends NodeExt, TTaintStoreNode {
- private ArgumentNode arg;
- private DataFlowType t;
- private Configuration config0;
-
- TaintStoreNode() { this = TTaintStoreNode(arg, t, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = t }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = arg.toString() + " [taint store]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private predicate additionalLocalFlowStepExt(
- NodeExt node1, NodeExt node2, DataFlowType t, Configuration config
-) {
- exists(ArgumentNode arg, Content f |
- node1 = TReadTaintNode(arg, f, config) and
- argumentFlowsThrough(arg, node2.getNode(), _, t, TSummaryReadTaint(f), config)
- )
- or
- node2 = TTaintStoreNode(node1.getNode(), t, config)
-}
-
-pragma[nomagic]
-private predicate readExt(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- read(node1.getNode(), f, node2.getNode(), config)
- or
- node2 = TReadStoreNode(_, node1.getNode(), f, config)
- or
- node2 = TReadTaintNode(node1.getNode(), f, config)
-}
-
-pragma[nomagic]
-private predicate storeExt(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- store(node1.getNode(), f, node2.getNode(), config)
- or
- exists(DataFlowCall call, ArgumentNode arg, Content f1, Node n2 |
- node1 = TReadStoreNode(call, arg, f1, config) and
- n2 = node2.getNode() and
- argumentValueFlowsThrough(call, arg, TContentSome(f1), TContentSome(f), n2) and
+private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
+ exists(TypedContent tc |
+ nodeCand1IsReadAndStored(c, config) and
nodeCand1(n2, unbind(config)) and
- readStoreCand1(f, unbind(config))
- )
- or
- exists(ArgumentNode arg, DataFlowType t |
- node1 = TTaintStoreNode(arg, t, config) and
- argumentFlowsThrough(arg, node2.getNode(), t, _, TSummaryTaintStore(f), config)
+ store(n1, tc, n2, _) and
+ c = tc.getContent()
)
}
-private predicate jumpStepExt(NodeExt node1, NodeExt node2, Configuration config) {
- jumpStep(node1.getNode(), node2.getNode(), config)
-}
-
-private predicate additionalJumpStepExt(NodeExt node1, NodeExt node2, Configuration config) {
- additionalJumpStep(node1.getNode(), node2.getNode(), config)
-}
-
-private predicate argumentValueFlowsThrough(NodeExt node1, NodeExt node2) {
- argumentValueFlowsThrough(_, node1.getNode(), TContentNone(), TContentNone(), node2.getNode())
-}
-
-private predicate argumentFlowsThrough(
- NodeExt arg, NodeExt out, DataFlowType t, Configuration config
-) {
- argumentFlowsThrough(arg.getNode(), out.getNode(), _, t, TSummaryTaint(), config)
-}
-
-/**
- * Holds if data can flow from `node1` to `node2` in one local step or a step
- * through a callable.
- */
-pragma[noinline]
-private predicate localFlowStepOrFlowThroughCallable(
- NodeExt node1, NodeExt node2, Configuration config
-) {
- exists(Node n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode()
- |
- nodeCand1(n1, config) and
- localFlowStep(n1, n2, config)
- or
- nodeCand1(n1, config) and
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentNone(), n2)
- )
-}
-
-/**
- * Holds if data can flow from `node1` to `node2` in one local step or a step
- * through a callable, in both cases using an additional flow step from the
- * configuration.
- */
-pragma[noinline]
-private predicate additionalLocalFlowStepOrFlowThroughCallable(
- NodeExt node1, NodeExt node2, Configuration config
-) {
- exists(Node n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode()
- |
- nodeCand1(n1, config) and
- additionalLocalFlowStep(n1, n2, config)
- or
- argumentFlowsThrough(n1, n2, _, _, TSummaryTaint(), config)
- )
- or
- additionalLocalFlowStepExt(node1, node2, _, config)
+pragma[nomagic]
+private predicate read(Node n1, Content c, Node n2, Configuration config) {
+ nodeCand1IsReadAndStored(c, config) and
+ nodeCand1(n2, unbind(config)) and
+ read(n1, c, n2)
}
pragma[noinline]
-private ReturnPosition getReturnPosition1(ReturnNodeExt node, Configuration config) {
- result = getReturnPosition(node) and
- nodeCand1(node, config)
+private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ localFlowStep(node1, node2, config)
+}
+
+pragma[noinline]
+private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ additionalLocalFlowStep(node1, node2, config)
+}
+
+pragma[nomagic]
+private predicate viableReturnPosOutNodeCand1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+) {
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, config)
}
/**
- * Holds if data can flow out of a callable from `node1` to `node2`, either
+ * Holds if data can flow out of `call` from `ret` to `out`, 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 flowOutOfCallableNodeCand1(ReturnNodeExt node1, Node node2, Configuration config) {
- nodeCand1(node2, config) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- exists(DataFlowCall call, ReturnKindExt kind |
- getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
- node2 = kind.getAnOutNode(call)
- )
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand1(
+ DataFlowCall call, ReturnNodeExt ret, Node out, Configuration config
+) {
+ viableReturnPosOutNodeCand1(call, getReturnPosition(ret), out, config) and
+ nodeCand1(ret, config) and
+ not outBarrier(ret, config) and
+ not inBarrier(out, config)
+}
+
+pragma[nomagic]
+private predicate viableParamArgNodeCand1(
+ DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+) {
+ viableParamArgNodeCandFwd1(call, p, arg, config) and
+ nodeCand1(arg, config)
}
/**
- * Holds if data can flow into a callable and that this step is part of a
+ * Holds if data can flow into `call` and that this step is part of a
* path from a source to a sink.
*/
-private predicate flowIntoCallableNodeCand1(
- ArgumentNode node1, ParameterNode node2, Configuration config
+pragma[nomagic]
+private predicate flowIntoCallNodeCand1(
+ DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config
) {
- viableParamArgCand(_, node2, node1, config)
+ viableParamArgNodeCand1(call, p, arg, config) and
+ nodeCand1(p, config) and
+ not outBarrier(arg, config) and
+ not inBarrier(p, config)
}
/**
@@ -999,7 +649,7 @@ private predicate flowIntoCallableNodeCand1(
private int branch(Node n1, Configuration conf) {
result =
strictcount(Node n |
- flowOutOfCallableNodeCand1(n1, n, conf) or flowIntoCallableNodeCand1(n1, n, conf)
+ flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1011,269 +661,391 @@ private int branch(Node n1, Configuration conf) {
private int join(Node n2, Configuration conf) {
result =
strictcount(Node n |
- flowOutOfCallableNodeCand1(n, n2, conf) or flowIntoCallableNodeCand1(n, n2, conf)
+ flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
/**
- * Holds if data can flow out of a callable from `node1` to `node2`, either
+ * Holds if data can flow out of `call` from `ret` to `out`, 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. The
* `allowsFieldFlow` flag indicates whether the branching is within the limit
* specified by the configuration.
*/
-private predicate flowOutOfCallableNodeCand1(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand1(
+ DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config
) {
- exists(ReturnNodeExt n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode() and
- flowOutOfCallableNodeCand1(n1, n2, config) and
- exists(int b, int j |
- b = branch(n1, config) and
- j = join(n2, config) and
- if b.minimum(j) <= config.fieldFlowBranchLimit()
- then allowsFieldFlow = true
- else allowsFieldFlow = false
- )
+ flowOutOfCallNodeCand1(call, ret, out, config) and
+ exists(int b, int j |
+ b = branch(ret, config) and
+ j = join(out, config) and
+ if b.minimum(j) <= config.fieldFlowBranchLimit()
+ then allowsFieldFlow = true
+ else allowsFieldFlow = false
)
}
/**
- * Holds if data can flow into a callable and that this step is part of a
+ * Holds if data can flow into `call` and that this step is part of a
* path from a source to a sink. The `allowsFieldFlow` flag indicates whether
* the branching is within the limit specified by the configuration.
*/
-private predicate flowIntoCallableNodeCand1(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+pragma[nomagic]
+private predicate flowIntoCallNodeCand1(
+ DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow,
+ Configuration config
) {
- exists(ArgumentNode n1, ParameterNode n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode() and
- flowIntoCallableNodeCand1(n1, n2, config) and
- exists(int b, int j |
- b = branch(n1, config) and
- j = join(n2, config) and
- if b.minimum(j) <= config.fieldFlowBranchLimit()
- then allowsFieldFlow = true
- else allowsFieldFlow = false
- )
+ flowIntoCallNodeCand1(call, arg, p, config) and
+ exists(int b, int j |
+ b = branch(arg, config) and
+ j = join(p, config) and
+ if b.minimum(j) <= config.fieldFlowBranchLimit()
+ then allowsFieldFlow = true
+ else allowsFieldFlow = false
)
}
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is reachable from a source in the configuration `config`.
+ * The Boolean `stored` records whether the tracked value is stored into a
+ * field of `node`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argStored` records whether the tracked
+ * value was stored into a field of the argument.
*/
-private predicate nodeCandFwd2(NodeExt node, boolean fromArg, boolean stored, Configuration config) {
- nodeCand1(node.getNode(), config) and
- config.isSource(node.getNode()) and
+private predicate nodeCandFwd2(
+ Node node, boolean fromArg, BooleanOption argStored, boolean stored, Configuration config
+) {
+ nodeCand1(node, config) and
+ config.isSource(node) and
fromArg = false and
+ argStored = TBooleanNone() and
stored = false
or
- node.isCand1(unbind(config)) and
+ nodeCand1(node, unbind(config)) and
(
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, stored, config) and
- localFlowStepOrFlowThroughCallable(mid, node, config)
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, stored, config) and
+ localFlowStepNodeCand1(mid, node, config)
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, stored, config) and
- additionalLocalFlowStepOrFlowThroughCallable(mid, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, stored, config) and
+ additionalLocalFlowStepNodeCand1(mid, node, config) and
stored = false
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, _, stored, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
+ exists(Node mid |
+ nodeCandFwd2(mid, _, _, stored, config) and
+ jumpStep(mid, node, config) and
+ fromArg = false and
+ argStored = TBooleanNone()
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, _, stored, config) and
- additionalJumpStepExt(mid, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, _, _, stored, config) and
+ additionalJumpStep(mid, node, config) and
fromArg = false and
+ argStored = TBooleanNone() and
stored = false
)
or
// store
- exists(NodeExt mid, Content f |
- nodeCandFwd2(mid, fromArg, _, config) and
- storeExt(mid, f, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, _, config) and
+ storeCand1(mid, _, node, config) and
stored = true
)
or
// read
- exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, config) and
- storeCandFwd2(f, stored, config)
+ exists(Content c |
+ nodeCandFwd2Read(c, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(c, stored, config)
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- nodeCandFwd2(mid, _, stored, config) and
- flowIntoCallableNodeCand1(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (stored = false or allowsFieldFlow = true)
- )
+ // flow into a callable
+ nodeCandFwd2In(_, node, _, _, stored, config) and
+ fromArg = true and
+ if parameterThroughFlowNodeCand1(node, config)
+ then argStored = TBooleanSome(stored)
+ else argStored = TBooleanNone()
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- nodeCandFwd2(mid, false, stored, config) and
- flowOutOfCallableNodeCand1(mid, node, allowsFieldFlow, config) and
- fromArg = false and
- (stored = false or allowsFieldFlow = true)
+ // flow out of a callable
+ exists(DataFlowCall call |
+ nodeCandFwd2Out(call, node, fromArg, argStored, stored, config) and
+ fromArg = false
+ or
+ exists(boolean argStored0 |
+ nodeCandFwd2OutFromArg(call, node, argStored0, stored, config) and
+ nodeCandFwd2IsEntered(call, fromArg, argStored, argStored0, config)
+ )
)
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
*/
pragma[noinline]
-private predicate storeCandFwd2(Content f, boolean stored, Configuration config) {
- exists(NodeExt mid, NodeExt node |
+private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
+ exists(Node mid, Node node |
useFieldFlow(config) and
- node.isCand1(unbind(config)) and
- nodeCandFwd2(mid, _, stored, config) and
- storeExt(mid, f, node, config)
+ nodeCand1(node, unbind(config)) and
+ nodeCandFwd2(mid, _, _, stored, config) and
+ storeCand1(mid, c, node, config)
)
}
pragma[nomagic]
-private predicate nodeCandFwd2Read(Content f, NodeExt node, boolean fromArg, Configuration config) {
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, true, config) and
- readExt(mid, f, node, config)
+private predicate nodeCandFwd2Read(
+ Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+) {
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, true, config) and
+ read(mid, c, node, config)
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2In(
+ DataFlowCall call, ParameterNode p, boolean fromArg, BooleanOption argStored, boolean stored,
+ Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ nodeCandFwd2(arg, fromArg, argStored, stored, config) and
+ flowIntoCallNodeCand1(call, arg, p, allowsFieldFlow, config)
+ |
+ stored = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2Out(
+ DataFlowCall call, Node out, boolean fromArg, BooleanOption argStored, boolean stored,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ nodeCandFwd2(ret, fromArg, argStored, stored, config) and
+ flowOutOfCallNodeCand1(call, ret, out, allowsFieldFlow, config)
+ |
+ stored = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2OutFromArg(
+ DataFlowCall call, Node out, boolean argStored, boolean stored, Configuration config
+) {
+ nodeCandFwd2Out(call, out, true, TBooleanSome(argStored), stored, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `nodeCandFwd2`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd2IsEntered(
+ DataFlowCall call, boolean fromArg, BooleanOption argStored, boolean stored, Configuration config
+) {
+ exists(ParameterNode p |
+ nodeCandFwd2In(call, p, fromArg, argStored, stored, config) and
+ parameterThroughFlowNodeCand1(p, config)
)
}
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is part of a path from a source to a sink in the
+ * configuration `config`. The Boolean `read` records whether the tracked
+ * value must be read from a field of `node` in order to reach a sink.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnRead`
+ * records whether a field must be read from the returned value.
*/
-private predicate nodeCand2(NodeExt node, boolean toReturn, boolean read, Configuration config) {
- nodeCandFwd2(node, _, false, config) and
- config.isSink(node.getNode()) and
+private predicate nodeCand2(
+ Node node, boolean toReturn, BooleanOption returnRead, boolean read, Configuration config
+) {
+ nodeCandFwd2(node, _, _, false, config) and
+ config.isSink(node) and
toReturn = false and
+ returnRead = TBooleanNone() and
read = false
or
- nodeCandFwd2(node, _, unbindBool(read), unbind(config)) and
+ nodeCandFwd2(node, _, _, unbindBool(read), unbind(config)) and
(
- exists(NodeExt mid |
- localFlowStepOrFlowThroughCallable(node, mid, config) and
- nodeCand2(mid, toReturn, read, config)
+ exists(Node mid |
+ localFlowStepNodeCand1(node, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, read, config)
)
or
- exists(NodeExt mid |
- additionalLocalFlowStepOrFlowThroughCallable(node, mid, config) and
- nodeCand2(mid, toReturn, read, config) and
+ exists(Node mid |
+ additionalLocalFlowStepNodeCand1(node, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, read, config) and
read = false
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- nodeCand2(mid, _, read, config) and
- toReturn = false
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ nodeCand2(mid, _, _, read, config) and
+ toReturn = false and
+ returnRead = TBooleanNone()
)
or
- exists(NodeExt mid |
- additionalJumpStepExt(node, mid, config) and
- nodeCand2(mid, _, read, config) and
+ exists(Node mid |
+ additionalJumpStep(node, mid, config) and
+ nodeCand2(mid, _, _, read, config) and
toReturn = false and
+ returnRead = TBooleanNone() and
read = false
)
or
// store
- exists(Content f |
- nodeCand2Store(f, node, toReturn, read, config) and
- readCand2(f, read, config)
+ exists(Content c |
+ nodeCand2Store(c, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(c, read, config)
)
or
// read
- exists(NodeExt mid, Content f, boolean read0 |
- readExt(node, f, mid, config) and
- storeCandFwd2(f, unbindBool(read0), unbind(config)) and
- nodeCand2(mid, toReturn, read0, config) and
+ exists(Node mid, Content c, boolean read0 |
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
+ nodeCand2(mid, toReturn, returnRead, read0, config) and
read = true
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand1(node, mid, allowsFieldFlow, config) and
- nodeCand2(mid, false, read, config) and
- toReturn = false and
- (read = false or allowsFieldFlow = true)
+ // flow into a callable
+ exists(DataFlowCall call |
+ nodeCand2In(call, node, toReturn, returnRead, read, config) and
+ toReturn = false
+ or
+ exists(boolean returnRead0 |
+ nodeCand2InToReturn(call, node, returnRead0, read, config) and
+ nodeCand2IsReturned(call, toReturn, returnRead, returnRead0, config)
+ )
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand1(node, mid, allowsFieldFlow, config) and
- nodeCand2(mid, _, read, config) and
- toReturn = true and
- (read = false or allowsFieldFlow = true)
- )
+ // flow out of a callable
+ nodeCand2Out(_, node, _, _, read, config) and
+ toReturn = true and
+ if nodeCandFwd2(node, true, TBooleanSome(_), unbindBool(read), config)
+ then returnRead = TBooleanSome(read)
+ else returnRead = TBooleanNone()
)
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate readCand2(Content f, boolean read, Configuration config) {
- exists(NodeExt mid, NodeExt node |
+private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
+ exists(Node mid, Node node |
useFieldFlow(config) and
- nodeCandFwd2(node, _, true, unbind(config)) and
- readExt(node, f, mid, config) and
- storeCandFwd2(f, unbindBool(read), unbind(config)) and
- nodeCand2(mid, _, read, config)
+ nodeCandFwd2(node, _, _, true, unbind(config)) and
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
+ nodeCand2(mid, _, _, read, config)
)
}
pragma[nomagic]
private predicate nodeCand2Store(
- Content f, NodeExt node, boolean toReturn, boolean stored, Configuration config
+ Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
+ Configuration config
) {
- exists(NodeExt mid |
- storeExt(node, f, mid, config) and
- nodeCand2(mid, toReturn, true, config) and
- nodeCandFwd2(node, _, stored, unbind(config))
- )
-}
-
-pragma[nomagic]
-private predicate storeCand2(Content f, boolean stored, Configuration conf) {
- exists(NodeExt node |
- nodeCand2Store(f, node, _, stored, conf) and
- nodeCand2(node, _, stored, conf)
+ exists(Node mid |
+ storeCand1(node, c, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, true, config) and
+ nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
/**
- * Holds if `f` is the target of both a store and a read in the path graph
- * covered by `nodeCand2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
*/
-pragma[noinline]
-private predicate readStoreCand(Content f, Configuration conf) {
- exists(boolean apNonEmpty |
- storeCand2(f, apNonEmpty, conf) and
- readCand2(f, apNonEmpty, conf)
+pragma[nomagic]
+private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
+ exists(Node node |
+ nodeCand2Store(c, node, _, _, stored, conf) and
+ nodeCand2(node, _, _, stored, conf)
)
}
-private predicate nodeCand2(NodeExt node, Configuration config) { nodeCand2(node, _, _, config) }
+/**
+ * Holds if `c` is the target of both a store and a read in the path graph
+ * covered by `nodeCand2`.
+ */
+pragma[noinline]
+private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
+ exists(boolean apNonEmpty |
+ nodeCand2IsStored(c, apNonEmpty, conf) and
+ nodeCand2IsRead(c, apNonEmpty, conf)
+ )
+}
pragma[nomagic]
-private predicate flowOutOfCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate nodeCand2Out(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
) {
- flowOutOfCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ exists(Node out, boolean allowsFieldFlow |
+ nodeCand2(out, toReturn, returnRead, read, config) and
+ flowOutOfCallNodeCand1(call, ret, out, allowsFieldFlow, config)
+ |
+ read = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCand2In(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ nodeCand2(p, toReturn, returnRead, read, config) and
+ flowIntoCallNodeCand1(call, arg, p, allowsFieldFlow, config)
+ |
+ read = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCand2InToReturn(
+ DataFlowCall call, ArgumentNode arg, boolean returnRead, boolean read, Configuration config
+) {
+ nodeCand2In(call, arg, true, TBooleanSome(returnRead), read, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `nodeCand2`.
+ */
+pragma[nomagic]
+private predicate nodeCand2IsReturned(
+ DataFlowCall call, boolean toReturn, BooleanOption returnRead, boolean read, Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ nodeCand2Out(call, ret, toReturn, returnRead, read, config) and
+ nodeCandFwd2(ret, true, TBooleanSome(_), read, config)
+ )
+}
+
+private predicate nodeCand2(Node node, Configuration config) { nodeCand2(node, _, _, _, config) }
+
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand2(
+ DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
+) {
+ flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
nodeCand2(node2, config) and
nodeCand2(node1, unbind(config))
}
pragma[nomagic]
-private predicate flowIntoCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate flowIntoCallNodeCand2(
+ DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ Configuration config
) {
- flowIntoCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
nodeCand2(node2, config) and
nodeCand2(node1, unbind(config))
}
@@ -1284,15 +1056,15 @@ private module LocalFlowBigStep {
* flow steps in a dataflow path.
*/
private predicate localFlowEntry(Node node, Configuration config) {
- nodeCand2(TNormalNode(node), config) and
+ nodeCand2(node, config) and
(
config.isSource(node) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
node instanceof ParameterNode or
- node instanceof OutNode or
- node instanceof PostUpdateNode or
- readDirect(_, _, node) or
+ node instanceof OutNodeExt or
+ store(_, _, node, _) or
+ read(_, _, node) or
node instanceof CastNode
)
}
@@ -1302,15 +1074,13 @@ private module LocalFlowBigStep {
* flow steps in a dataflow path.
*/
private predicate localFlowExit(Node node, Configuration config) {
- exists(Node next | nodeCand2(TNormalNode(next), config) |
+ exists(Node next | nodeCand2(next, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
- flowIntoCallableNodeCand1(node, next, config) or
- flowOutOfCallableNodeCand1(node, next, config) or
- argumentFlowsThrough(node, next, _, _, _, config) or
- argumentValueFlowsThrough(_, node, TContentNone(), TContentNone(), next) or
- storeDirect(node, _, next) or
- readDirect(node, _, next)
+ flowIntoCallNodeCand1(_, node, next, config) or
+ flowOutOfCallNodeCand1(_, node, next, config) or
+ store(node, _, next, _) or
+ read(node, _, next)
)
or
node instanceof CastNode
@@ -1318,6 +1088,13 @@ private module LocalFlowBigStep {
config.isSink(node)
}
+ pragma[noinline]
+ private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
+ additionalLocalFlowStepNodeCand1(node1, node2, config) and
+ nodeCand2(node1, _, _, false, config) and
+ nodeCand2(node2, _, _, false, unbind(config))
+ }
+
/**
* Holds if the local path from `node1` to `node2` is a prefix of a maximal
* subsequence of local flow steps in a dataflow path.
@@ -1334,33 +1111,33 @@ private module LocalFlowBigStep {
(
localFlowEntry(node1, config) and
(
- localFlowStep(node1, node2, config) and
+ localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
t = getErasedNodeTypeBound(node1)
or
- additionalLocalFlowStep(node1, node2, config) and
+ additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
t = getErasedNodeTypeBound(node2)
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
or
exists(Node mid |
localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
- localFlowStep(mid, node2, config) and
+ localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof CastNode and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
)
or
exists(Node mid |
localFlowStepPlus(node1, mid, _, _, config, cc) and
- additionalLocalFlowStep(mid, node2, config) and
+ additionalLocalFlowStepNodeCand2(mid, node2, config) and
not mid instanceof CastNode and
preservesValue = false and
t = getErasedNodeTypeBound(node2) and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
)
)
}
@@ -1371,311 +1148,380 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config,
+ Node node1, Node node2, boolean preservesValue, AccessPathFrontNil apf, Configuration config,
LocalCallContext callContext
) {
- localFlowStepPlus(node1, node2, preservesValue, t, config, callContext) and
+ localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, config)
}
-
- pragma[nomagic]
- predicate localFlowBigStepExt(
- NodeExt node1, NodeExt node2, boolean preservesValue, AccessPathFrontNil apf,
- Configuration config
- ) {
- localFlowBigStep(node1.getNode(), node2.getNode(), preservesValue, apf.getType(), config, _)
- or
- additionalLocalFlowStepExt(node1, node2, apf.getType(), config) and
- nodeCand2(node1, config) and
- nodeCand2(node2, unbind(config)) and
- preservesValue = false
- }
}
private import LocalFlowBigStep
pragma[nomagic]
-private predicate readExtCand2(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- readExt(node1, f, node2, config) and
- nodeCand2(node1, _, true, unbind(config)) and
+private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
+ read(node1, c, node2, config) and
+ nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- readStoreCand(f, unbind(config))
+ nodeCand2IsReadAndStored(c, unbind(config))
}
pragma[nomagic]
-private predicate storeExtCand2(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- storeExt(node1, f, node2, config) and
+private predicate storeCand2(
+ Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+) {
+ store(node1, tc, node2, contentType) and
nodeCand2(node1, config) and
- nodeCand2(node2, _, true, unbind(config)) and
- readStoreCand(f, unbind(config))
-}
-
-private newtype TAccessPathFront =
- TFrontNil(DataFlowType t) or
- TFrontHead(Content f)
-
-/**
- * The front of an `AccessPath`. This is either a head or a nil.
- */
-abstract private class AccessPathFront extends TAccessPathFront {
- abstract string toString();
-
- abstract DataFlowType getType();
-
- abstract boolean toBoolNonEmpty();
-
- predicate headUsesContent(Content f) { this = TFrontHead(f) }
-}
-
-private class AccessPathFrontNil extends AccessPathFront, TFrontNil {
- override string toString() {
- exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
- }
-
- override DataFlowType getType() { this = TFrontNil(result) }
-
- override boolean toBoolNonEmpty() { result = false }
-}
-
-private class AccessPathFrontHead extends AccessPathFront, TFrontHead {
- override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) }
-
- override DataFlowType getType() {
- exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
- }
-
- override boolean toBoolNonEmpty() { result = true }
+ nodeCand2(node2, _, _, true, unbind(config)) and
+ nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
}
/**
- * Holds if data can flow from a source to `node` with the given `apf`.
+ * Holds if `node` is reachable with access path front `apf` from a
+ * source in the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argApf` records the front of the
+ * access path of that argument.
*/
pragma[nomagic]
private predicate flowCandFwd(
- NodeExt node, boolean fromArg, AccessPathFront apf, Configuration config
+ Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
) {
- flowCandFwd0(node, fromArg, apf, config) and
- if node instanceof CastingNodeExt
- then compatibleTypes(node.getErasedNodeTypeBound(), apf.getType())
+ flowCandFwd0(node, fromArg, argApf, apf, config) and
+ if node instanceof CastingNode
+ then compatibleTypes(getErasedNodeTypeBound(node), apf.getType())
else any()
}
pragma[nomagic]
private predicate flowCandFwd0(
- NodeExt node, boolean fromArg, AccessPathFront apf, Configuration config
+ Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
) {
- nodeCand2(node, _, false, config) and
- config.isSource(node.getNode()) and
+ nodeCand2(node, _, _, false, config) and
+ config.isSource(node) and
fromArg = false and
- apf = TFrontNil(node.getErasedNodeTypeBound())
+ argApf = TAccessPathFrontNone() and
+ apf = TFrontNil(getErasedNodeTypeBound(node))
or
- exists(NodeExt mid |
- flowCandFwd(mid, fromArg, apf, config) and
- localFlowBigStepExt(mid, node, true, _, config)
+ exists(Node mid |
+ flowCandFwd(mid, fromArg, argApf, apf, config) and
+ localFlowBigStep(mid, node, true, _, config, _)
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(mid, fromArg, nil, config) and
- localFlowBigStepExt(mid, node, false, apf, config)
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(mid, fromArg, argApf, nil, config) and
+ localFlowBigStep(mid, node, false, apf, config, _)
)
or
- nodeCand2(node, unbind(config)) and
- (
- exists(NodeExt mid |
- flowCandFwd(mid, _, apf, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(mid, _, nil, config) and
- additionalJumpStepExt(mid, node, config) and
- fromArg = false and
- apf = TFrontNil(node.getErasedNodeTypeBound())
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowCandFwd(mid, _, apf, config) and
- flowIntoCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowCandFwd(mid, false, apf, config) and
- flowOutOfCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = false and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- flowCandFwd(mid, fromArg, apf, config) and
- argumentValueFlowsThrough(mid, node)
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil, DataFlowType t |
- flowCandFwd(mid, fromArg, nil, config) and
- argumentFlowsThrough(mid, node, t, config) and
- apf = TFrontNil(t)
- )
+ exists(Node mid |
+ flowCandFwd(mid, _, _, apf, config) and
+ nodeCand2(node, unbind(config)) and
+ jumpStep(mid, node, config) and
+ fromArg = false and
+ argApf = TAccessPathFrontNone()
)
or
- exists(NodeExt mid, Content f |
- flowCandFwd(mid, fromArg, _, config) and
- storeExtCand2(mid, f, node, config) and
- nodeCand2(node, _, true, unbind(config)) and
- apf.headUsesContent(f)
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(mid, _, _, nil, config) and
+ nodeCand2(node, unbind(config)) and
+ additionalJumpStep(mid, node, config) and
+ fromArg = false and
+ argApf = TAccessPathFrontNone() and
+ apf = TFrontNil(getErasedNodeTypeBound(node))
)
or
- exists(Content f |
- flowCandFwdRead(f, node, fromArg, config) and
- consCandFwd(f, apf, config) and
- nodeCand2(node, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ // store
+ exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ storeCand2(mid, tc, node, contentType, config) and
+ nodeCand2(node, _, _, true, unbind(config)) and
+ apf.headUsesContent(tc) and
+ compatibleTypes(apf0.getType(), contentType)
+ )
+ or
+ // read
+ exists(TypedContent tc |
+ flowCandFwdRead(tc, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(tc, apf, config) and
+ nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ )
+ or
+ // flow into a callable
+ flowCandFwdIn(_, node, _, _, apf, config) and
+ fromArg = true and
+ if nodeCand2(node, true, _, unbindBool(apf.toBoolNonEmpty()), config)
+ then argApf = TAccessPathFrontSome(apf)
+ else argApf = TAccessPathFrontNone()
+ or
+ // flow out of a callable
+ exists(DataFlowCall call |
+ flowCandFwdOut(call, node, fromArg, argApf, apf, config) and
+ fromArg = false
+ or
+ exists(AccessPathFront argApf0 |
+ flowCandFwdOutFromArg(call, node, argApf0, apf, config) and
+ flowCandFwdIsEntered(call, fromArg, argApf, argApf0, config)
+ )
)
}
pragma[nomagic]
-private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) {
- exists(NodeExt mid, NodeExt n |
- flowCandFwd(mid, _, apf, config) and
- storeExtCand2(mid, f, n, config) and
- nodeCand2(n, _, true, unbind(config)) and
- compatibleTypes(apf.getType(), f.getType())
+private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n, DataFlowType contentType |
+ flowCandFwd(mid, _, _, apf, config) and
+ storeCand2(mid, tc, n, contentType, config) and
+ nodeCand2(n, _, _, true, unbind(config)) and
+ compatibleTypes(apf.getType(), contentType)
)
}
pragma[nomagic]
-private predicate flowCandFwdRead(Content f, NodeExt node, boolean fromArg, Configuration config) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowCandFwd(mid, fromArg, apf0, config) and
- readExtCand2(mid, f, node, config) and
- apf0.headUsesContent(f)
+private predicate flowCandFwdRead0(
+ Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFrontHead apf, Configuration config
+) {
+ flowCandFwd(node1, fromArg, argApf, apf, config) and
+ readCand2(node1, c, node2, config) and
+ apf.headUsesContent(tc)
+}
+
+pragma[nomagic]
+private predicate flowCandFwdRead(
+ TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+) {
+ flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
+}
+
+pragma[nomagic]
+private predicate flowCandFwdIn(
+ DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ flowCandFwd(arg, fromArg, argApf, apf, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ flowCandFwd(ret, fromArg, argApf, apf, config) and
+ flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandFwdOutFromArg(
+ DataFlowCall call, Node node, AccessPathFront argApf, AccessPathFront apf, Configuration config
+) {
+ flowCandFwdOut(call, node, true, TAccessPathFrontSome(argApf), apf, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `flowCandFwd`.
+ */
+pragma[nomagic]
+private predicate flowCandFwdIsEntered(
+ DataFlowCall call, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ParameterNode p |
+ flowCandFwdIn(call, p, fromArg, argApf, apf, config) and
+ nodeCand2(p, true, TBooleanSome(_), unbindBool(apf.toBoolNonEmpty()), config)
)
}
/**
- * Holds if data can flow from a source to `node` with the given `apf` and
- * from there flow to a sink.
+ * Holds if `node` with access path front `apf` is part of a path from a
+ * source to a sink in the configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnApf`
+ * records the front of the access path of the returned value.
*/
pragma[nomagic]
-private predicate flowCand(NodeExt node, boolean toReturn, AccessPathFront apf, Configuration config) {
- flowCand0(node, toReturn, apf, config) and
- flowCandFwd(node, _, apf, config)
+private predicate flowCand(
+ Node node, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ flowCand0(node, toReturn, returnApf, apf, config) and
+ flowCandFwd(node, _, _, apf, config)
}
pragma[nomagic]
private predicate flowCand0(
- NodeExt node, boolean toReturn, AccessPathFront apf, Configuration config
+ Node node, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
) {
- flowCandFwd(node, _, apf, config) and
- config.isSink(node.getNode()) and
+ flowCandFwd(node, _, _, apf, config) and
+ config.isSink(node) and
toReturn = false and
+ returnApf = TAccessPathFrontNone() and
apf instanceof AccessPathFrontNil
or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flowCand(mid, toReturn, apf, config)
+ exists(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flowCand(mid, toReturn, returnApf, apf, config)
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- localFlowBigStepExt(node, mid, false, _, config) and
- flowCand(mid, toReturn, nil, config) and
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(node, _, _, apf, config) and
+ localFlowBigStep(node, mid, false, _, config, _) and
+ flowCand(mid, toReturn, returnApf, nil, config) and
apf instanceof AccessPathFrontNil
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flowCand(mid, _, apf, config) and
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flowCand(mid, _, _, apf, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone()
+ )
+ or
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(node, _, _, apf, config) and
+ additionalJumpStep(node, mid, config) and
+ flowCand(mid, _, _, nil, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone() and
+ apf instanceof AccessPathFrontNil
+ )
+ or
+ // store
+ exists(TypedContent tc |
+ flowCandStore(node, tc, apf, toReturn, returnApf, config) and
+ flowCandConsCand(tc, apf, config)
+ )
+ or
+ // read
+ exists(TypedContent tc, AccessPathFront apf0 |
+ flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(tc, apf0, config)
+ )
+ or
+ // flow into a callable
+ exists(DataFlowCall call |
+ flowCandIn(call, node, toReturn, returnApf, apf, config) and
toReturn = false
+ or
+ exists(AccessPathFront returnApf0 |
+ flowCandInToReturn(call, node, returnApf0, apf, config) and
+ flowCandIsReturned(call, toReturn, returnApf, returnApf0, config)
+ )
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- additionalJumpStepExt(node, mid, config) and
- flowCand(mid, _, nil, config) and
- toReturn = false and
- apf instanceof AccessPathFrontNil
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flowCand(mid, false, apf, config) and
- toReturn = false and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flowCand(mid, _, apf, config) and
- toReturn = true and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- argumentValueFlowsThrough(node, mid) and
- flowCand(mid, toReturn, apf, config)
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil |
- argumentFlowsThrough(node, mid, _, config) and
- flowCand(mid, toReturn, nil, config) and
- apf instanceof AccessPathFrontNil and
- flowCandFwd(node, _, apf, config)
- )
- or
- exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, apf0, config) and
- apf0.headUsesContent(f) and
- consCand(f, apf, config)
- )
- or
- exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, config) and
- apf.headUsesContent(f)
- )
+ // flow out of a callable
+ flowCandOut(_, node, _, _, apf, config) and
+ toReturn = true and
+ if flowCandFwd(node, true, _, apf, config)
+ then returnApf = TAccessPathFrontSome(apf)
+ else returnApf = TAccessPathFrontNone()
+}
+
+pragma[nomagic]
+private predicate readCandFwd(
+ Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
+) {
+ flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
}
pragma[nomagic]
private predicate flowCandRead(
- NodeExt node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
) {
- exists(NodeExt mid |
- readExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ readCandFwd(node, tc, apf, mid, config) and
+ flowCand(mid, toReturn, returnApf, apf0, config)
)
}
pragma[nomagic]
private predicate flowCandStore(
- NodeExt node, Content f, boolean toReturn, AccessPathFrontHead apf0, Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, Configuration config
) {
- exists(NodeExt mid |
- storeExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ flowCandFwd(node, _, _, apf, config) and
+ storeCand2(node, tc, mid, _, unbind(config)) and
+ flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
)
}
pragma[nomagic]
-private predicate consCand(Content f, AccessPathFront apf, Configuration config) {
- consCandFwd(f, apf, config) and
- exists(NodeExt n, AccessPathFrontHead apf0 |
- flowCandFwd(n, _, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandRead(n, f, _, apf, config)
+private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(tc, apf, config) and
+ flowCandRead(_, tc, _, _, _, apf, config)
+}
+
+pragma[nomagic]
+private predicate flowCandOut(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathFrontOption returnApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(Node out, boolean allowsFieldFlow |
+ flowCand(out, toReturn, returnApf, apf, config) and
+ flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandIn(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathFrontOption returnApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ flowCand(p, toReturn, returnApf, apf, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandInToReturn(
+ DataFlowCall call, ArgumentNode arg, AccessPathFront returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ flowCandIn(call, arg, true, TAccessPathFrontSome(returnApf), apf, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `flowCand`.
+ */
+pragma[nomagic]
+private predicate flowCandIsReturned(
+ DataFlowCall call, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ flowCandOut(call, ret, toReturn, returnApf, apf, config) and
+ flowCandFwd(ret, true, TAccessPathFrontSome(_), apf, config)
)
}
private newtype TAccessPath =
TNil(DataFlowType t) or
- TConsNil(Content f, DataFlowType t) { consCand(f, TFrontNil(t), _) } or
- TConsCons(Content f1, Content f2, int len) {
- consCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
+ TConsCons(TypedContent tc1, TypedContent tc2, int len) {
+ flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
}
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first two
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
* elements of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -1684,7 +1530,7 @@ private newtype TAccessPath =
abstract private class AccessPath extends TAccessPath {
abstract string toString();
- abstract Content getHead();
+ abstract TypedContent getHead();
abstract int len();
@@ -1695,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
/**
* Holds if this access path has `head` at the front and may be followed by `tail`.
*/
- abstract predicate pop(Content head, AccessPath tail);
+ abstract predicate pop(TypedContent head, AccessPath tail);
}
private class AccessPathNil extends AccessPath, TNil {
@@ -1705,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
override string toString() { result = concat(": " + ppReprType(t)) }
- override Content getHead() { none() }
+ override TypedContent getHead() { none() }
override int len() { result = 0 }
@@ -1713,357 +1559,469 @@ private class AccessPathNil extends AccessPath, TNil {
override AccessPathFront getFront() { result = TFrontNil(t) }
- override predicate pop(Content head, AccessPath tail) { none() }
+ override predicate pop(TypedContent head, AccessPath tail) { none() }
}
abstract private class AccessPathCons extends AccessPath { }
private class AccessPathConsNil extends AccessPathCons, TConsNil {
- private Content f;
+ private TypedContent tc;
private DataFlowType t;
- AccessPathConsNil() { this = TConsNil(f, t) }
+ AccessPathConsNil() { this = TConsNil(tc, t) }
override string toString() {
// The `concat` becomes "" if `ppReprType` has no result.
- result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
+ result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
}
- override Content getHead() { result = f }
+ override TypedContent getHead() { result = tc }
override int len() { result = 1 }
- override DataFlowType getType() { result = f.getContainerType() }
+ override DataFlowType getType() { result = tc.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f) }
+ override AccessPathFront getFront() { result = TFrontHead(tc) }
- override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
+ override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
}
private class AccessPathConsCons extends AccessPathCons, TConsCons {
- private Content f1;
- private Content f2;
+ private TypedContent tc1;
+ private TypedContent tc2;
private int len;
- AccessPathConsCons() { this = TConsCons(f1, f2, len) }
+ AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
override string toString() {
if len = 2
- then result = "[" + f1.toString() + ", " + f2.toString() + "]"
- else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
+ else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
}
- override Content getHead() { result = f1 }
+ override TypedContent getHead() { result = tc1 }
override int len() { result = len }
- override DataFlowType getType() { result = f1.getContainerType() }
+ override DataFlowType getType() { result = tc1.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f1) }
+ override AccessPathFront getFront() { result = TFrontHead(tc1) }
- override predicate pop(Content head, AccessPath tail) {
- head = f1 and
+ override predicate pop(TypedContent head, AccessPath tail) {
+ head = tc1 and
(
- tail = TConsCons(f2, _, len - 1)
+ tail = TConsCons(tc2, _, len - 1)
or
len = 2 and
- tail = TConsNil(f2, _)
+ tail = TConsNil(tc2, _)
)
}
}
-/** Gets the access path obtained by popping `f` from `ap`, if any. */
-private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
+/** Gets the access path obtained by popping `tc` from `ap`, if any. */
+private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
-/** Gets the access path obtained by pushing `f` onto `ap`. */
-private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
+/** Gets the access path obtained by pushing `tc` onto `ap`. */
+private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
+
+private newtype TAccessPathOption =
+ TAccessPathNone() or
+ TAccessPathSome(AccessPath ap)
+
+private class AccessPathOption extends TAccessPathOption {
+ string toString() {
+ this = TAccessPathNone() and result = ""
+ or
+ this = TAccessPathSome(any(AccessPath ap | result = ap.toString()))
+ }
+}
/**
- * Holds if data can flow from a source to `node` with the given `ap`.
+ * Holds if `node` is reachable with access path `ap` from a source in
+ * the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argAp` records the access path of that
+ * argument.
*/
private predicate flowFwd(
- NodeExt node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config
+ Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
) {
- flowFwd0(node, fromArg, apf, ap, config) and
- flowCand(node, _, apf, config)
+ flowFwd0(node, fromArg, argAp, apf, ap, config) and
+ flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
- NodeExt node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config
+ Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
) {
- flowCand(node, _, _, config) and
- config.isSource(node.getNode()) and
+ flowCand(node, _, _, _, config) and
+ config.isSource(node) and
fromArg = false and
- ap = TNil(node.getErasedNodeTypeBound()) and
+ argAp = TAccessPathNone() and
+ ap = TNil(getErasedNodeTypeBound(node)) and
apf = ap.(AccessPathNil).getFront()
or
- flowCand(node, _, _, unbind(config)) and
+ flowCand(node, _, _, _, unbind(config)) and
(
- exists(NodeExt mid |
- flowFwd(mid, fromArg, apf, ap, config) and
- localFlowBigStepExt(mid, node, true, _, config)
+ exists(Node mid |
+ flowFwd(mid, fromArg, argAp, apf, ap, config) and
+ localFlowBigStep(mid, node, true, _, config, _)
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(mid, fromArg, _, nil, config) and
- localFlowBigStepExt(mid, node, false, apf, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(mid, fromArg, argAp, _, nil, config) and
+ localFlowBigStep(mid, node, false, apf, config, _) and
apf = ap.(AccessPathNil).getFront()
)
or
- exists(NodeExt mid |
- flowFwd(mid, _, apf, ap, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
- )
- or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(mid, _, _, nil, config) and
- additionalJumpStepExt(mid, node, config) and
+ exists(Node mid |
+ flowFwd(mid, _, _, apf, ap, config) and
+ jumpStep(mid, node, config) and
fromArg = false and
- ap = TNil(node.getErasedNodeTypeBound()) and
- apf = ap.(AccessPathNil).getFront()
+ argAp = TAccessPathNone()
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowFwd(mid, _, apf, ap, config) and
- flowIntoCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowFwd(mid, false, apf, ap, config) and
- flowOutOfCallableNodeCand2(mid, node, allowsFieldFlow, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(mid, _, _, _, nil, config) and
+ additionalJumpStep(mid, node, config) and
fromArg = false and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- flowFwd(mid, fromArg, apf, ap, config) and
- argumentValueFlowsThrough(mid, node)
- )
- or
- exists(NodeExt mid, AccessPathNil nil, DataFlowType t |
- flowFwd(mid, fromArg, _, nil, config) and
- argumentFlowsThrough(mid, node, t, config) and
- ap = TNil(t) and
+ argAp = TAccessPathNone() and
+ ap = TNil(getErasedNodeTypeBound(node)) and
apf = ap.(AccessPathNil).getFront()
)
)
or
- exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, config) and
- ap = push(f, ap0)
+ // store
+ exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
+ or
+ // read
+ exists(TypedContent tc |
+ flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
+ flowFwdConsCand(tc, apf, ap, config)
)
or
- exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, config) and
- flowConsCandFwd(f, apf, ap, config)
+ // flow into a callable
+ flowFwdIn(_, node, _, _, apf, ap, config) and
+ fromArg = true and
+ if flowCand(node, true, _, apf, config)
+ then argAp = TAccessPathSome(ap)
+ else argAp = TAccessPathNone()
+ or
+ // flow out of a callable
+ exists(DataFlowCall call |
+ flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
+ fromArg = false
+ or
+ exists(AccessPath argAp0 |
+ flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and
+ flowFwdIsEntered(call, fromArg, argAp, argAp0, config)
+ )
)
}
pragma[nomagic]
private predicate flowFwdStore(
- NodeExt node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
- Configuration config
+ Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFront apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- flowFwdStore1(mid, f, node, apf0, apf, config)
+ exists(Node mid, AccessPathFront apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
+ flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
pragma[nomagic]
-private predicate flowFwdStore0(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, Configuration config
+private predicate storeCand(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
+ Configuration config
) {
- storeExtCand2(mid, f, node, config) and
- flowCand(mid, _, apf0, config)
+ storeCand2(mid, tc, node, _, config) and
+ flowCand(mid, _, _, apf0, config) and
+ apf.headUsesContent(tc)
}
pragma[noinline]
-private predicate flowFwdStore1(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, AccessPathFrontHead apf,
+private predicate flowFwdStore0(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
- flowFwdStore0(mid, f, node, apf0, config) and
- consCand(f, apf0, config) and
- apf.headUsesContent(f) and
- flowCand(node, _, apf, unbind(config))
+ storeCand(mid, tc, node, apf0, apf, config) and
+ flowCandConsCand(tc, apf0, config) and
+ flowCand(node, _, _, apf, unbind(config))
+}
+
+pragma[nomagic]
+private predicate flowFwdRead0(
+ Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
+ boolean fromArg, AccessPathOption argAp, Configuration config
+) {
+ flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
+ readCandFwd(node1, tc, apf0, node2, config)
}
pragma[nomagic]
private predicate flowFwdRead(
- NodeExt node, Content f, AccessPath ap0, boolean fromArg, Configuration config
+ Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- readExtCand2(mid, f, node, config) and
- apf0.headUsesContent(f) and
- flowCand(node, _, _, unbind(config))
+ exists(Node mid, TypedContent tc |
+ flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
+ flowCand(node, _, _, apf, unbind(config)) and
+ flowCandConsCand(tc, apf, unbind(config))
)
}
pragma[nomagic]
-private predicate flowConsCandFwd(
- Content f, AccessPathFront apf, AccessPath ap, Configuration config
+private predicate flowFwdConsCand(
+ TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
) {
- exists(NodeExt n |
- flowFwd(n, _, apf, ap, config) and
- flowFwdStore1(n, f, _, apf, _, config)
+ exists(Node n |
+ flowFwd(n, _, _, apf, ap, config) and
+ flowFwdStore0(n, tc, _, apf, _, config)
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdIn(
+ DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ flowFwd(arg, fromArg, argAp, apf, ap, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
+ flowCand(p, _, _, _, unbind(config))
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ flowFwd(ret, fromArg, argAp, apf, ap, config) and
+ flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
+ flowCand(node, _, _, _, unbind(config))
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdOutFromArg(
+ DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
+) {
+ flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `flowFwd`.
+ */
+pragma[nomagic]
+private predicate flowFwdIsEntered(
+ DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
+) {
+ exists(ParameterNode p, AccessPathFront apf |
+ flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and
+ flowCand(p, true, TAccessPathFrontSome(_), apf, config)
)
}
/**
- * Holds if data can flow from a source to `node` with the given `ap` and
- * from there flow to a sink.
+ * Holds if `node` with access path `ap` is part of a path from a source to
+ * a sink in the configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnAp`
+ * records the access path of the returned value.
*/
-private predicate flow(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flow0(node, toReturn, ap, config) and
- flowFwd(node, _, _, ap, config)
+private predicate flow(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flow0(node, toReturn, returnAp, ap, config) and
+ flowFwd(node, _, _, _, ap, config)
}
-private predicate flow0(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flowFwd(node, _, _, ap, config) and
- config.isSink(node.getNode()) and
+private predicate flow0(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flowFwd(node, _, _, _, ap, config) and
+ config.isSink(node) and
toReturn = false and
+ returnAp = TAccessPathNone() and
ap instanceof AccessPathNil
or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flow(mid, toReturn, ap, config)
+ exists(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flow(mid, toReturn, returnAp, ap, config)
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(node, _, _, ap, config) and
- localFlowBigStepExt(node, mid, false, _, config) and
- flow(mid, toReturn, nil, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(node, _, _, _, ap, config) and
+ localFlowBigStep(node, mid, false, _, config, _) and
+ flow(mid, toReturn, returnAp, nil, config) and
ap instanceof AccessPathNil
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flow(mid, _, ap, config) and
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flow(mid, _, _, ap, config) and
+ toReturn = false and
+ returnAp = TAccessPathNone()
+ )
+ or
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(node, _, _, _, ap, config) and
+ additionalJumpStep(node, mid, config) and
+ flow(mid, _, _, nil, config) and
+ toReturn = false and
+ returnAp = TAccessPathNone() and
+ ap instanceof AccessPathNil
+ )
+ or
+ // store
+ exists(TypedContent tc |
+ flowStore(tc, node, toReturn, returnAp, ap, config) and
+ flowConsCand(tc, ap, config)
+ )
+ or
+ // read
+ exists(Node mid, AccessPath ap0 |
+ readFlowFwd(node, _, mid, ap, ap0, config) and
+ flow(mid, toReturn, returnAp, ap0, config)
+ )
+ or
+ // flow into a callable
+ exists(DataFlowCall call |
+ flowIn(call, node, toReturn, returnAp, ap, config) and
toReturn = false
+ or
+ exists(AccessPath returnAp0 |
+ flowInToReturn(call, node, returnAp0, ap, config) and
+ flowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(node, _, _, ap, config) and
- additionalJumpStepExt(node, mid, config) and
- flow(mid, _, nil, config) and
- toReturn = false and
- ap instanceof AccessPathNil
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flow(mid, false, ap, config) and
- toReturn = false and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flow(mid, _, ap, config) and
- toReturn = true and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- argumentValueFlowsThrough(node, mid) and
- flow(mid, toReturn, ap, config)
- )
- or
- exists(NodeExt mid, AccessPathNil nil |
- argumentFlowsThrough(node, mid, _, config) and
- flow(mid, toReturn, nil, config) and
- ap instanceof AccessPathNil and
- flowFwd(node, _, _, ap, config)
- )
- or
- exists(Content f |
- flowStore(f, node, toReturn, ap, config) and
- flowConsCand(f, ap, config)
- )
- or
- exists(NodeExt mid, AccessPath ap0 |
- readFwd(node, _, mid, ap, ap0, config) and
- flow(mid, toReturn, ap0, config)
- )
+ // flow out of a callable
+ flowOut(_, node, _, _, ap, config) and
+ toReturn = true and
+ if flowFwd(node, true, TAccessPathSome(_), _, ap, config)
+ then returnAp = TAccessPathSome(ap)
+ else returnAp = TAccessPathNone()
}
pragma[nomagic]
-private predicate storeFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate storeFlowFwd(
+ Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- storeExtCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, config) and
- ap0 = push(f, ap)
+ storeCand2(node1, tc, node2, _, config) and
+ flowFwdStore(node2, tc, ap, _, _, _, config) and
+ ap0 = push(tc, ap)
}
pragma[nomagic]
private predicate flowStore(
- Content f, NodeExt node, boolean toReturn, AccessPath ap, Configuration config
+ TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
) {
- exists(NodeExt mid, AccessPath ap0 |
- storeFwd(node, f, mid, ap, ap0, config) and
- flow(mid, toReturn, ap0, config)
+ exists(Node mid, AccessPath ap0 |
+ storeFlowFwd(node, tc, mid, ap, ap0, config) and
+ flow(mid, toReturn, returnAp, ap0, config)
)
}
pragma[nomagic]
-private predicate readFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate readFlowFwd(
+ Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- readExtCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, config) and
- ap0 = pop(f, ap) and
- flowConsCandFwd(f, _, ap0, unbind(config))
+ exists(AccessPathFrontHead apf |
+ readCandFwd(node1, tc, apf, node2, config) and
+ flowFwdRead(node2, apf, ap, _, _, _, config) and
+ ap0 = pop(tc, ap) and
+ flowFwdConsCand(tc, _, ap0, unbind(config))
+ )
}
pragma[nomagic]
-private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
- exists(NodeExt n, NodeExt mid |
- flow(mid, _, ap, config) and
- readFwd(n, f, mid, _, ap, config)
+private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
+ exists(Node n, Node mid |
+ flow(mid, _, _, ap, config) and
+ readFlowFwd(n, tc, mid, _, ap, config)
+ )
+}
+
+pragma[nomagic]
+private predicate flowOut(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(Node out, boolean allowsFieldFlow |
+ flow(out, toReturn, returnAp, ap, config) and
+ flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config)
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowIn(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ flow(p, toReturn, returnAp, ap, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowInToReturn(
+ DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config
+) {
+ flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `flow`.
+ */
+pragma[nomagic]
+private predicate flowIsReturned(
+ DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ flowOut(call, ret, toReturn, returnAp, ap, config) and
+ flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
)
}
bindingset[conf, result]
private Configuration unbind(Configuration conf) { result >= conf and result <= conf }
-private predicate flow(Node n, Configuration config) { flow(TNormalNode(n), _, _, config) }
+private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) }
+
+pragma[noinline]
+private predicate parameterFlow(
+ ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config
+) {
+ flow(p, true, _, ap, config) and
+ c = p.getEnclosingCallable()
+}
private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParameterNode p, AccessPath ap) {
- exists(ReturnNodeExt ret, Configuration config | flow(TNormalNode(p), true, ap, config) |
- exists(Summary summary |
- parameterFlowReturn(p, ret, _, _, _, summary, config) and
- flow(ret, unbind(config))
- |
- // taint through
- summary = TSummaryTaint() and
- ap instanceof AccessPathNil
- or
- // taint setter
- summary = TSummaryTaintStore(_) and
- ap instanceof AccessPathNil
- or
- // taint getter
- summary = TSummaryReadTaint(ap.(AccessPathConsNil).getHead())
- )
- or
- exists(ContentOption contentIn |
- parameterValueFlowReturn(p, ret, _, contentIn, _) and
- flow(ret, unbind(config))
- |
- // value through/setter
- contentIn = TContentNone()
- or
- // value getter (+ setter)
- contentIn = TContentSome(ap.getHead())
- )
+ exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 |
+ parameterFlow(p, ap, ret.getEnclosingCallable(), config) and
+ flow(ret, true, TAccessPathSome(_), ap0, config) and
+ flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config)
)
}
@@ -2113,7 +2071,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, cc, sc, ap) and
config = mid.getConfiguration() and
- flow(TNormalNode(node), _, ap, unbind(config))
+ flow(node, _, _, ap, unbind(config))
)
} or
TPathNodeSink(Node node, Configuration config) {
@@ -2164,14 +2122,31 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
+ private predicate isHidden() {
+ nodeIsHidden(this.getNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ }
+
+ private PathNode getASuccessorIfHidden() {
+ this.isHidden() and
+ result = this.(PathNodeImpl).getASuccessorImpl()
+ }
+
/** Gets a successor of this node, if any. */
- PathNode getASuccessor() { none() }
+ final PathNode getASuccessor() {
+ result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
+ not this.isHidden() and
+ not result.isHidden()
+ }
/** Holds if this node is a source. */
predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
+ abstract PathNode getASuccessorImpl();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2246,7 +2221,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
result.getConfiguration() = unbind(this.getConfiguration())
}
- override PathNodeImpl getASuccessor() {
+ override PathNodeImpl getASuccessorImpl() {
// an intermediate step to another intermediate node
result = getSuccMid()
or
@@ -2283,7 +2258,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNode getASuccessor() { none() }
+ override PathNode getASuccessorImpl() { none() }
override predicate isSource() { config.isSource(node) }
}
@@ -2293,17 +2268,18 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
* a callable is recorded by `cc`.
*/
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
- exists(
- AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
- LocalCallContext localCC
- |
- pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
- localCC = getLocalCallContext(cc, enclosing)
+ exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
+ midnode = mid.getNode() and
+ conf = mid.getConfiguration() and
+ cc = mid.getCallContext() and
+ sc = mid.getSummaryCtx() and
+ localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
ap = ap0
or
- localFlowBigStep(midnode, node, false, ap.(AccessPathNil).getType(), conf, localCC) and
+ localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -2318,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
mid.getAp() instanceof AccessPathNil and
ap = TNil(getErasedNodeTypeBound(node))
or
- exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
+ exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2332,44 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
}
pragma[nomagic]
-private predicate pathIntoLocalStep(
- PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
- AccessPath ap0, Configuration conf
-) {
- midnode = mid.getNode() and
- cc = mid.getCallContext() and
- conf = mid.getConfiguration() and
- localFlowBigStep(midnode, _, _, _, conf, _) and
- enclosing = midnode.getEnclosingCallable() and
- sc = mid.getSummaryCtx() and
- ap0 = mid.getAp()
-}
-
-pragma[nomagic]
-private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
- readDirect(node1, f, node2) and
+private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ readCandFwd(node1, tc, _, node2, config) and
flow(node2, config)
}
pragma[nomagic]
-private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
+private predicate pathReadStep(
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+) {
ap0 = mid.getAp() and
- readCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
-private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- storeDirect(node1, f, node2) and
+private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ storeCand2(node1, tc, node2, _, config) and
flow(node2, config)
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
@@ -2399,11 +2363,11 @@ private predicate pathOutOfCallable1(
}
pragma[noinline]
-private Node getAnOutNodeCand(
+private Node getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config
) {
result = kind.getAnOutNode(call) and
- flow(TNormalNode(result), _, ap, config)
+ flow(result, _, _, ap, config)
}
/**
@@ -2415,7 +2379,7 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config |
pathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = getAnOutNodeCand(kind, call, ap, config)
+ out = getAnOutNodeFlow(kind, call, ap, config)
)
}
@@ -2439,7 +2403,7 @@ private predicate parameterCand(
DataFlowCallable callable, int i, AccessPath ap, Configuration config
) {
exists(ParameterNode p |
- flow(TNormalNode(p), _, ap, config) and
+ flow(p, _, _, ap, config) and
p.isParameterOf(callable, i)
)
}
@@ -2514,7 +2478,7 @@ pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
exists(DataFlowCall call, ReturnKindExt kind |
pathThroughCallable0(call, mid, kind, cc, ap) and
- out = getAnOutNodeCand(kind, call, ap, mid.getConfiguration())
+ out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration())
)
}
@@ -2555,10 +2519,7 @@ private module FlowExploration {
viableParamArg(_, node2, node1)
or
// flow out of a callable
- exists(DataFlowCall call, ReturnKindExt kind |
- getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = kind.getAnOutNode(call)
- )
+ viableReturnPosOut(_, getReturnPosition(node1), node2)
|
c1 = node1.getEnclosingCallable() and
c2 = node2.getEnclosingCallable() and
@@ -2603,10 +2564,10 @@ private module FlowExploration {
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
- TPartialCons(Content f, int len) { len in [1 .. 5] }
+ TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
* element of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -2615,7 +2576,7 @@ private module FlowExploration {
private class PartialAccessPath extends TPartialAccessPath {
abstract string toString();
- Content getHead() { this = TPartialCons(result, _) }
+ TypedContent getHead() { this = TPartialCons(result, _) }
int len() {
this = TPartialNil(_) and result = 0
@@ -2626,7 +2587,7 @@ private module FlowExploration {
DataFlowType getType() {
this = TPartialNil(result)
or
- exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
+ exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
@@ -2644,15 +2605,15 @@ private module FlowExploration {
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
override string toString() {
- exists(Content f, int len | this = TPartialCons(f, len) |
+ exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
if len = 1
- then result = "[" + f.toString() + "]"
- else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc.toString() + "]"
+ else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
override AccessPathFront getFront() {
- exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
+ exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
}
}
@@ -2828,11 +2789,12 @@ private module FlowExploration {
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
- exists(PartialAccessPath ap0, Content f |
- partialPathReadStep(mid, ap0, f, node, cc, config) and
+ exists(PartialAccessPath ap0, TypedContent tc |
+ partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, f, ap0, config)
+ apConsFwd(ap, tc, ap0, config) and
+ compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -2851,35 +2813,42 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
+ PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
+ PartialAccessPath ap2
) {
- ap1 = mid.getAp() and
- storeDirect(mid.getNode(), f, node) and
- ap2.getHead() = f and
- ap2.len() = unbindInt(ap1.len() + 1) and
- compatibleTypes(ap1.getType(), f.getType())
+ exists(Node midNode, DataFlowType contentType |
+ midNode = mid.getNode() and
+ ap1 = mid.getAp() and
+ store(midNode, tc, node, contentType) and
+ ap2.getHead() = tc and
+ ap2.len() = unbindInt(ap1.len() + 1) and
+ compatibleTypes(ap1.getType(), contentType)
+ )
}
pragma[nomagic]
private predicate apConsFwd(
- PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
+ PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
- partialPathStoreStep(mid, ap1, f, _, ap2) and
+ partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
+ PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
- ap = mid.getAp() and
- readStep(mid.getNode(), f, node) and
- ap.getHead() = f and
- config = mid.getConfiguration() and
- cc = mid.getCallContext()
+ exists(Node midNode |
+ midNode = mid.getNode() and
+ ap = mid.getAp() and
+ read(midNode, tc.getContent(), node) and
+ ap.getHead() = tc and
+ config = mid.getConfiguration() and
+ cc = mid.getCallContext()
+ )
}
private predicate partialPathOutOfCallable0(
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
index a1daeb66411..1aeedf717f7 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
*/
predicate isBarrier(Node node) { none() }
- /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */
- deprecated predicate isBarrierEdge(Node node1, Node node2) { none() }
-
/** Holds if data flow into `node` is prohibited. */
predicate isBarrierIn(Node node) { none() }
@@ -251,15 +248,11 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
-pragma[noinline]
-private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
- viableCallable(call) = result.getCallable() and
- kind = result.getKind()
-}
-
/**
- * Holds if `node` is reachable from a source in the given configuration
- * taking simple call contexts into consideration.
+ * Holds if `node` is reachable from a source in the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call.
*/
private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) {
not fullBarrier(node, config) and
@@ -293,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
exists(Node mid |
useFieldFlow(config) and
nodeCandFwd1(mid, fromArg, config) and
- storeDirect(mid, _, node) and
+ store(mid, _, node, _) and
not outBarrier(mid, config)
)
or
// read
- exists(Content f |
- nodeCandFwd1Read(f, node, fromArg, config) and
- storeCandFwd1(f, config) and
+ exists(Content c |
+ nodeCandFwd1Read(c, node, fromArg, config) and
+ nodeCandFwd1IsStored(c, config) and
not inBarrier(node, config)
)
or
@@ -317,13 +310,35 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
fromArg = false
or
nodeCandFwd1OutFromArg(call, node, config) and
- flowOutCandFwd1(call, fromArg, config)
+ nodeCandFwd1IsEntered(call, fromArg, config)
)
)
}
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
+pragma[nomagic]
+private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
+ exists(Node mid |
+ nodeCandFwd1(mid, fromArg, config) and
+ read(mid, c, node)
+ )
+}
+
+/**
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
+ exists(Node mid, Node node, TypedContent tc |
+ not fullBarrier(node, config) and
+ useFieldFlow(config) and
+ nodeCandFwd1(mid, config) and
+ store(mid, tc, node, _) and
+ c = tc.getContent()
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1ReturnPosition(
ReturnPosition pos, boolean fromArg, Configuration config
@@ -335,43 +350,10 @@ private predicate nodeCandFwd1ReturnPosition(
}
pragma[nomagic]
-private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
- exists(Node mid |
- nodeCandFwd1(mid, fromArg, config) and
- readDirect(mid, f, node)
- )
-}
-
-/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
- */
-pragma[nomagic]
-private predicate storeCandFwd1(Content f, Configuration config) {
- exists(Node mid, Node node |
- not fullBarrier(node, config) and
- useFieldFlow(config) and
- nodeCandFwd1(mid, config) and
- storeDirect(mid, f, node)
- )
-}
-
-pragma[nomagic]
-private predicate nodeCandFwd1ReturnKind(
- DataFlowCall call, ReturnKindExt kind, boolean fromArg, Configuration config
-) {
+private predicate nodeCandFwd1Out(DataFlowCall call, Node out, boolean fromArg, Configuration config) {
exists(ReturnPosition pos |
nodeCandFwd1ReturnPosition(pos, fromArg, config) and
- pos = viableReturnPos(call, kind)
- )
-}
-
-pragma[nomagic]
-private predicate nodeCandFwd1Out(
- DataFlowCall call, Node node, boolean fromArg, Configuration config
-) {
- exists(ReturnKindExt kind |
- nodeCandFwd1ReturnKind(call, kind, fromArg, config) and
- node = kind.getAnOutNode(call)
+ viableReturnPosOut(call, pos, out)
)
}
@@ -384,7 +366,7 @@ private predicate nodeCandFwd1OutFromArg(DataFlowCall call, Node node, Configura
* Holds if an argument to `call` is reached in the flow covered by `nodeCandFwd1`.
*/
pragma[nomagic]
-private predicate flowOutCandFwd1(DataFlowCall call, boolean fromArg, Configuration config) {
+private predicate nodeCandFwd1IsEntered(DataFlowCall call, boolean fromArg, Configuration config) {
exists(ArgumentNode arg |
nodeCandFwd1(arg, fromArg, config) and
viableParamArg(call, _, arg)
@@ -395,8 +377,11 @@ bindingset[result, b]
private boolean unbindBool(boolean b) { result != b.booleanNot() }
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is part of a path from a source to a sink in the
+ * configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink.
*/
pragma[nomagic]
private predicate nodeCand1(Node node, boolean toReturn, Configuration config) {
@@ -433,81 +418,86 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
)
or
// store
- exists(Content f |
- nodeCand1Store(f, node, toReturn, config) and
- readCand1(f, config)
+ exists(Content c |
+ nodeCand1Store(c, node, toReturn, config) and
+ nodeCand1IsRead(c, config)
)
or
// read
- exists(Node mid, Content f |
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ exists(Node mid, Content c |
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, toReturn, config)
)
or
// flow into a callable
exists(DataFlowCall call |
- nodeCand1Arg(call, node, false, config) and
+ nodeCand1In(call, node, false, config) and
toReturn = false
or
- nodeCand1ArgToReturn(call, node, config) and
- flowInCand1(call, toReturn, config)
+ nodeCand1InToReturn(call, node, config) and
+ nodeCand1IsReturned(call, toReturn, config)
)
or
// flow out of a callable
exists(ReturnPosition pos |
- nodeCand1ReturnPosition(pos, config) and
+ nodeCand1Out(pos, config) and
getReturnPosition(node) = pos and
toReturn = true
)
}
-pragma[nomagic]
-private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _, config) }
-
-pragma[nomagic]
-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`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate readCand1(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content c, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd1(node, unbind(config)) and
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, _, config)
)
}
pragma[nomagic]
-private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
- exists(Node mid |
+private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
+ exists(Node mid, TypedContent tc |
nodeCand1(mid, toReturn, config) and
- storeCandFwd1(f, unbind(config)) and
- storeDirect(node, f, mid)
+ nodeCandFwd1IsStored(c, unbind(config)) and
+ store(node, tc, mid, _) and
+ c = tc.getContent()
)
}
/**
- * Holds if `f` is the target of both a read and a store in the flow covered
+ * Holds if `c` is the target of both a read and a store in the flow covered
* by `nodeCand1`.
*/
-private predicate readStoreCand1(Content f, Configuration conf) {
- readCand1(f, conf) and
- nodeCand1Store(f, _, _, conf)
+private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
+ nodeCand1IsRead(c, conf) and
+ nodeCand1Store(c, _, _, conf)
}
pragma[nomagic]
-private predicate viableParamArgCandFwd1(
+private predicate viableReturnPosOutNodeCandFwd1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+) {
+ nodeCandFwd1ReturnPosition(pos, _, config) and
+ viableReturnPosOut(call, pos, out)
+}
+
+pragma[nomagic]
+private predicate nodeCand1Out(ReturnPosition pos, Configuration config) {
+ exists(DataFlowCall call, Node out |
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, config)
+ )
+}
+
+pragma[nomagic]
+private predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
) {
viableParamArg(call, p, arg) and
@@ -515,32 +505,35 @@ private predicate viableParamArgCandFwd1(
}
pragma[nomagic]
-private predicate nodeCand1Arg(
+private predicate nodeCand1In(
DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config
) {
exists(ParameterNode p |
nodeCand1(p, toReturn, config) and
- viableParamArgCandFwd1(call, p, arg, config)
+ viableParamArgNodeCandFwd1(call, p, arg, config)
)
}
pragma[nomagic]
-private predicate nodeCand1ArgToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
- nodeCand1Arg(call, arg, true, config)
+private predicate nodeCand1InToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
+ nodeCand1In(call, arg, true, config)
}
/**
* Holds if an output from `call` is reached in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate flowInCand1(DataFlowCall call, boolean toReturn, Configuration config) {
+private predicate nodeCand1IsReturned(DataFlowCall call, boolean toReturn, Configuration config) {
exists(Node out |
nodeCand1(out, toReturn, config) and
nodeCandFwd1OutFromArg(call, out, config)
)
}
-private predicate throughFlowNodeCand(Node node, Configuration config) {
+pragma[nomagic]
+private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _, config) }
+
+private predicate throughFlowNodeCand1(Node node, Configuration config) {
nodeCand1(node, true, config) and
not fullBarrier(node, config) and
not inBarrier(node, config) and
@@ -549,11 +542,11 @@ private predicate throughFlowNodeCand(Node node, Configuration config) {
/** Holds if flow may return from `callable`. */
pragma[nomagic]
-private predicate returnFlowCallableCand(
+private predicate returnFlowCallableNodeCand1(
DataFlowCallable callable, ReturnKindExt kind, Configuration config
) {
exists(ReturnNodeExt ret |
- throughFlowNodeCand(ret, config) and
+ throughFlowNodeCand1(ret, config) and
callable = ret.getEnclosingCallable() and
kind = ret.getKind()
)
@@ -563,10 +556,10 @@ private predicate returnFlowCallableCand(
* Holds if flow may enter through `p` and reach a return node making `p` a
* candidate for the origin of a summary.
*/
-private predicate parameterThroughFlowCand(ParameterNode p, Configuration config) {
+private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration config) {
exists(ReturnKindExt kind |
- throughFlowNodeCand(p, config) and
- returnFlowCallableCand(p.getEnclosingCallable(), kind, config) and
+ throughFlowNodeCand1(p, config) and
+ returnFlowCallableNodeCand1(p.getEnclosingCallable(), kind, config) and
// we don't expect a parameter to return stored in itself
not exists(int pos |
kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos)
@@ -575,420 +568,77 @@ private predicate parameterThroughFlowCand(ParameterNode p, Configuration config
}
pragma[nomagic]
-private predicate store(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
- nodeCand1(n2, unbind(config)) and
- (
- storeDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentSome(f), n2)
- )
-}
-
-pragma[nomagic]
-private predicate read(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
- nodeCand1(n2, unbind(config)) and
- (
- readDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentSome(f), TContentNone(), n2)
- )
-}
-
-/**
- * Holds if `p` can flow to `node` in the same callable with `summary`
- * representing the flow path. The type of the tracked object is `t2`, and if
- * the summary includes a store step, `t1` is the tracked type just prior to the
- * store, that is, the type of the stored object, otherwise `t1` is equal to `t2`.
- */
-pragma[nomagic]
-private predicate parameterFlow(
- ParameterNode p, Node node, DataFlowType t1, DataFlowType t2, Summary summary,
- Configuration config
-) {
- parameterThroughFlowCand(p, config) and
- p = node and
- t1 = getErasedNodeTypeBound(node) and
- t1 = t2 and
- summary = TSummaryVal()
- or
- throughFlowNodeCand(node, unbind(config)) and
- (
- exists(Node mid |
- parameterFlow(p, mid, t1, t2, summary, config) and
- localFlowStep(mid, node, config) and
- compatibleTypes(t2, getErasedNodeTypeBound(node))
- )
- or
- exists(Node mid, Summary midsum |
- parameterFlow(p, mid, _, _, midsum, config) and
- additionalLocalFlowStep(mid, node, config) and
- t1 = getErasedNodeTypeBound(node) and
- t1 = t2 and
- summary = midsum.additionalStep()
- )
- or
- // read step
- exists(Node mid, Content f, Summary midsum |
- parameterFlow(p, mid, _, _, midsum, config) and
- read(mid, f, node, config) and
- summary = midsum.readStep(f) and
- t1 = f.getType() and
- t1 = t2
- )
- or
- // store step
- exists(Node mid, Content f, Summary midsum |
- parameterFlow(p, mid, t1, /* t1 */ _, midsum, config) and
- store(mid, f, node, config) and
- summary = midsum.storeStep(f) and
- compatibleTypes(t1, f.getType()) and
- t2 = f.getContainerType()
- )
- or
- // value flow through a callable
- exists(Node arg |
- parameterFlow(p, arg, t1, t2, summary, config) and
- argumentValueFlowsThrough(_, arg, TContentNone(), TContentNone(), node) and
- compatibleTypes(t2, getErasedNodeTypeBound(node))
- )
- or
- // flow through a callable
- exists(Node arg, Summary s1, Summary s2 |
- parameterFlow(p, arg, _, _, s1, config) and
- argumentFlowsThrough(arg, node, t1, t2, s2, config) and
- summary = s1.compose(s2)
- )
- )
-}
-
-private predicate viableParamArgCand(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
-) {
- viableParamArg(call, p, arg) and
- nodeCand1(arg, unbind(config)) and
- nodeCand1(p, config) and
- not outBarrier(arg, config) and
- not inBarrier(p, config)
-}
-
-pragma[nomagic]
-private predicate parameterFlowReturn(
- ParameterNode p, ReturnNodeExt ret, ReturnKindExt kind, DataFlowType t1, DataFlowType t2,
- Summary summary, Configuration config
-) {
- parameterFlow(p, ret, t1, t2, summary, config) and
- kind = ret.getKind() and
- not summary.isPartial() and
- not exists(int pos | kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos))
-}
-
-pragma[nomagic]
-private predicate argumentFlowsThrough0(
- DataFlowCall call, ArgumentNode arg, ReturnKindExt kind, DataFlowType t1, DataFlowType t2,
- Summary summary, Configuration config
-) {
- exists(ParameterNode p |
- viableParamArgCand(call, p, arg, config) and
- parameterFlowReturn(p, _, kind, t1, t2, summary, config)
- )
-}
-
-/**
- * Holds if data can flow from `arg` to `out` through a call with `summary`
- * representing the flow path. The type of the tracked object is `t2`, and if
- * the summary includes a store step, `t1` is the tracked type just prior to the
- * store, that is, the type of the stored object, otherwise `t1` is equal to `t2`.
- */
-private predicate argumentFlowsThrough(
- ArgumentNode arg, Node out, DataFlowType t1, DataFlowType t2, Summary summary,
- Configuration config
-) {
- nodeCand1(out, unbind(config)) and
- not inBarrier(out, config) and
- compatibleTypes(t2, getErasedNodeTypeBound(out)) and
- exists(DataFlowCall call, ReturnKindExt kind |
- argumentFlowsThrough0(call, arg, kind, t1, t2, summary, config) and
- out = kind.getAnOutNode(call)
- )
-}
-
-pragma[noinline]
-private predicate readStoreNode(
- DataFlowCall call, ArgumentNode arg, Content f1, Configuration config
-) {
- exists(Content f2, Node out |
- argumentValueFlowsThrough(call, arg, TContentSome(f1), TContentSome(f2), out) and
- nodeCand1(out, config) and
- readStoreCand1(f2, unbind(config))
- )
-}
-
-private newtype TNodeExt =
- TNormalNode(Node node) { nodeCand1(node, _) } or
- TReadStoreNode(DataFlowCall call, ArgumentNode arg, Content f1, Configuration config) {
- nodeCand1(arg, config) and
- readStoreNode(call, arg, f1, config) and
- readStoreCand1(f1, unbind(config))
- } or
- TReadTaintNode(ArgumentNode arg, Content f, Configuration config) {
- argumentFlowsThrough(arg, _, _, _, TSummaryReadTaint(f), config)
- } or
- TTaintStoreNode(ArgumentNode arg, DataFlowType t, Configuration config) {
- argumentFlowsThrough(arg, _, t, _, TSummaryTaintStore(_), config)
- }
-
-/**
- * An extended data flow node. Either a normal node, or an intermediate node
- * used to split up a summarized flow steps.
- *
- * This is purely an internal implementation detail.
- */
-abstract private class NodeExt extends TNodeExt {
- /** Gets the underlying (normal) node, if any. */
- abstract Node getNode();
-
- abstract DataFlowType getErasedNodeTypeBound();
-
- abstract DataFlowCallable getEnclosingCallable();
-
- abstract predicate isCand1(Configuration config);
-
- abstract string toString();
-
- abstract predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- );
-}
-
-/** A `Node` at which a cast can occur such that the type should be checked. */
-abstract private class CastingNodeExt extends NodeExt { }
-
-private class NormalNodeExt extends NodeExt, TNormalNode {
- override Node getNode() { this = TNormalNode(result) }
-
- override DataFlowType getErasedNodeTypeBound() {
- result = getErasedRepr(this.getNode().getTypeBound())
- }
-
- override DataFlowCallable getEnclosingCallable() {
- result = this.getNode().getEnclosingCallable()
- }
-
- override predicate isCand1(Configuration config) { nodeCand1(this.getNode(), config) }
-
- override string toString() { result = this.getNode().toString() }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class NormalCastingNodeExt extends CastingNodeExt, NormalNodeExt {
- NormalCastingNodeExt() { this.getNode() instanceof CastingNode }
-}
-
-private class ReadStoreNodeExt extends CastingNodeExt, TReadStoreNode {
- private DataFlowCall call;
- private ArgumentNode arg;
- private Content f1;
- private Configuration config0;
-
- ReadStoreNodeExt() { this = TReadStoreNode(call, arg, f1, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = f1.getType() }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = "(inside) " + call.toString() + " [read " + f1 + "]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class ReadTaintNode extends NodeExt, TReadTaintNode {
- private ArgumentNode arg;
- private Content f;
- private Configuration config0;
-
- ReadTaintNode() { this = TReadTaintNode(arg, f, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = f.getType() }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = arg.toString() + " [read taint " + f + "]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class TaintStoreNode extends NodeExt, TTaintStoreNode {
- private ArgumentNode arg;
- private DataFlowType t;
- private Configuration config0;
-
- TaintStoreNode() { this = TTaintStoreNode(arg, t, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = t }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = arg.toString() + " [taint store]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private predicate additionalLocalFlowStepExt(
- NodeExt node1, NodeExt node2, DataFlowType t, Configuration config
-) {
- exists(ArgumentNode arg, Content f |
- node1 = TReadTaintNode(arg, f, config) and
- argumentFlowsThrough(arg, node2.getNode(), _, t, TSummaryReadTaint(f), config)
- )
- or
- node2 = TTaintStoreNode(node1.getNode(), t, config)
-}
-
-pragma[nomagic]
-private predicate readExt(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- read(node1.getNode(), f, node2.getNode(), config)
- or
- node2 = TReadStoreNode(_, node1.getNode(), f, config)
- or
- node2 = TReadTaintNode(node1.getNode(), f, config)
-}
-
-pragma[nomagic]
-private predicate storeExt(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- store(node1.getNode(), f, node2.getNode(), config)
- or
- exists(DataFlowCall call, ArgumentNode arg, Content f1, Node n2 |
- node1 = TReadStoreNode(call, arg, f1, config) and
- n2 = node2.getNode() and
- argumentValueFlowsThrough(call, arg, TContentSome(f1), TContentSome(f), n2) and
+private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
+ exists(TypedContent tc |
+ nodeCand1IsReadAndStored(c, config) and
nodeCand1(n2, unbind(config)) and
- readStoreCand1(f, unbind(config))
- )
- or
- exists(ArgumentNode arg, DataFlowType t |
- node1 = TTaintStoreNode(arg, t, config) and
- argumentFlowsThrough(arg, node2.getNode(), t, _, TSummaryTaintStore(f), config)
+ store(n1, tc, n2, _) and
+ c = tc.getContent()
)
}
-private predicate jumpStepExt(NodeExt node1, NodeExt node2, Configuration config) {
- jumpStep(node1.getNode(), node2.getNode(), config)
-}
-
-private predicate additionalJumpStepExt(NodeExt node1, NodeExt node2, Configuration config) {
- additionalJumpStep(node1.getNode(), node2.getNode(), config)
-}
-
-private predicate argumentValueFlowsThrough(NodeExt node1, NodeExt node2) {
- argumentValueFlowsThrough(_, node1.getNode(), TContentNone(), TContentNone(), node2.getNode())
-}
-
-private predicate argumentFlowsThrough(
- NodeExt arg, NodeExt out, DataFlowType t, Configuration config
-) {
- argumentFlowsThrough(arg.getNode(), out.getNode(), _, t, TSummaryTaint(), config)
-}
-
-/**
- * Holds if data can flow from `node1` to `node2` in one local step or a step
- * through a callable.
- */
-pragma[noinline]
-private predicate localFlowStepOrFlowThroughCallable(
- NodeExt node1, NodeExt node2, Configuration config
-) {
- exists(Node n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode()
- |
- nodeCand1(n1, config) and
- localFlowStep(n1, n2, config)
- or
- nodeCand1(n1, config) and
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentNone(), n2)
- )
-}
-
-/**
- * Holds if data can flow from `node1` to `node2` in one local step or a step
- * through a callable, in both cases using an additional flow step from the
- * configuration.
- */
-pragma[noinline]
-private predicate additionalLocalFlowStepOrFlowThroughCallable(
- NodeExt node1, NodeExt node2, Configuration config
-) {
- exists(Node n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode()
- |
- nodeCand1(n1, config) and
- additionalLocalFlowStep(n1, n2, config)
- or
- argumentFlowsThrough(n1, n2, _, _, TSummaryTaint(), config)
- )
- or
- additionalLocalFlowStepExt(node1, node2, _, config)
+pragma[nomagic]
+private predicate read(Node n1, Content c, Node n2, Configuration config) {
+ nodeCand1IsReadAndStored(c, config) and
+ nodeCand1(n2, unbind(config)) and
+ read(n1, c, n2)
}
pragma[noinline]
-private ReturnPosition getReturnPosition1(ReturnNodeExt node, Configuration config) {
- result = getReturnPosition(node) and
- nodeCand1(node, config)
+private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ localFlowStep(node1, node2, config)
+}
+
+pragma[noinline]
+private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ additionalLocalFlowStep(node1, node2, config)
+}
+
+pragma[nomagic]
+private predicate viableReturnPosOutNodeCand1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+) {
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, config)
}
/**
- * Holds if data can flow out of a callable from `node1` to `node2`, either
+ * Holds if data can flow out of `call` from `ret` to `out`, 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 flowOutOfCallableNodeCand1(ReturnNodeExt node1, Node node2, Configuration config) {
- nodeCand1(node2, config) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- exists(DataFlowCall call, ReturnKindExt kind |
- getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
- node2 = kind.getAnOutNode(call)
- )
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand1(
+ DataFlowCall call, ReturnNodeExt ret, Node out, Configuration config
+) {
+ viableReturnPosOutNodeCand1(call, getReturnPosition(ret), out, config) and
+ nodeCand1(ret, config) and
+ not outBarrier(ret, config) and
+ not inBarrier(out, config)
+}
+
+pragma[nomagic]
+private predicate viableParamArgNodeCand1(
+ DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+) {
+ viableParamArgNodeCandFwd1(call, p, arg, config) and
+ nodeCand1(arg, config)
}
/**
- * Holds if data can flow into a callable and that this step is part of a
+ * Holds if data can flow into `call` and that this step is part of a
* path from a source to a sink.
*/
-private predicate flowIntoCallableNodeCand1(
- ArgumentNode node1, ParameterNode node2, Configuration config
+pragma[nomagic]
+private predicate flowIntoCallNodeCand1(
+ DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config
) {
- viableParamArgCand(_, node2, node1, config)
+ viableParamArgNodeCand1(call, p, arg, config) and
+ nodeCand1(p, config) and
+ not outBarrier(arg, config) and
+ not inBarrier(p, config)
}
/**
@@ -999,7 +649,7 @@ private predicate flowIntoCallableNodeCand1(
private int branch(Node n1, Configuration conf) {
result =
strictcount(Node n |
- flowOutOfCallableNodeCand1(n1, n, conf) or flowIntoCallableNodeCand1(n1, n, conf)
+ flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1011,269 +661,391 @@ private int branch(Node n1, Configuration conf) {
private int join(Node n2, Configuration conf) {
result =
strictcount(Node n |
- flowOutOfCallableNodeCand1(n, n2, conf) or flowIntoCallableNodeCand1(n, n2, conf)
+ flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
/**
- * Holds if data can flow out of a callable from `node1` to `node2`, either
+ * Holds if data can flow out of `call` from `ret` to `out`, 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. The
* `allowsFieldFlow` flag indicates whether the branching is within the limit
* specified by the configuration.
*/
-private predicate flowOutOfCallableNodeCand1(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand1(
+ DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config
) {
- exists(ReturnNodeExt n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode() and
- flowOutOfCallableNodeCand1(n1, n2, config) and
- exists(int b, int j |
- b = branch(n1, config) and
- j = join(n2, config) and
- if b.minimum(j) <= config.fieldFlowBranchLimit()
- then allowsFieldFlow = true
- else allowsFieldFlow = false
- )
+ flowOutOfCallNodeCand1(call, ret, out, config) and
+ exists(int b, int j |
+ b = branch(ret, config) and
+ j = join(out, config) and
+ if b.minimum(j) <= config.fieldFlowBranchLimit()
+ then allowsFieldFlow = true
+ else allowsFieldFlow = false
)
}
/**
- * Holds if data can flow into a callable and that this step is part of a
+ * Holds if data can flow into `call` and that this step is part of a
* path from a source to a sink. The `allowsFieldFlow` flag indicates whether
* the branching is within the limit specified by the configuration.
*/
-private predicate flowIntoCallableNodeCand1(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+pragma[nomagic]
+private predicate flowIntoCallNodeCand1(
+ DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow,
+ Configuration config
) {
- exists(ArgumentNode n1, ParameterNode n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode() and
- flowIntoCallableNodeCand1(n1, n2, config) and
- exists(int b, int j |
- b = branch(n1, config) and
- j = join(n2, config) and
- if b.minimum(j) <= config.fieldFlowBranchLimit()
- then allowsFieldFlow = true
- else allowsFieldFlow = false
- )
+ flowIntoCallNodeCand1(call, arg, p, config) and
+ exists(int b, int j |
+ b = branch(arg, config) and
+ j = join(p, config) and
+ if b.minimum(j) <= config.fieldFlowBranchLimit()
+ then allowsFieldFlow = true
+ else allowsFieldFlow = false
)
}
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is reachable from a source in the configuration `config`.
+ * The Boolean `stored` records whether the tracked value is stored into a
+ * field of `node`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argStored` records whether the tracked
+ * value was stored into a field of the argument.
*/
-private predicate nodeCandFwd2(NodeExt node, boolean fromArg, boolean stored, Configuration config) {
- nodeCand1(node.getNode(), config) and
- config.isSource(node.getNode()) and
+private predicate nodeCandFwd2(
+ Node node, boolean fromArg, BooleanOption argStored, boolean stored, Configuration config
+) {
+ nodeCand1(node, config) and
+ config.isSource(node) and
fromArg = false and
+ argStored = TBooleanNone() and
stored = false
or
- node.isCand1(unbind(config)) and
+ nodeCand1(node, unbind(config)) and
(
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, stored, config) and
- localFlowStepOrFlowThroughCallable(mid, node, config)
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, stored, config) and
+ localFlowStepNodeCand1(mid, node, config)
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, stored, config) and
- additionalLocalFlowStepOrFlowThroughCallable(mid, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, stored, config) and
+ additionalLocalFlowStepNodeCand1(mid, node, config) and
stored = false
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, _, stored, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
+ exists(Node mid |
+ nodeCandFwd2(mid, _, _, stored, config) and
+ jumpStep(mid, node, config) and
+ fromArg = false and
+ argStored = TBooleanNone()
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, _, stored, config) and
- additionalJumpStepExt(mid, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, _, _, stored, config) and
+ additionalJumpStep(mid, node, config) and
fromArg = false and
+ argStored = TBooleanNone() and
stored = false
)
or
// store
- exists(NodeExt mid, Content f |
- nodeCandFwd2(mid, fromArg, _, config) and
- storeExt(mid, f, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, _, config) and
+ storeCand1(mid, _, node, config) and
stored = true
)
or
// read
- exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, config) and
- storeCandFwd2(f, stored, config)
+ exists(Content c |
+ nodeCandFwd2Read(c, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(c, stored, config)
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- nodeCandFwd2(mid, _, stored, config) and
- flowIntoCallableNodeCand1(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (stored = false or allowsFieldFlow = true)
- )
+ // flow into a callable
+ nodeCandFwd2In(_, node, _, _, stored, config) and
+ fromArg = true and
+ if parameterThroughFlowNodeCand1(node, config)
+ then argStored = TBooleanSome(stored)
+ else argStored = TBooleanNone()
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- nodeCandFwd2(mid, false, stored, config) and
- flowOutOfCallableNodeCand1(mid, node, allowsFieldFlow, config) and
- fromArg = false and
- (stored = false or allowsFieldFlow = true)
+ // flow out of a callable
+ exists(DataFlowCall call |
+ nodeCandFwd2Out(call, node, fromArg, argStored, stored, config) and
+ fromArg = false
+ or
+ exists(boolean argStored0 |
+ nodeCandFwd2OutFromArg(call, node, argStored0, stored, config) and
+ nodeCandFwd2IsEntered(call, fromArg, argStored, argStored0, config)
+ )
)
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
*/
pragma[noinline]
-private predicate storeCandFwd2(Content f, boolean stored, Configuration config) {
- exists(NodeExt mid, NodeExt node |
+private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
+ exists(Node mid, Node node |
useFieldFlow(config) and
- node.isCand1(unbind(config)) and
- nodeCandFwd2(mid, _, stored, config) and
- storeExt(mid, f, node, config)
+ nodeCand1(node, unbind(config)) and
+ nodeCandFwd2(mid, _, _, stored, config) and
+ storeCand1(mid, c, node, config)
)
}
pragma[nomagic]
-private predicate nodeCandFwd2Read(Content f, NodeExt node, boolean fromArg, Configuration config) {
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, true, config) and
- readExt(mid, f, node, config)
+private predicate nodeCandFwd2Read(
+ Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+) {
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, true, config) and
+ read(mid, c, node, config)
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2In(
+ DataFlowCall call, ParameterNode p, boolean fromArg, BooleanOption argStored, boolean stored,
+ Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ nodeCandFwd2(arg, fromArg, argStored, stored, config) and
+ flowIntoCallNodeCand1(call, arg, p, allowsFieldFlow, config)
+ |
+ stored = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2Out(
+ DataFlowCall call, Node out, boolean fromArg, BooleanOption argStored, boolean stored,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ nodeCandFwd2(ret, fromArg, argStored, stored, config) and
+ flowOutOfCallNodeCand1(call, ret, out, allowsFieldFlow, config)
+ |
+ stored = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2OutFromArg(
+ DataFlowCall call, Node out, boolean argStored, boolean stored, Configuration config
+) {
+ nodeCandFwd2Out(call, out, true, TBooleanSome(argStored), stored, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `nodeCandFwd2`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd2IsEntered(
+ DataFlowCall call, boolean fromArg, BooleanOption argStored, boolean stored, Configuration config
+) {
+ exists(ParameterNode p |
+ nodeCandFwd2In(call, p, fromArg, argStored, stored, config) and
+ parameterThroughFlowNodeCand1(p, config)
)
}
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is part of a path from a source to a sink in the
+ * configuration `config`. The Boolean `read` records whether the tracked
+ * value must be read from a field of `node` in order to reach a sink.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnRead`
+ * records whether a field must be read from the returned value.
*/
-private predicate nodeCand2(NodeExt node, boolean toReturn, boolean read, Configuration config) {
- nodeCandFwd2(node, _, false, config) and
- config.isSink(node.getNode()) and
+private predicate nodeCand2(
+ Node node, boolean toReturn, BooleanOption returnRead, boolean read, Configuration config
+) {
+ nodeCandFwd2(node, _, _, false, config) and
+ config.isSink(node) and
toReturn = false and
+ returnRead = TBooleanNone() and
read = false
or
- nodeCandFwd2(node, _, unbindBool(read), unbind(config)) and
+ nodeCandFwd2(node, _, _, unbindBool(read), unbind(config)) and
(
- exists(NodeExt mid |
- localFlowStepOrFlowThroughCallable(node, mid, config) and
- nodeCand2(mid, toReturn, read, config)
+ exists(Node mid |
+ localFlowStepNodeCand1(node, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, read, config)
)
or
- exists(NodeExt mid |
- additionalLocalFlowStepOrFlowThroughCallable(node, mid, config) and
- nodeCand2(mid, toReturn, read, config) and
+ exists(Node mid |
+ additionalLocalFlowStepNodeCand1(node, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, read, config) and
read = false
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- nodeCand2(mid, _, read, config) and
- toReturn = false
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ nodeCand2(mid, _, _, read, config) and
+ toReturn = false and
+ returnRead = TBooleanNone()
)
or
- exists(NodeExt mid |
- additionalJumpStepExt(node, mid, config) and
- nodeCand2(mid, _, read, config) and
+ exists(Node mid |
+ additionalJumpStep(node, mid, config) and
+ nodeCand2(mid, _, _, read, config) and
toReturn = false and
+ returnRead = TBooleanNone() and
read = false
)
or
// store
- exists(Content f |
- nodeCand2Store(f, node, toReturn, read, config) and
- readCand2(f, read, config)
+ exists(Content c |
+ nodeCand2Store(c, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(c, read, config)
)
or
// read
- exists(NodeExt mid, Content f, boolean read0 |
- readExt(node, f, mid, config) and
- storeCandFwd2(f, unbindBool(read0), unbind(config)) and
- nodeCand2(mid, toReturn, read0, config) and
+ exists(Node mid, Content c, boolean read0 |
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
+ nodeCand2(mid, toReturn, returnRead, read0, config) and
read = true
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand1(node, mid, allowsFieldFlow, config) and
- nodeCand2(mid, false, read, config) and
- toReturn = false and
- (read = false or allowsFieldFlow = true)
+ // flow into a callable
+ exists(DataFlowCall call |
+ nodeCand2In(call, node, toReturn, returnRead, read, config) and
+ toReturn = false
+ or
+ exists(boolean returnRead0 |
+ nodeCand2InToReturn(call, node, returnRead0, read, config) and
+ nodeCand2IsReturned(call, toReturn, returnRead, returnRead0, config)
+ )
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand1(node, mid, allowsFieldFlow, config) and
- nodeCand2(mid, _, read, config) and
- toReturn = true and
- (read = false or allowsFieldFlow = true)
- )
+ // flow out of a callable
+ nodeCand2Out(_, node, _, _, read, config) and
+ toReturn = true and
+ if nodeCandFwd2(node, true, TBooleanSome(_), unbindBool(read), config)
+ then returnRead = TBooleanSome(read)
+ else returnRead = TBooleanNone()
)
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate readCand2(Content f, boolean read, Configuration config) {
- exists(NodeExt mid, NodeExt node |
+private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
+ exists(Node mid, Node node |
useFieldFlow(config) and
- nodeCandFwd2(node, _, true, unbind(config)) and
- readExt(node, f, mid, config) and
- storeCandFwd2(f, unbindBool(read), unbind(config)) and
- nodeCand2(mid, _, read, config)
+ nodeCandFwd2(node, _, _, true, unbind(config)) and
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
+ nodeCand2(mid, _, _, read, config)
)
}
pragma[nomagic]
private predicate nodeCand2Store(
- Content f, NodeExt node, boolean toReturn, boolean stored, Configuration config
+ Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
+ Configuration config
) {
- exists(NodeExt mid |
- storeExt(node, f, mid, config) and
- nodeCand2(mid, toReturn, true, config) and
- nodeCandFwd2(node, _, stored, unbind(config))
- )
-}
-
-pragma[nomagic]
-private predicate storeCand2(Content f, boolean stored, Configuration conf) {
- exists(NodeExt node |
- nodeCand2Store(f, node, _, stored, conf) and
- nodeCand2(node, _, stored, conf)
+ exists(Node mid |
+ storeCand1(node, c, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, true, config) and
+ nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
/**
- * Holds if `f` is the target of both a store and a read in the path graph
- * covered by `nodeCand2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
*/
-pragma[noinline]
-private predicate readStoreCand(Content f, Configuration conf) {
- exists(boolean apNonEmpty |
- storeCand2(f, apNonEmpty, conf) and
- readCand2(f, apNonEmpty, conf)
+pragma[nomagic]
+private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
+ exists(Node node |
+ nodeCand2Store(c, node, _, _, stored, conf) and
+ nodeCand2(node, _, _, stored, conf)
)
}
-private predicate nodeCand2(NodeExt node, Configuration config) { nodeCand2(node, _, _, config) }
+/**
+ * Holds if `c` is the target of both a store and a read in the path graph
+ * covered by `nodeCand2`.
+ */
+pragma[noinline]
+private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
+ exists(boolean apNonEmpty |
+ nodeCand2IsStored(c, apNonEmpty, conf) and
+ nodeCand2IsRead(c, apNonEmpty, conf)
+ )
+}
pragma[nomagic]
-private predicate flowOutOfCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate nodeCand2Out(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
) {
- flowOutOfCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ exists(Node out, boolean allowsFieldFlow |
+ nodeCand2(out, toReturn, returnRead, read, config) and
+ flowOutOfCallNodeCand1(call, ret, out, allowsFieldFlow, config)
+ |
+ read = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCand2In(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ nodeCand2(p, toReturn, returnRead, read, config) and
+ flowIntoCallNodeCand1(call, arg, p, allowsFieldFlow, config)
+ |
+ read = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCand2InToReturn(
+ DataFlowCall call, ArgumentNode arg, boolean returnRead, boolean read, Configuration config
+) {
+ nodeCand2In(call, arg, true, TBooleanSome(returnRead), read, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `nodeCand2`.
+ */
+pragma[nomagic]
+private predicate nodeCand2IsReturned(
+ DataFlowCall call, boolean toReturn, BooleanOption returnRead, boolean read, Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ nodeCand2Out(call, ret, toReturn, returnRead, read, config) and
+ nodeCandFwd2(ret, true, TBooleanSome(_), read, config)
+ )
+}
+
+private predicate nodeCand2(Node node, Configuration config) { nodeCand2(node, _, _, _, config) }
+
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand2(
+ DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
+) {
+ flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
nodeCand2(node2, config) and
nodeCand2(node1, unbind(config))
}
pragma[nomagic]
-private predicate flowIntoCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate flowIntoCallNodeCand2(
+ DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ Configuration config
) {
- flowIntoCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
nodeCand2(node2, config) and
nodeCand2(node1, unbind(config))
}
@@ -1284,15 +1056,15 @@ private module LocalFlowBigStep {
* flow steps in a dataflow path.
*/
private predicate localFlowEntry(Node node, Configuration config) {
- nodeCand2(TNormalNode(node), config) and
+ nodeCand2(node, config) and
(
config.isSource(node) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
node instanceof ParameterNode or
- node instanceof OutNode or
- node instanceof PostUpdateNode or
- readDirect(_, _, node) or
+ node instanceof OutNodeExt or
+ store(_, _, node, _) or
+ read(_, _, node) or
node instanceof CastNode
)
}
@@ -1302,15 +1074,13 @@ private module LocalFlowBigStep {
* flow steps in a dataflow path.
*/
private predicate localFlowExit(Node node, Configuration config) {
- exists(Node next | nodeCand2(TNormalNode(next), config) |
+ exists(Node next | nodeCand2(next, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
- flowIntoCallableNodeCand1(node, next, config) or
- flowOutOfCallableNodeCand1(node, next, config) or
- argumentFlowsThrough(node, next, _, _, _, config) or
- argumentValueFlowsThrough(_, node, TContentNone(), TContentNone(), next) or
- storeDirect(node, _, next) or
- readDirect(node, _, next)
+ flowIntoCallNodeCand1(_, node, next, config) or
+ flowOutOfCallNodeCand1(_, node, next, config) or
+ store(node, _, next, _) or
+ read(node, _, next)
)
or
node instanceof CastNode
@@ -1318,6 +1088,13 @@ private module LocalFlowBigStep {
config.isSink(node)
}
+ pragma[noinline]
+ private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
+ additionalLocalFlowStepNodeCand1(node1, node2, config) and
+ nodeCand2(node1, _, _, false, config) and
+ nodeCand2(node2, _, _, false, unbind(config))
+ }
+
/**
* Holds if the local path from `node1` to `node2` is a prefix of a maximal
* subsequence of local flow steps in a dataflow path.
@@ -1334,33 +1111,33 @@ private module LocalFlowBigStep {
(
localFlowEntry(node1, config) and
(
- localFlowStep(node1, node2, config) and
+ localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
t = getErasedNodeTypeBound(node1)
or
- additionalLocalFlowStep(node1, node2, config) and
+ additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
t = getErasedNodeTypeBound(node2)
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
or
exists(Node mid |
localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
- localFlowStep(mid, node2, config) and
+ localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof CastNode and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
)
or
exists(Node mid |
localFlowStepPlus(node1, mid, _, _, config, cc) and
- additionalLocalFlowStep(mid, node2, config) and
+ additionalLocalFlowStepNodeCand2(mid, node2, config) and
not mid instanceof CastNode and
preservesValue = false and
t = getErasedNodeTypeBound(node2) and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
)
)
}
@@ -1371,311 +1148,380 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config,
+ Node node1, Node node2, boolean preservesValue, AccessPathFrontNil apf, Configuration config,
LocalCallContext callContext
) {
- localFlowStepPlus(node1, node2, preservesValue, t, config, callContext) and
+ localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, config)
}
-
- pragma[nomagic]
- predicate localFlowBigStepExt(
- NodeExt node1, NodeExt node2, boolean preservesValue, AccessPathFrontNil apf,
- Configuration config
- ) {
- localFlowBigStep(node1.getNode(), node2.getNode(), preservesValue, apf.getType(), config, _)
- or
- additionalLocalFlowStepExt(node1, node2, apf.getType(), config) and
- nodeCand2(node1, config) and
- nodeCand2(node2, unbind(config)) and
- preservesValue = false
- }
}
private import LocalFlowBigStep
pragma[nomagic]
-private predicate readExtCand2(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- readExt(node1, f, node2, config) and
- nodeCand2(node1, _, true, unbind(config)) and
+private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
+ read(node1, c, node2, config) and
+ nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- readStoreCand(f, unbind(config))
+ nodeCand2IsReadAndStored(c, unbind(config))
}
pragma[nomagic]
-private predicate storeExtCand2(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- storeExt(node1, f, node2, config) and
+private predicate storeCand2(
+ Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+) {
+ store(node1, tc, node2, contentType) and
nodeCand2(node1, config) and
- nodeCand2(node2, _, true, unbind(config)) and
- readStoreCand(f, unbind(config))
-}
-
-private newtype TAccessPathFront =
- TFrontNil(DataFlowType t) or
- TFrontHead(Content f)
-
-/**
- * The front of an `AccessPath`. This is either a head or a nil.
- */
-abstract private class AccessPathFront extends TAccessPathFront {
- abstract string toString();
-
- abstract DataFlowType getType();
-
- abstract boolean toBoolNonEmpty();
-
- predicate headUsesContent(Content f) { this = TFrontHead(f) }
-}
-
-private class AccessPathFrontNil extends AccessPathFront, TFrontNil {
- override string toString() {
- exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
- }
-
- override DataFlowType getType() { this = TFrontNil(result) }
-
- override boolean toBoolNonEmpty() { result = false }
-}
-
-private class AccessPathFrontHead extends AccessPathFront, TFrontHead {
- override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) }
-
- override DataFlowType getType() {
- exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
- }
-
- override boolean toBoolNonEmpty() { result = true }
+ nodeCand2(node2, _, _, true, unbind(config)) and
+ nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
}
/**
- * Holds if data can flow from a source to `node` with the given `apf`.
+ * Holds if `node` is reachable with access path front `apf` from a
+ * source in the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argApf` records the front of the
+ * access path of that argument.
*/
pragma[nomagic]
private predicate flowCandFwd(
- NodeExt node, boolean fromArg, AccessPathFront apf, Configuration config
+ Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
) {
- flowCandFwd0(node, fromArg, apf, config) and
- if node instanceof CastingNodeExt
- then compatibleTypes(node.getErasedNodeTypeBound(), apf.getType())
+ flowCandFwd0(node, fromArg, argApf, apf, config) and
+ if node instanceof CastingNode
+ then compatibleTypes(getErasedNodeTypeBound(node), apf.getType())
else any()
}
pragma[nomagic]
private predicate flowCandFwd0(
- NodeExt node, boolean fromArg, AccessPathFront apf, Configuration config
+ Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
) {
- nodeCand2(node, _, false, config) and
- config.isSource(node.getNode()) and
+ nodeCand2(node, _, _, false, config) and
+ config.isSource(node) and
fromArg = false and
- apf = TFrontNil(node.getErasedNodeTypeBound())
+ argApf = TAccessPathFrontNone() and
+ apf = TFrontNil(getErasedNodeTypeBound(node))
or
- exists(NodeExt mid |
- flowCandFwd(mid, fromArg, apf, config) and
- localFlowBigStepExt(mid, node, true, _, config)
+ exists(Node mid |
+ flowCandFwd(mid, fromArg, argApf, apf, config) and
+ localFlowBigStep(mid, node, true, _, config, _)
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(mid, fromArg, nil, config) and
- localFlowBigStepExt(mid, node, false, apf, config)
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(mid, fromArg, argApf, nil, config) and
+ localFlowBigStep(mid, node, false, apf, config, _)
)
or
- nodeCand2(node, unbind(config)) and
- (
- exists(NodeExt mid |
- flowCandFwd(mid, _, apf, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(mid, _, nil, config) and
- additionalJumpStepExt(mid, node, config) and
- fromArg = false and
- apf = TFrontNil(node.getErasedNodeTypeBound())
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowCandFwd(mid, _, apf, config) and
- flowIntoCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowCandFwd(mid, false, apf, config) and
- flowOutOfCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = false and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- flowCandFwd(mid, fromArg, apf, config) and
- argumentValueFlowsThrough(mid, node)
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil, DataFlowType t |
- flowCandFwd(mid, fromArg, nil, config) and
- argumentFlowsThrough(mid, node, t, config) and
- apf = TFrontNil(t)
- )
+ exists(Node mid |
+ flowCandFwd(mid, _, _, apf, config) and
+ nodeCand2(node, unbind(config)) and
+ jumpStep(mid, node, config) and
+ fromArg = false and
+ argApf = TAccessPathFrontNone()
)
or
- exists(NodeExt mid, Content f |
- flowCandFwd(mid, fromArg, _, config) and
- storeExtCand2(mid, f, node, config) and
- nodeCand2(node, _, true, unbind(config)) and
- apf.headUsesContent(f)
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(mid, _, _, nil, config) and
+ nodeCand2(node, unbind(config)) and
+ additionalJumpStep(mid, node, config) and
+ fromArg = false and
+ argApf = TAccessPathFrontNone() and
+ apf = TFrontNil(getErasedNodeTypeBound(node))
)
or
- exists(Content f |
- flowCandFwdRead(f, node, fromArg, config) and
- consCandFwd(f, apf, config) and
- nodeCand2(node, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ // store
+ exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ storeCand2(mid, tc, node, contentType, config) and
+ nodeCand2(node, _, _, true, unbind(config)) and
+ apf.headUsesContent(tc) and
+ compatibleTypes(apf0.getType(), contentType)
+ )
+ or
+ // read
+ exists(TypedContent tc |
+ flowCandFwdRead(tc, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(tc, apf, config) and
+ nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ )
+ or
+ // flow into a callable
+ flowCandFwdIn(_, node, _, _, apf, config) and
+ fromArg = true and
+ if nodeCand2(node, true, _, unbindBool(apf.toBoolNonEmpty()), config)
+ then argApf = TAccessPathFrontSome(apf)
+ else argApf = TAccessPathFrontNone()
+ or
+ // flow out of a callable
+ exists(DataFlowCall call |
+ flowCandFwdOut(call, node, fromArg, argApf, apf, config) and
+ fromArg = false
+ or
+ exists(AccessPathFront argApf0 |
+ flowCandFwdOutFromArg(call, node, argApf0, apf, config) and
+ flowCandFwdIsEntered(call, fromArg, argApf, argApf0, config)
+ )
)
}
pragma[nomagic]
-private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) {
- exists(NodeExt mid, NodeExt n |
- flowCandFwd(mid, _, apf, config) and
- storeExtCand2(mid, f, n, config) and
- nodeCand2(n, _, true, unbind(config)) and
- compatibleTypes(apf.getType(), f.getType())
+private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n, DataFlowType contentType |
+ flowCandFwd(mid, _, _, apf, config) and
+ storeCand2(mid, tc, n, contentType, config) and
+ nodeCand2(n, _, _, true, unbind(config)) and
+ compatibleTypes(apf.getType(), contentType)
)
}
pragma[nomagic]
-private predicate flowCandFwdRead(Content f, NodeExt node, boolean fromArg, Configuration config) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowCandFwd(mid, fromArg, apf0, config) and
- readExtCand2(mid, f, node, config) and
- apf0.headUsesContent(f)
+private predicate flowCandFwdRead0(
+ Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFrontHead apf, Configuration config
+) {
+ flowCandFwd(node1, fromArg, argApf, apf, config) and
+ readCand2(node1, c, node2, config) and
+ apf.headUsesContent(tc)
+}
+
+pragma[nomagic]
+private predicate flowCandFwdRead(
+ TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+) {
+ flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
+}
+
+pragma[nomagic]
+private predicate flowCandFwdIn(
+ DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ flowCandFwd(arg, fromArg, argApf, apf, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ flowCandFwd(ret, fromArg, argApf, apf, config) and
+ flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandFwdOutFromArg(
+ DataFlowCall call, Node node, AccessPathFront argApf, AccessPathFront apf, Configuration config
+) {
+ flowCandFwdOut(call, node, true, TAccessPathFrontSome(argApf), apf, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `flowCandFwd`.
+ */
+pragma[nomagic]
+private predicate flowCandFwdIsEntered(
+ DataFlowCall call, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ParameterNode p |
+ flowCandFwdIn(call, p, fromArg, argApf, apf, config) and
+ nodeCand2(p, true, TBooleanSome(_), unbindBool(apf.toBoolNonEmpty()), config)
)
}
/**
- * Holds if data can flow from a source to `node` with the given `apf` and
- * from there flow to a sink.
+ * Holds if `node` with access path front `apf` is part of a path from a
+ * source to a sink in the configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnApf`
+ * records the front of the access path of the returned value.
*/
pragma[nomagic]
-private predicate flowCand(NodeExt node, boolean toReturn, AccessPathFront apf, Configuration config) {
- flowCand0(node, toReturn, apf, config) and
- flowCandFwd(node, _, apf, config)
+private predicate flowCand(
+ Node node, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ flowCand0(node, toReturn, returnApf, apf, config) and
+ flowCandFwd(node, _, _, apf, config)
}
pragma[nomagic]
private predicate flowCand0(
- NodeExt node, boolean toReturn, AccessPathFront apf, Configuration config
+ Node node, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
) {
- flowCandFwd(node, _, apf, config) and
- config.isSink(node.getNode()) and
+ flowCandFwd(node, _, _, apf, config) and
+ config.isSink(node) and
toReturn = false and
+ returnApf = TAccessPathFrontNone() and
apf instanceof AccessPathFrontNil
or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flowCand(mid, toReturn, apf, config)
+ exists(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flowCand(mid, toReturn, returnApf, apf, config)
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- localFlowBigStepExt(node, mid, false, _, config) and
- flowCand(mid, toReturn, nil, config) and
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(node, _, _, apf, config) and
+ localFlowBigStep(node, mid, false, _, config, _) and
+ flowCand(mid, toReturn, returnApf, nil, config) and
apf instanceof AccessPathFrontNil
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flowCand(mid, _, apf, config) and
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flowCand(mid, _, _, apf, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone()
+ )
+ or
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(node, _, _, apf, config) and
+ additionalJumpStep(node, mid, config) and
+ flowCand(mid, _, _, nil, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone() and
+ apf instanceof AccessPathFrontNil
+ )
+ or
+ // store
+ exists(TypedContent tc |
+ flowCandStore(node, tc, apf, toReturn, returnApf, config) and
+ flowCandConsCand(tc, apf, config)
+ )
+ or
+ // read
+ exists(TypedContent tc, AccessPathFront apf0 |
+ flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(tc, apf0, config)
+ )
+ or
+ // flow into a callable
+ exists(DataFlowCall call |
+ flowCandIn(call, node, toReturn, returnApf, apf, config) and
toReturn = false
+ or
+ exists(AccessPathFront returnApf0 |
+ flowCandInToReturn(call, node, returnApf0, apf, config) and
+ flowCandIsReturned(call, toReturn, returnApf, returnApf0, config)
+ )
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- additionalJumpStepExt(node, mid, config) and
- flowCand(mid, _, nil, config) and
- toReturn = false and
- apf instanceof AccessPathFrontNil
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flowCand(mid, false, apf, config) and
- toReturn = false and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flowCand(mid, _, apf, config) and
- toReturn = true and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- argumentValueFlowsThrough(node, mid) and
- flowCand(mid, toReturn, apf, config)
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil |
- argumentFlowsThrough(node, mid, _, config) and
- flowCand(mid, toReturn, nil, config) and
- apf instanceof AccessPathFrontNil and
- flowCandFwd(node, _, apf, config)
- )
- or
- exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, apf0, config) and
- apf0.headUsesContent(f) and
- consCand(f, apf, config)
- )
- or
- exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, config) and
- apf.headUsesContent(f)
- )
+ // flow out of a callable
+ flowCandOut(_, node, _, _, apf, config) and
+ toReturn = true and
+ if flowCandFwd(node, true, _, apf, config)
+ then returnApf = TAccessPathFrontSome(apf)
+ else returnApf = TAccessPathFrontNone()
+}
+
+pragma[nomagic]
+private predicate readCandFwd(
+ Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
+) {
+ flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
}
pragma[nomagic]
private predicate flowCandRead(
- NodeExt node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
) {
- exists(NodeExt mid |
- readExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ readCandFwd(node, tc, apf, mid, config) and
+ flowCand(mid, toReturn, returnApf, apf0, config)
)
}
pragma[nomagic]
private predicate flowCandStore(
- NodeExt node, Content f, boolean toReturn, AccessPathFrontHead apf0, Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, Configuration config
) {
- exists(NodeExt mid |
- storeExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ flowCandFwd(node, _, _, apf, config) and
+ storeCand2(node, tc, mid, _, unbind(config)) and
+ flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
)
}
pragma[nomagic]
-private predicate consCand(Content f, AccessPathFront apf, Configuration config) {
- consCandFwd(f, apf, config) and
- exists(NodeExt n, AccessPathFrontHead apf0 |
- flowCandFwd(n, _, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandRead(n, f, _, apf, config)
+private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(tc, apf, config) and
+ flowCandRead(_, tc, _, _, _, apf, config)
+}
+
+pragma[nomagic]
+private predicate flowCandOut(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathFrontOption returnApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(Node out, boolean allowsFieldFlow |
+ flowCand(out, toReturn, returnApf, apf, config) and
+ flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandIn(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathFrontOption returnApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ flowCand(p, toReturn, returnApf, apf, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandInToReturn(
+ DataFlowCall call, ArgumentNode arg, AccessPathFront returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ flowCandIn(call, arg, true, TAccessPathFrontSome(returnApf), apf, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `flowCand`.
+ */
+pragma[nomagic]
+private predicate flowCandIsReturned(
+ DataFlowCall call, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ flowCandOut(call, ret, toReturn, returnApf, apf, config) and
+ flowCandFwd(ret, true, TAccessPathFrontSome(_), apf, config)
)
}
private newtype TAccessPath =
TNil(DataFlowType t) or
- TConsNil(Content f, DataFlowType t) { consCand(f, TFrontNil(t), _) } or
- TConsCons(Content f1, Content f2, int len) {
- consCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
+ TConsCons(TypedContent tc1, TypedContent tc2, int len) {
+ flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
}
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first two
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
* elements of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -1684,7 +1530,7 @@ private newtype TAccessPath =
abstract private class AccessPath extends TAccessPath {
abstract string toString();
- abstract Content getHead();
+ abstract TypedContent getHead();
abstract int len();
@@ -1695,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
/**
* Holds if this access path has `head` at the front and may be followed by `tail`.
*/
- abstract predicate pop(Content head, AccessPath tail);
+ abstract predicate pop(TypedContent head, AccessPath tail);
}
private class AccessPathNil extends AccessPath, TNil {
@@ -1705,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
override string toString() { result = concat(": " + ppReprType(t)) }
- override Content getHead() { none() }
+ override TypedContent getHead() { none() }
override int len() { result = 0 }
@@ -1713,357 +1559,469 @@ private class AccessPathNil extends AccessPath, TNil {
override AccessPathFront getFront() { result = TFrontNil(t) }
- override predicate pop(Content head, AccessPath tail) { none() }
+ override predicate pop(TypedContent head, AccessPath tail) { none() }
}
abstract private class AccessPathCons extends AccessPath { }
private class AccessPathConsNil extends AccessPathCons, TConsNil {
- private Content f;
+ private TypedContent tc;
private DataFlowType t;
- AccessPathConsNil() { this = TConsNil(f, t) }
+ AccessPathConsNil() { this = TConsNil(tc, t) }
override string toString() {
// The `concat` becomes "" if `ppReprType` has no result.
- result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
+ result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
}
- override Content getHead() { result = f }
+ override TypedContent getHead() { result = tc }
override int len() { result = 1 }
- override DataFlowType getType() { result = f.getContainerType() }
+ override DataFlowType getType() { result = tc.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f) }
+ override AccessPathFront getFront() { result = TFrontHead(tc) }
- override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
+ override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
}
private class AccessPathConsCons extends AccessPathCons, TConsCons {
- private Content f1;
- private Content f2;
+ private TypedContent tc1;
+ private TypedContent tc2;
private int len;
- AccessPathConsCons() { this = TConsCons(f1, f2, len) }
+ AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
override string toString() {
if len = 2
- then result = "[" + f1.toString() + ", " + f2.toString() + "]"
- else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
+ else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
}
- override Content getHead() { result = f1 }
+ override TypedContent getHead() { result = tc1 }
override int len() { result = len }
- override DataFlowType getType() { result = f1.getContainerType() }
+ override DataFlowType getType() { result = tc1.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f1) }
+ override AccessPathFront getFront() { result = TFrontHead(tc1) }
- override predicate pop(Content head, AccessPath tail) {
- head = f1 and
+ override predicate pop(TypedContent head, AccessPath tail) {
+ head = tc1 and
(
- tail = TConsCons(f2, _, len - 1)
+ tail = TConsCons(tc2, _, len - 1)
or
len = 2 and
- tail = TConsNil(f2, _)
+ tail = TConsNil(tc2, _)
)
}
}
-/** Gets the access path obtained by popping `f` from `ap`, if any. */
-private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
+/** Gets the access path obtained by popping `tc` from `ap`, if any. */
+private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
-/** Gets the access path obtained by pushing `f` onto `ap`. */
-private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
+/** Gets the access path obtained by pushing `tc` onto `ap`. */
+private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
+
+private newtype TAccessPathOption =
+ TAccessPathNone() or
+ TAccessPathSome(AccessPath ap)
+
+private class AccessPathOption extends TAccessPathOption {
+ string toString() {
+ this = TAccessPathNone() and result = ""
+ or
+ this = TAccessPathSome(any(AccessPath ap | result = ap.toString()))
+ }
+}
/**
- * Holds if data can flow from a source to `node` with the given `ap`.
+ * Holds if `node` is reachable with access path `ap` from a source in
+ * the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argAp` records the access path of that
+ * argument.
*/
private predicate flowFwd(
- NodeExt node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config
+ Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
) {
- flowFwd0(node, fromArg, apf, ap, config) and
- flowCand(node, _, apf, config)
+ flowFwd0(node, fromArg, argAp, apf, ap, config) and
+ flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
- NodeExt node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config
+ Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
) {
- flowCand(node, _, _, config) and
- config.isSource(node.getNode()) and
+ flowCand(node, _, _, _, config) and
+ config.isSource(node) and
fromArg = false and
- ap = TNil(node.getErasedNodeTypeBound()) and
+ argAp = TAccessPathNone() and
+ ap = TNil(getErasedNodeTypeBound(node)) and
apf = ap.(AccessPathNil).getFront()
or
- flowCand(node, _, _, unbind(config)) and
+ flowCand(node, _, _, _, unbind(config)) and
(
- exists(NodeExt mid |
- flowFwd(mid, fromArg, apf, ap, config) and
- localFlowBigStepExt(mid, node, true, _, config)
+ exists(Node mid |
+ flowFwd(mid, fromArg, argAp, apf, ap, config) and
+ localFlowBigStep(mid, node, true, _, config, _)
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(mid, fromArg, _, nil, config) and
- localFlowBigStepExt(mid, node, false, apf, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(mid, fromArg, argAp, _, nil, config) and
+ localFlowBigStep(mid, node, false, apf, config, _) and
apf = ap.(AccessPathNil).getFront()
)
or
- exists(NodeExt mid |
- flowFwd(mid, _, apf, ap, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
- )
- or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(mid, _, _, nil, config) and
- additionalJumpStepExt(mid, node, config) and
+ exists(Node mid |
+ flowFwd(mid, _, _, apf, ap, config) and
+ jumpStep(mid, node, config) and
fromArg = false and
- ap = TNil(node.getErasedNodeTypeBound()) and
- apf = ap.(AccessPathNil).getFront()
+ argAp = TAccessPathNone()
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowFwd(mid, _, apf, ap, config) and
- flowIntoCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowFwd(mid, false, apf, ap, config) and
- flowOutOfCallableNodeCand2(mid, node, allowsFieldFlow, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(mid, _, _, _, nil, config) and
+ additionalJumpStep(mid, node, config) and
fromArg = false and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- flowFwd(mid, fromArg, apf, ap, config) and
- argumentValueFlowsThrough(mid, node)
- )
- or
- exists(NodeExt mid, AccessPathNil nil, DataFlowType t |
- flowFwd(mid, fromArg, _, nil, config) and
- argumentFlowsThrough(mid, node, t, config) and
- ap = TNil(t) and
+ argAp = TAccessPathNone() and
+ ap = TNil(getErasedNodeTypeBound(node)) and
apf = ap.(AccessPathNil).getFront()
)
)
or
- exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, config) and
- ap = push(f, ap0)
+ // store
+ exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
+ or
+ // read
+ exists(TypedContent tc |
+ flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
+ flowFwdConsCand(tc, apf, ap, config)
)
or
- exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, config) and
- flowConsCandFwd(f, apf, ap, config)
+ // flow into a callable
+ flowFwdIn(_, node, _, _, apf, ap, config) and
+ fromArg = true and
+ if flowCand(node, true, _, apf, config)
+ then argAp = TAccessPathSome(ap)
+ else argAp = TAccessPathNone()
+ or
+ // flow out of a callable
+ exists(DataFlowCall call |
+ flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
+ fromArg = false
+ or
+ exists(AccessPath argAp0 |
+ flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and
+ flowFwdIsEntered(call, fromArg, argAp, argAp0, config)
+ )
)
}
pragma[nomagic]
private predicate flowFwdStore(
- NodeExt node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
- Configuration config
+ Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFront apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- flowFwdStore1(mid, f, node, apf0, apf, config)
+ exists(Node mid, AccessPathFront apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
+ flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
pragma[nomagic]
-private predicate flowFwdStore0(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, Configuration config
+private predicate storeCand(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
+ Configuration config
) {
- storeExtCand2(mid, f, node, config) and
- flowCand(mid, _, apf0, config)
+ storeCand2(mid, tc, node, _, config) and
+ flowCand(mid, _, _, apf0, config) and
+ apf.headUsesContent(tc)
}
pragma[noinline]
-private predicate flowFwdStore1(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, AccessPathFrontHead apf,
+private predicate flowFwdStore0(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
- flowFwdStore0(mid, f, node, apf0, config) and
- consCand(f, apf0, config) and
- apf.headUsesContent(f) and
- flowCand(node, _, apf, unbind(config))
+ storeCand(mid, tc, node, apf0, apf, config) and
+ flowCandConsCand(tc, apf0, config) and
+ flowCand(node, _, _, apf, unbind(config))
+}
+
+pragma[nomagic]
+private predicate flowFwdRead0(
+ Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
+ boolean fromArg, AccessPathOption argAp, Configuration config
+) {
+ flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
+ readCandFwd(node1, tc, apf0, node2, config)
}
pragma[nomagic]
private predicate flowFwdRead(
- NodeExt node, Content f, AccessPath ap0, boolean fromArg, Configuration config
+ Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- readExtCand2(mid, f, node, config) and
- apf0.headUsesContent(f) and
- flowCand(node, _, _, unbind(config))
+ exists(Node mid, TypedContent tc |
+ flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
+ flowCand(node, _, _, apf, unbind(config)) and
+ flowCandConsCand(tc, apf, unbind(config))
)
}
pragma[nomagic]
-private predicate flowConsCandFwd(
- Content f, AccessPathFront apf, AccessPath ap, Configuration config
+private predicate flowFwdConsCand(
+ TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
) {
- exists(NodeExt n |
- flowFwd(n, _, apf, ap, config) and
- flowFwdStore1(n, f, _, apf, _, config)
+ exists(Node n |
+ flowFwd(n, _, _, apf, ap, config) and
+ flowFwdStore0(n, tc, _, apf, _, config)
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdIn(
+ DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ flowFwd(arg, fromArg, argAp, apf, ap, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
+ flowCand(p, _, _, _, unbind(config))
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ flowFwd(ret, fromArg, argAp, apf, ap, config) and
+ flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
+ flowCand(node, _, _, _, unbind(config))
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdOutFromArg(
+ DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
+) {
+ flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `flowFwd`.
+ */
+pragma[nomagic]
+private predicate flowFwdIsEntered(
+ DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
+) {
+ exists(ParameterNode p, AccessPathFront apf |
+ flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and
+ flowCand(p, true, TAccessPathFrontSome(_), apf, config)
)
}
/**
- * Holds if data can flow from a source to `node` with the given `ap` and
- * from there flow to a sink.
+ * Holds if `node` with access path `ap` is part of a path from a source to
+ * a sink in the configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnAp`
+ * records the access path of the returned value.
*/
-private predicate flow(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flow0(node, toReturn, ap, config) and
- flowFwd(node, _, _, ap, config)
+private predicate flow(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flow0(node, toReturn, returnAp, ap, config) and
+ flowFwd(node, _, _, _, ap, config)
}
-private predicate flow0(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flowFwd(node, _, _, ap, config) and
- config.isSink(node.getNode()) and
+private predicate flow0(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flowFwd(node, _, _, _, ap, config) and
+ config.isSink(node) and
toReturn = false and
+ returnAp = TAccessPathNone() and
ap instanceof AccessPathNil
or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flow(mid, toReturn, ap, config)
+ exists(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flow(mid, toReturn, returnAp, ap, config)
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(node, _, _, ap, config) and
- localFlowBigStepExt(node, mid, false, _, config) and
- flow(mid, toReturn, nil, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(node, _, _, _, ap, config) and
+ localFlowBigStep(node, mid, false, _, config, _) and
+ flow(mid, toReturn, returnAp, nil, config) and
ap instanceof AccessPathNil
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flow(mid, _, ap, config) and
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flow(mid, _, _, ap, config) and
+ toReturn = false and
+ returnAp = TAccessPathNone()
+ )
+ or
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(node, _, _, _, ap, config) and
+ additionalJumpStep(node, mid, config) and
+ flow(mid, _, _, nil, config) and
+ toReturn = false and
+ returnAp = TAccessPathNone() and
+ ap instanceof AccessPathNil
+ )
+ or
+ // store
+ exists(TypedContent tc |
+ flowStore(tc, node, toReturn, returnAp, ap, config) and
+ flowConsCand(tc, ap, config)
+ )
+ or
+ // read
+ exists(Node mid, AccessPath ap0 |
+ readFlowFwd(node, _, mid, ap, ap0, config) and
+ flow(mid, toReturn, returnAp, ap0, config)
+ )
+ or
+ // flow into a callable
+ exists(DataFlowCall call |
+ flowIn(call, node, toReturn, returnAp, ap, config) and
toReturn = false
+ or
+ exists(AccessPath returnAp0 |
+ flowInToReturn(call, node, returnAp0, ap, config) and
+ flowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(node, _, _, ap, config) and
- additionalJumpStepExt(node, mid, config) and
- flow(mid, _, nil, config) and
- toReturn = false and
- ap instanceof AccessPathNil
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flow(mid, false, ap, config) and
- toReturn = false and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flow(mid, _, ap, config) and
- toReturn = true and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- argumentValueFlowsThrough(node, mid) and
- flow(mid, toReturn, ap, config)
- )
- or
- exists(NodeExt mid, AccessPathNil nil |
- argumentFlowsThrough(node, mid, _, config) and
- flow(mid, toReturn, nil, config) and
- ap instanceof AccessPathNil and
- flowFwd(node, _, _, ap, config)
- )
- or
- exists(Content f |
- flowStore(f, node, toReturn, ap, config) and
- flowConsCand(f, ap, config)
- )
- or
- exists(NodeExt mid, AccessPath ap0 |
- readFwd(node, _, mid, ap, ap0, config) and
- flow(mid, toReturn, ap0, config)
- )
+ // flow out of a callable
+ flowOut(_, node, _, _, ap, config) and
+ toReturn = true and
+ if flowFwd(node, true, TAccessPathSome(_), _, ap, config)
+ then returnAp = TAccessPathSome(ap)
+ else returnAp = TAccessPathNone()
}
pragma[nomagic]
-private predicate storeFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate storeFlowFwd(
+ Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- storeExtCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, config) and
- ap0 = push(f, ap)
+ storeCand2(node1, tc, node2, _, config) and
+ flowFwdStore(node2, tc, ap, _, _, _, config) and
+ ap0 = push(tc, ap)
}
pragma[nomagic]
private predicate flowStore(
- Content f, NodeExt node, boolean toReturn, AccessPath ap, Configuration config
+ TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
) {
- exists(NodeExt mid, AccessPath ap0 |
- storeFwd(node, f, mid, ap, ap0, config) and
- flow(mid, toReturn, ap0, config)
+ exists(Node mid, AccessPath ap0 |
+ storeFlowFwd(node, tc, mid, ap, ap0, config) and
+ flow(mid, toReturn, returnAp, ap0, config)
)
}
pragma[nomagic]
-private predicate readFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate readFlowFwd(
+ Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- readExtCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, config) and
- ap0 = pop(f, ap) and
- flowConsCandFwd(f, _, ap0, unbind(config))
+ exists(AccessPathFrontHead apf |
+ readCandFwd(node1, tc, apf, node2, config) and
+ flowFwdRead(node2, apf, ap, _, _, _, config) and
+ ap0 = pop(tc, ap) and
+ flowFwdConsCand(tc, _, ap0, unbind(config))
+ )
}
pragma[nomagic]
-private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
- exists(NodeExt n, NodeExt mid |
- flow(mid, _, ap, config) and
- readFwd(n, f, mid, _, ap, config)
+private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
+ exists(Node n, Node mid |
+ flow(mid, _, _, ap, config) and
+ readFlowFwd(n, tc, mid, _, ap, config)
+ )
+}
+
+pragma[nomagic]
+private predicate flowOut(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(Node out, boolean allowsFieldFlow |
+ flow(out, toReturn, returnAp, ap, config) and
+ flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config)
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowIn(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ flow(p, toReturn, returnAp, ap, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowInToReturn(
+ DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config
+) {
+ flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `flow`.
+ */
+pragma[nomagic]
+private predicate flowIsReturned(
+ DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ flowOut(call, ret, toReturn, returnAp, ap, config) and
+ flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
)
}
bindingset[conf, result]
private Configuration unbind(Configuration conf) { result >= conf and result <= conf }
-private predicate flow(Node n, Configuration config) { flow(TNormalNode(n), _, _, config) }
+private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) }
+
+pragma[noinline]
+private predicate parameterFlow(
+ ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config
+) {
+ flow(p, true, _, ap, config) and
+ c = p.getEnclosingCallable()
+}
private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParameterNode p, AccessPath ap) {
- exists(ReturnNodeExt ret, Configuration config | flow(TNormalNode(p), true, ap, config) |
- exists(Summary summary |
- parameterFlowReturn(p, ret, _, _, _, summary, config) and
- flow(ret, unbind(config))
- |
- // taint through
- summary = TSummaryTaint() and
- ap instanceof AccessPathNil
- or
- // taint setter
- summary = TSummaryTaintStore(_) and
- ap instanceof AccessPathNil
- or
- // taint getter
- summary = TSummaryReadTaint(ap.(AccessPathConsNil).getHead())
- )
- or
- exists(ContentOption contentIn |
- parameterValueFlowReturn(p, ret, _, contentIn, _) and
- flow(ret, unbind(config))
- |
- // value through/setter
- contentIn = TContentNone()
- or
- // value getter (+ setter)
- contentIn = TContentSome(ap.getHead())
- )
+ exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 |
+ parameterFlow(p, ap, ret.getEnclosingCallable(), config) and
+ flow(ret, true, TAccessPathSome(_), ap0, config) and
+ flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config)
)
}
@@ -2113,7 +2071,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, cc, sc, ap) and
config = mid.getConfiguration() and
- flow(TNormalNode(node), _, ap, unbind(config))
+ flow(node, _, _, ap, unbind(config))
)
} or
TPathNodeSink(Node node, Configuration config) {
@@ -2164,14 +2122,31 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
+ private predicate isHidden() {
+ nodeIsHidden(this.getNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ }
+
+ private PathNode getASuccessorIfHidden() {
+ this.isHidden() and
+ result = this.(PathNodeImpl).getASuccessorImpl()
+ }
+
/** Gets a successor of this node, if any. */
- PathNode getASuccessor() { none() }
+ final PathNode getASuccessor() {
+ result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
+ not this.isHidden() and
+ not result.isHidden()
+ }
/** Holds if this node is a source. */
predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
+ abstract PathNode getASuccessorImpl();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2246,7 +2221,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
result.getConfiguration() = unbind(this.getConfiguration())
}
- override PathNodeImpl getASuccessor() {
+ override PathNodeImpl getASuccessorImpl() {
// an intermediate step to another intermediate node
result = getSuccMid()
or
@@ -2283,7 +2258,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNode getASuccessor() { none() }
+ override PathNode getASuccessorImpl() { none() }
override predicate isSource() { config.isSource(node) }
}
@@ -2293,17 +2268,18 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
* a callable is recorded by `cc`.
*/
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
- exists(
- AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
- LocalCallContext localCC
- |
- pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
- localCC = getLocalCallContext(cc, enclosing)
+ exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
+ midnode = mid.getNode() and
+ conf = mid.getConfiguration() and
+ cc = mid.getCallContext() and
+ sc = mid.getSummaryCtx() and
+ localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
ap = ap0
or
- localFlowBigStep(midnode, node, false, ap.(AccessPathNil).getType(), conf, localCC) and
+ localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -2318,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
mid.getAp() instanceof AccessPathNil and
ap = TNil(getErasedNodeTypeBound(node))
or
- exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
+ exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2332,44 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
}
pragma[nomagic]
-private predicate pathIntoLocalStep(
- PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
- AccessPath ap0, Configuration conf
-) {
- midnode = mid.getNode() and
- cc = mid.getCallContext() and
- conf = mid.getConfiguration() and
- localFlowBigStep(midnode, _, _, _, conf, _) and
- enclosing = midnode.getEnclosingCallable() and
- sc = mid.getSummaryCtx() and
- ap0 = mid.getAp()
-}
-
-pragma[nomagic]
-private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
- readDirect(node1, f, node2) and
+private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ readCandFwd(node1, tc, _, node2, config) and
flow(node2, config)
}
pragma[nomagic]
-private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
+private predicate pathReadStep(
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+) {
ap0 = mid.getAp() and
- readCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
-private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- storeDirect(node1, f, node2) and
+private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ storeCand2(node1, tc, node2, _, config) and
flow(node2, config)
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
@@ -2399,11 +2363,11 @@ private predicate pathOutOfCallable1(
}
pragma[noinline]
-private Node getAnOutNodeCand(
+private Node getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config
) {
result = kind.getAnOutNode(call) and
- flow(TNormalNode(result), _, ap, config)
+ flow(result, _, _, ap, config)
}
/**
@@ -2415,7 +2379,7 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config |
pathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = getAnOutNodeCand(kind, call, ap, config)
+ out = getAnOutNodeFlow(kind, call, ap, config)
)
}
@@ -2439,7 +2403,7 @@ private predicate parameterCand(
DataFlowCallable callable, int i, AccessPath ap, Configuration config
) {
exists(ParameterNode p |
- flow(TNormalNode(p), _, ap, config) and
+ flow(p, _, _, ap, config) and
p.isParameterOf(callable, i)
)
}
@@ -2514,7 +2478,7 @@ pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
exists(DataFlowCall call, ReturnKindExt kind |
pathThroughCallable0(call, mid, kind, cc, ap) and
- out = getAnOutNodeCand(kind, call, ap, mid.getConfiguration())
+ out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration())
)
}
@@ -2555,10 +2519,7 @@ private module FlowExploration {
viableParamArg(_, node2, node1)
or
// flow out of a callable
- exists(DataFlowCall call, ReturnKindExt kind |
- getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = kind.getAnOutNode(call)
- )
+ viableReturnPosOut(_, getReturnPosition(node1), node2)
|
c1 = node1.getEnclosingCallable() and
c2 = node2.getEnclosingCallable() and
@@ -2603,10 +2564,10 @@ private module FlowExploration {
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
- TPartialCons(Content f, int len) { len in [1 .. 5] }
+ TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
* element of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -2615,7 +2576,7 @@ private module FlowExploration {
private class PartialAccessPath extends TPartialAccessPath {
abstract string toString();
- Content getHead() { this = TPartialCons(result, _) }
+ TypedContent getHead() { this = TPartialCons(result, _) }
int len() {
this = TPartialNil(_) and result = 0
@@ -2626,7 +2587,7 @@ private module FlowExploration {
DataFlowType getType() {
this = TPartialNil(result)
or
- exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
+ exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
@@ -2644,15 +2605,15 @@ private module FlowExploration {
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
override string toString() {
- exists(Content f, int len | this = TPartialCons(f, len) |
+ exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
if len = 1
- then result = "[" + f.toString() + "]"
- else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc.toString() + "]"
+ else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
override AccessPathFront getFront() {
- exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
+ exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
}
}
@@ -2828,11 +2789,12 @@ private module FlowExploration {
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
- exists(PartialAccessPath ap0, Content f |
- partialPathReadStep(mid, ap0, f, node, cc, config) and
+ exists(PartialAccessPath ap0, TypedContent tc |
+ partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, f, ap0, config)
+ apConsFwd(ap, tc, ap0, config) and
+ compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -2851,35 +2813,42 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
+ PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
+ PartialAccessPath ap2
) {
- ap1 = mid.getAp() and
- storeDirect(mid.getNode(), f, node) and
- ap2.getHead() = f and
- ap2.len() = unbindInt(ap1.len() + 1) and
- compatibleTypes(ap1.getType(), f.getType())
+ exists(Node midNode, DataFlowType contentType |
+ midNode = mid.getNode() and
+ ap1 = mid.getAp() and
+ store(midNode, tc, node, contentType) and
+ ap2.getHead() = tc and
+ ap2.len() = unbindInt(ap1.len() + 1) and
+ compatibleTypes(ap1.getType(), contentType)
+ )
}
pragma[nomagic]
private predicate apConsFwd(
- PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
+ PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
- partialPathStoreStep(mid, ap1, f, _, ap2) and
+ partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
+ PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
- ap = mid.getAp() and
- readStep(mid.getNode(), f, node) and
- ap.getHead() = f and
- config = mid.getConfiguration() and
- cc = mid.getCallContext()
+ exists(Node midNode |
+ midNode = mid.getNode() and
+ ap = mid.getAp() and
+ read(midNode, tc.getContent(), node) and
+ ap.getHead() = tc and
+ config = mid.getConfiguration() and
+ cc = mid.getCallContext()
+ )
}
private predicate partialPathOutOfCallable0(
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
index a1daeb66411..1aeedf717f7 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
*/
predicate isBarrier(Node node) { none() }
- /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */
- deprecated predicate isBarrierEdge(Node node1, Node node2) { none() }
-
/** Holds if data flow into `node` is prohibited. */
predicate isBarrierIn(Node node) { none() }
@@ -251,15 +248,11 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
-pragma[noinline]
-private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
- viableCallable(call) = result.getCallable() and
- kind = result.getKind()
-}
-
/**
- * Holds if `node` is reachable from a source in the given configuration
- * taking simple call contexts into consideration.
+ * Holds if `node` is reachable from a source in the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call.
*/
private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) {
not fullBarrier(node, config) and
@@ -293,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
exists(Node mid |
useFieldFlow(config) and
nodeCandFwd1(mid, fromArg, config) and
- storeDirect(mid, _, node) and
+ store(mid, _, node, _) and
not outBarrier(mid, config)
)
or
// read
- exists(Content f |
- nodeCandFwd1Read(f, node, fromArg, config) and
- storeCandFwd1(f, config) and
+ exists(Content c |
+ nodeCandFwd1Read(c, node, fromArg, config) and
+ nodeCandFwd1IsStored(c, config) and
not inBarrier(node, config)
)
or
@@ -317,13 +310,35 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
fromArg = false
or
nodeCandFwd1OutFromArg(call, node, config) and
- flowOutCandFwd1(call, fromArg, config)
+ nodeCandFwd1IsEntered(call, fromArg, config)
)
)
}
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
+pragma[nomagic]
+private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
+ exists(Node mid |
+ nodeCandFwd1(mid, fromArg, config) and
+ read(mid, c, node)
+ )
+}
+
+/**
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
+ exists(Node mid, Node node, TypedContent tc |
+ not fullBarrier(node, config) and
+ useFieldFlow(config) and
+ nodeCandFwd1(mid, config) and
+ store(mid, tc, node, _) and
+ c = tc.getContent()
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1ReturnPosition(
ReturnPosition pos, boolean fromArg, Configuration config
@@ -335,43 +350,10 @@ private predicate nodeCandFwd1ReturnPosition(
}
pragma[nomagic]
-private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
- exists(Node mid |
- nodeCandFwd1(mid, fromArg, config) and
- readDirect(mid, f, node)
- )
-}
-
-/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
- */
-pragma[nomagic]
-private predicate storeCandFwd1(Content f, Configuration config) {
- exists(Node mid, Node node |
- not fullBarrier(node, config) and
- useFieldFlow(config) and
- nodeCandFwd1(mid, config) and
- storeDirect(mid, f, node)
- )
-}
-
-pragma[nomagic]
-private predicate nodeCandFwd1ReturnKind(
- DataFlowCall call, ReturnKindExt kind, boolean fromArg, Configuration config
-) {
+private predicate nodeCandFwd1Out(DataFlowCall call, Node out, boolean fromArg, Configuration config) {
exists(ReturnPosition pos |
nodeCandFwd1ReturnPosition(pos, fromArg, config) and
- pos = viableReturnPos(call, kind)
- )
-}
-
-pragma[nomagic]
-private predicate nodeCandFwd1Out(
- DataFlowCall call, Node node, boolean fromArg, Configuration config
-) {
- exists(ReturnKindExt kind |
- nodeCandFwd1ReturnKind(call, kind, fromArg, config) and
- node = kind.getAnOutNode(call)
+ viableReturnPosOut(call, pos, out)
)
}
@@ -384,7 +366,7 @@ private predicate nodeCandFwd1OutFromArg(DataFlowCall call, Node node, Configura
* Holds if an argument to `call` is reached in the flow covered by `nodeCandFwd1`.
*/
pragma[nomagic]
-private predicate flowOutCandFwd1(DataFlowCall call, boolean fromArg, Configuration config) {
+private predicate nodeCandFwd1IsEntered(DataFlowCall call, boolean fromArg, Configuration config) {
exists(ArgumentNode arg |
nodeCandFwd1(arg, fromArg, config) and
viableParamArg(call, _, arg)
@@ -395,8 +377,11 @@ bindingset[result, b]
private boolean unbindBool(boolean b) { result != b.booleanNot() }
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is part of a path from a source to a sink in the
+ * configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink.
*/
pragma[nomagic]
private predicate nodeCand1(Node node, boolean toReturn, Configuration config) {
@@ -433,81 +418,86 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
)
or
// store
- exists(Content f |
- nodeCand1Store(f, node, toReturn, config) and
- readCand1(f, config)
+ exists(Content c |
+ nodeCand1Store(c, node, toReturn, config) and
+ nodeCand1IsRead(c, config)
)
or
// read
- exists(Node mid, Content f |
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ exists(Node mid, Content c |
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, toReturn, config)
)
or
// flow into a callable
exists(DataFlowCall call |
- nodeCand1Arg(call, node, false, config) and
+ nodeCand1In(call, node, false, config) and
toReturn = false
or
- nodeCand1ArgToReturn(call, node, config) and
- flowInCand1(call, toReturn, config)
+ nodeCand1InToReturn(call, node, config) and
+ nodeCand1IsReturned(call, toReturn, config)
)
or
// flow out of a callable
exists(ReturnPosition pos |
- nodeCand1ReturnPosition(pos, config) and
+ nodeCand1Out(pos, config) and
getReturnPosition(node) = pos and
toReturn = true
)
}
-pragma[nomagic]
-private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _, config) }
-
-pragma[nomagic]
-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`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate readCand1(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content c, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd1(node, unbind(config)) and
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, _, config)
)
}
pragma[nomagic]
-private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
- exists(Node mid |
+private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
+ exists(Node mid, TypedContent tc |
nodeCand1(mid, toReturn, config) and
- storeCandFwd1(f, unbind(config)) and
- storeDirect(node, f, mid)
+ nodeCandFwd1IsStored(c, unbind(config)) and
+ store(node, tc, mid, _) and
+ c = tc.getContent()
)
}
/**
- * Holds if `f` is the target of both a read and a store in the flow covered
+ * Holds if `c` is the target of both a read and a store in the flow covered
* by `nodeCand1`.
*/
-private predicate readStoreCand1(Content f, Configuration conf) {
- readCand1(f, conf) and
- nodeCand1Store(f, _, _, conf)
+private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
+ nodeCand1IsRead(c, conf) and
+ nodeCand1Store(c, _, _, conf)
}
pragma[nomagic]
-private predicate viableParamArgCandFwd1(
+private predicate viableReturnPosOutNodeCandFwd1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+) {
+ nodeCandFwd1ReturnPosition(pos, _, config) and
+ viableReturnPosOut(call, pos, out)
+}
+
+pragma[nomagic]
+private predicate nodeCand1Out(ReturnPosition pos, Configuration config) {
+ exists(DataFlowCall call, Node out |
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, config)
+ )
+}
+
+pragma[nomagic]
+private predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
) {
viableParamArg(call, p, arg) and
@@ -515,32 +505,35 @@ private predicate viableParamArgCandFwd1(
}
pragma[nomagic]
-private predicate nodeCand1Arg(
+private predicate nodeCand1In(
DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config
) {
exists(ParameterNode p |
nodeCand1(p, toReturn, config) and
- viableParamArgCandFwd1(call, p, arg, config)
+ viableParamArgNodeCandFwd1(call, p, arg, config)
)
}
pragma[nomagic]
-private predicate nodeCand1ArgToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
- nodeCand1Arg(call, arg, true, config)
+private predicate nodeCand1InToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
+ nodeCand1In(call, arg, true, config)
}
/**
* Holds if an output from `call` is reached in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate flowInCand1(DataFlowCall call, boolean toReturn, Configuration config) {
+private predicate nodeCand1IsReturned(DataFlowCall call, boolean toReturn, Configuration config) {
exists(Node out |
nodeCand1(out, toReturn, config) and
nodeCandFwd1OutFromArg(call, out, config)
)
}
-private predicate throughFlowNodeCand(Node node, Configuration config) {
+pragma[nomagic]
+private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _, config) }
+
+private predicate throughFlowNodeCand1(Node node, Configuration config) {
nodeCand1(node, true, config) and
not fullBarrier(node, config) and
not inBarrier(node, config) and
@@ -549,11 +542,11 @@ private predicate throughFlowNodeCand(Node node, Configuration config) {
/** Holds if flow may return from `callable`. */
pragma[nomagic]
-private predicate returnFlowCallableCand(
+private predicate returnFlowCallableNodeCand1(
DataFlowCallable callable, ReturnKindExt kind, Configuration config
) {
exists(ReturnNodeExt ret |
- throughFlowNodeCand(ret, config) and
+ throughFlowNodeCand1(ret, config) and
callable = ret.getEnclosingCallable() and
kind = ret.getKind()
)
@@ -563,10 +556,10 @@ private predicate returnFlowCallableCand(
* Holds if flow may enter through `p` and reach a return node making `p` a
* candidate for the origin of a summary.
*/
-private predicate parameterThroughFlowCand(ParameterNode p, Configuration config) {
+private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration config) {
exists(ReturnKindExt kind |
- throughFlowNodeCand(p, config) and
- returnFlowCallableCand(p.getEnclosingCallable(), kind, config) and
+ throughFlowNodeCand1(p, config) and
+ returnFlowCallableNodeCand1(p.getEnclosingCallable(), kind, config) and
// we don't expect a parameter to return stored in itself
not exists(int pos |
kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos)
@@ -575,420 +568,77 @@ private predicate parameterThroughFlowCand(ParameterNode p, Configuration config
}
pragma[nomagic]
-private predicate store(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
- nodeCand1(n2, unbind(config)) and
- (
- storeDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentSome(f), n2)
- )
-}
-
-pragma[nomagic]
-private predicate read(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
- nodeCand1(n2, unbind(config)) and
- (
- readDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentSome(f), TContentNone(), n2)
- )
-}
-
-/**
- * Holds if `p` can flow to `node` in the same callable with `summary`
- * representing the flow path. The type of the tracked object is `t2`, and if
- * the summary includes a store step, `t1` is the tracked type just prior to the
- * store, that is, the type of the stored object, otherwise `t1` is equal to `t2`.
- */
-pragma[nomagic]
-private predicate parameterFlow(
- ParameterNode p, Node node, DataFlowType t1, DataFlowType t2, Summary summary,
- Configuration config
-) {
- parameterThroughFlowCand(p, config) and
- p = node and
- t1 = getErasedNodeTypeBound(node) and
- t1 = t2 and
- summary = TSummaryVal()
- or
- throughFlowNodeCand(node, unbind(config)) and
- (
- exists(Node mid |
- parameterFlow(p, mid, t1, t2, summary, config) and
- localFlowStep(mid, node, config) and
- compatibleTypes(t2, getErasedNodeTypeBound(node))
- )
- or
- exists(Node mid, Summary midsum |
- parameterFlow(p, mid, _, _, midsum, config) and
- additionalLocalFlowStep(mid, node, config) and
- t1 = getErasedNodeTypeBound(node) and
- t1 = t2 and
- summary = midsum.additionalStep()
- )
- or
- // read step
- exists(Node mid, Content f, Summary midsum |
- parameterFlow(p, mid, _, _, midsum, config) and
- read(mid, f, node, config) and
- summary = midsum.readStep(f) and
- t1 = f.getType() and
- t1 = t2
- )
- or
- // store step
- exists(Node mid, Content f, Summary midsum |
- parameterFlow(p, mid, t1, /* t1 */ _, midsum, config) and
- store(mid, f, node, config) and
- summary = midsum.storeStep(f) and
- compatibleTypes(t1, f.getType()) and
- t2 = f.getContainerType()
- )
- or
- // value flow through a callable
- exists(Node arg |
- parameterFlow(p, arg, t1, t2, summary, config) and
- argumentValueFlowsThrough(_, arg, TContentNone(), TContentNone(), node) and
- compatibleTypes(t2, getErasedNodeTypeBound(node))
- )
- or
- // flow through a callable
- exists(Node arg, Summary s1, Summary s2 |
- parameterFlow(p, arg, _, _, s1, config) and
- argumentFlowsThrough(arg, node, t1, t2, s2, config) and
- summary = s1.compose(s2)
- )
- )
-}
-
-private predicate viableParamArgCand(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
-) {
- viableParamArg(call, p, arg) and
- nodeCand1(arg, unbind(config)) and
- nodeCand1(p, config) and
- not outBarrier(arg, config) and
- not inBarrier(p, config)
-}
-
-pragma[nomagic]
-private predicate parameterFlowReturn(
- ParameterNode p, ReturnNodeExt ret, ReturnKindExt kind, DataFlowType t1, DataFlowType t2,
- Summary summary, Configuration config
-) {
- parameterFlow(p, ret, t1, t2, summary, config) and
- kind = ret.getKind() and
- not summary.isPartial() and
- not exists(int pos | kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos))
-}
-
-pragma[nomagic]
-private predicate argumentFlowsThrough0(
- DataFlowCall call, ArgumentNode arg, ReturnKindExt kind, DataFlowType t1, DataFlowType t2,
- Summary summary, Configuration config
-) {
- exists(ParameterNode p |
- viableParamArgCand(call, p, arg, config) and
- parameterFlowReturn(p, _, kind, t1, t2, summary, config)
- )
-}
-
-/**
- * Holds if data can flow from `arg` to `out` through a call with `summary`
- * representing the flow path. The type of the tracked object is `t2`, and if
- * the summary includes a store step, `t1` is the tracked type just prior to the
- * store, that is, the type of the stored object, otherwise `t1` is equal to `t2`.
- */
-private predicate argumentFlowsThrough(
- ArgumentNode arg, Node out, DataFlowType t1, DataFlowType t2, Summary summary,
- Configuration config
-) {
- nodeCand1(out, unbind(config)) and
- not inBarrier(out, config) and
- compatibleTypes(t2, getErasedNodeTypeBound(out)) and
- exists(DataFlowCall call, ReturnKindExt kind |
- argumentFlowsThrough0(call, arg, kind, t1, t2, summary, config) and
- out = kind.getAnOutNode(call)
- )
-}
-
-pragma[noinline]
-private predicate readStoreNode(
- DataFlowCall call, ArgumentNode arg, Content f1, Configuration config
-) {
- exists(Content f2, Node out |
- argumentValueFlowsThrough(call, arg, TContentSome(f1), TContentSome(f2), out) and
- nodeCand1(out, config) and
- readStoreCand1(f2, unbind(config))
- )
-}
-
-private newtype TNodeExt =
- TNormalNode(Node node) { nodeCand1(node, _) } or
- TReadStoreNode(DataFlowCall call, ArgumentNode arg, Content f1, Configuration config) {
- nodeCand1(arg, config) and
- readStoreNode(call, arg, f1, config) and
- readStoreCand1(f1, unbind(config))
- } or
- TReadTaintNode(ArgumentNode arg, Content f, Configuration config) {
- argumentFlowsThrough(arg, _, _, _, TSummaryReadTaint(f), config)
- } or
- TTaintStoreNode(ArgumentNode arg, DataFlowType t, Configuration config) {
- argumentFlowsThrough(arg, _, t, _, TSummaryTaintStore(_), config)
- }
-
-/**
- * An extended data flow node. Either a normal node, or an intermediate node
- * used to split up a summarized flow steps.
- *
- * This is purely an internal implementation detail.
- */
-abstract private class NodeExt extends TNodeExt {
- /** Gets the underlying (normal) node, if any. */
- abstract Node getNode();
-
- abstract DataFlowType getErasedNodeTypeBound();
-
- abstract DataFlowCallable getEnclosingCallable();
-
- abstract predicate isCand1(Configuration config);
-
- abstract string toString();
-
- abstract predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- );
-}
-
-/** A `Node` at which a cast can occur such that the type should be checked. */
-abstract private class CastingNodeExt extends NodeExt { }
-
-private class NormalNodeExt extends NodeExt, TNormalNode {
- override Node getNode() { this = TNormalNode(result) }
-
- override DataFlowType getErasedNodeTypeBound() {
- result = getErasedRepr(this.getNode().getTypeBound())
- }
-
- override DataFlowCallable getEnclosingCallable() {
- result = this.getNode().getEnclosingCallable()
- }
-
- override predicate isCand1(Configuration config) { nodeCand1(this.getNode(), config) }
-
- override string toString() { result = this.getNode().toString() }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class NormalCastingNodeExt extends CastingNodeExt, NormalNodeExt {
- NormalCastingNodeExt() { this.getNode() instanceof CastingNode }
-}
-
-private class ReadStoreNodeExt extends CastingNodeExt, TReadStoreNode {
- private DataFlowCall call;
- private ArgumentNode arg;
- private Content f1;
- private Configuration config0;
-
- ReadStoreNodeExt() { this = TReadStoreNode(call, arg, f1, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = f1.getType() }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = "(inside) " + call.toString() + " [read " + f1 + "]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class ReadTaintNode extends NodeExt, TReadTaintNode {
- private ArgumentNode arg;
- private Content f;
- private Configuration config0;
-
- ReadTaintNode() { this = TReadTaintNode(arg, f, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = f.getType() }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = arg.toString() + " [read taint " + f + "]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class TaintStoreNode extends NodeExt, TTaintStoreNode {
- private ArgumentNode arg;
- private DataFlowType t;
- private Configuration config0;
-
- TaintStoreNode() { this = TTaintStoreNode(arg, t, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = t }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = arg.toString() + " [taint store]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private predicate additionalLocalFlowStepExt(
- NodeExt node1, NodeExt node2, DataFlowType t, Configuration config
-) {
- exists(ArgumentNode arg, Content f |
- node1 = TReadTaintNode(arg, f, config) and
- argumentFlowsThrough(arg, node2.getNode(), _, t, TSummaryReadTaint(f), config)
- )
- or
- node2 = TTaintStoreNode(node1.getNode(), t, config)
-}
-
-pragma[nomagic]
-private predicate readExt(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- read(node1.getNode(), f, node2.getNode(), config)
- or
- node2 = TReadStoreNode(_, node1.getNode(), f, config)
- or
- node2 = TReadTaintNode(node1.getNode(), f, config)
-}
-
-pragma[nomagic]
-private predicate storeExt(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- store(node1.getNode(), f, node2.getNode(), config)
- or
- exists(DataFlowCall call, ArgumentNode arg, Content f1, Node n2 |
- node1 = TReadStoreNode(call, arg, f1, config) and
- n2 = node2.getNode() and
- argumentValueFlowsThrough(call, arg, TContentSome(f1), TContentSome(f), n2) and
+private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
+ exists(TypedContent tc |
+ nodeCand1IsReadAndStored(c, config) and
nodeCand1(n2, unbind(config)) and
- readStoreCand1(f, unbind(config))
- )
- or
- exists(ArgumentNode arg, DataFlowType t |
- node1 = TTaintStoreNode(arg, t, config) and
- argumentFlowsThrough(arg, node2.getNode(), t, _, TSummaryTaintStore(f), config)
+ store(n1, tc, n2, _) and
+ c = tc.getContent()
)
}
-private predicate jumpStepExt(NodeExt node1, NodeExt node2, Configuration config) {
- jumpStep(node1.getNode(), node2.getNode(), config)
-}
-
-private predicate additionalJumpStepExt(NodeExt node1, NodeExt node2, Configuration config) {
- additionalJumpStep(node1.getNode(), node2.getNode(), config)
-}
-
-private predicate argumentValueFlowsThrough(NodeExt node1, NodeExt node2) {
- argumentValueFlowsThrough(_, node1.getNode(), TContentNone(), TContentNone(), node2.getNode())
-}
-
-private predicate argumentFlowsThrough(
- NodeExt arg, NodeExt out, DataFlowType t, Configuration config
-) {
- argumentFlowsThrough(arg.getNode(), out.getNode(), _, t, TSummaryTaint(), config)
-}
-
-/**
- * Holds if data can flow from `node1` to `node2` in one local step or a step
- * through a callable.
- */
-pragma[noinline]
-private predicate localFlowStepOrFlowThroughCallable(
- NodeExt node1, NodeExt node2, Configuration config
-) {
- exists(Node n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode()
- |
- nodeCand1(n1, config) and
- localFlowStep(n1, n2, config)
- or
- nodeCand1(n1, config) and
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentNone(), n2)
- )
-}
-
-/**
- * Holds if data can flow from `node1` to `node2` in one local step or a step
- * through a callable, in both cases using an additional flow step from the
- * configuration.
- */
-pragma[noinline]
-private predicate additionalLocalFlowStepOrFlowThroughCallable(
- NodeExt node1, NodeExt node2, Configuration config
-) {
- exists(Node n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode()
- |
- nodeCand1(n1, config) and
- additionalLocalFlowStep(n1, n2, config)
- or
- argumentFlowsThrough(n1, n2, _, _, TSummaryTaint(), config)
- )
- or
- additionalLocalFlowStepExt(node1, node2, _, config)
+pragma[nomagic]
+private predicate read(Node n1, Content c, Node n2, Configuration config) {
+ nodeCand1IsReadAndStored(c, config) and
+ nodeCand1(n2, unbind(config)) and
+ read(n1, c, n2)
}
pragma[noinline]
-private ReturnPosition getReturnPosition1(ReturnNodeExt node, Configuration config) {
- result = getReturnPosition(node) and
- nodeCand1(node, config)
+private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ localFlowStep(node1, node2, config)
+}
+
+pragma[noinline]
+private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ additionalLocalFlowStep(node1, node2, config)
+}
+
+pragma[nomagic]
+private predicate viableReturnPosOutNodeCand1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+) {
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, config)
}
/**
- * Holds if data can flow out of a callable from `node1` to `node2`, either
+ * Holds if data can flow out of `call` from `ret` to `out`, 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 flowOutOfCallableNodeCand1(ReturnNodeExt node1, Node node2, Configuration config) {
- nodeCand1(node2, config) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- exists(DataFlowCall call, ReturnKindExt kind |
- getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
- node2 = kind.getAnOutNode(call)
- )
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand1(
+ DataFlowCall call, ReturnNodeExt ret, Node out, Configuration config
+) {
+ viableReturnPosOutNodeCand1(call, getReturnPosition(ret), out, config) and
+ nodeCand1(ret, config) and
+ not outBarrier(ret, config) and
+ not inBarrier(out, config)
+}
+
+pragma[nomagic]
+private predicate viableParamArgNodeCand1(
+ DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+) {
+ viableParamArgNodeCandFwd1(call, p, arg, config) and
+ nodeCand1(arg, config)
}
/**
- * Holds if data can flow into a callable and that this step is part of a
+ * Holds if data can flow into `call` and that this step is part of a
* path from a source to a sink.
*/
-private predicate flowIntoCallableNodeCand1(
- ArgumentNode node1, ParameterNode node2, Configuration config
+pragma[nomagic]
+private predicate flowIntoCallNodeCand1(
+ DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config
) {
- viableParamArgCand(_, node2, node1, config)
+ viableParamArgNodeCand1(call, p, arg, config) and
+ nodeCand1(p, config) and
+ not outBarrier(arg, config) and
+ not inBarrier(p, config)
}
/**
@@ -999,7 +649,7 @@ private predicate flowIntoCallableNodeCand1(
private int branch(Node n1, Configuration conf) {
result =
strictcount(Node n |
- flowOutOfCallableNodeCand1(n1, n, conf) or flowIntoCallableNodeCand1(n1, n, conf)
+ flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1011,269 +661,391 @@ private int branch(Node n1, Configuration conf) {
private int join(Node n2, Configuration conf) {
result =
strictcount(Node n |
- flowOutOfCallableNodeCand1(n, n2, conf) or flowIntoCallableNodeCand1(n, n2, conf)
+ flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
/**
- * Holds if data can flow out of a callable from `node1` to `node2`, either
+ * Holds if data can flow out of `call` from `ret` to `out`, 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. The
* `allowsFieldFlow` flag indicates whether the branching is within the limit
* specified by the configuration.
*/
-private predicate flowOutOfCallableNodeCand1(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand1(
+ DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config
) {
- exists(ReturnNodeExt n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode() and
- flowOutOfCallableNodeCand1(n1, n2, config) and
- exists(int b, int j |
- b = branch(n1, config) and
- j = join(n2, config) and
- if b.minimum(j) <= config.fieldFlowBranchLimit()
- then allowsFieldFlow = true
- else allowsFieldFlow = false
- )
+ flowOutOfCallNodeCand1(call, ret, out, config) and
+ exists(int b, int j |
+ b = branch(ret, config) and
+ j = join(out, config) and
+ if b.minimum(j) <= config.fieldFlowBranchLimit()
+ then allowsFieldFlow = true
+ else allowsFieldFlow = false
)
}
/**
- * Holds if data can flow into a callable and that this step is part of a
+ * Holds if data can flow into `call` and that this step is part of a
* path from a source to a sink. The `allowsFieldFlow` flag indicates whether
* the branching is within the limit specified by the configuration.
*/
-private predicate flowIntoCallableNodeCand1(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+pragma[nomagic]
+private predicate flowIntoCallNodeCand1(
+ DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow,
+ Configuration config
) {
- exists(ArgumentNode n1, ParameterNode n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode() and
- flowIntoCallableNodeCand1(n1, n2, config) and
- exists(int b, int j |
- b = branch(n1, config) and
- j = join(n2, config) and
- if b.minimum(j) <= config.fieldFlowBranchLimit()
- then allowsFieldFlow = true
- else allowsFieldFlow = false
- )
+ flowIntoCallNodeCand1(call, arg, p, config) and
+ exists(int b, int j |
+ b = branch(arg, config) and
+ j = join(p, config) and
+ if b.minimum(j) <= config.fieldFlowBranchLimit()
+ then allowsFieldFlow = true
+ else allowsFieldFlow = false
)
}
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is reachable from a source in the configuration `config`.
+ * The Boolean `stored` records whether the tracked value is stored into a
+ * field of `node`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argStored` records whether the tracked
+ * value was stored into a field of the argument.
*/
-private predicate nodeCandFwd2(NodeExt node, boolean fromArg, boolean stored, Configuration config) {
- nodeCand1(node.getNode(), config) and
- config.isSource(node.getNode()) and
+private predicate nodeCandFwd2(
+ Node node, boolean fromArg, BooleanOption argStored, boolean stored, Configuration config
+) {
+ nodeCand1(node, config) and
+ config.isSource(node) and
fromArg = false and
+ argStored = TBooleanNone() and
stored = false
or
- node.isCand1(unbind(config)) and
+ nodeCand1(node, unbind(config)) and
(
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, stored, config) and
- localFlowStepOrFlowThroughCallable(mid, node, config)
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, stored, config) and
+ localFlowStepNodeCand1(mid, node, config)
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, stored, config) and
- additionalLocalFlowStepOrFlowThroughCallable(mid, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, stored, config) and
+ additionalLocalFlowStepNodeCand1(mid, node, config) and
stored = false
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, _, stored, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
+ exists(Node mid |
+ nodeCandFwd2(mid, _, _, stored, config) and
+ jumpStep(mid, node, config) and
+ fromArg = false and
+ argStored = TBooleanNone()
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, _, stored, config) and
- additionalJumpStepExt(mid, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, _, _, stored, config) and
+ additionalJumpStep(mid, node, config) and
fromArg = false and
+ argStored = TBooleanNone() and
stored = false
)
or
// store
- exists(NodeExt mid, Content f |
- nodeCandFwd2(mid, fromArg, _, config) and
- storeExt(mid, f, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, _, config) and
+ storeCand1(mid, _, node, config) and
stored = true
)
or
// read
- exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, config) and
- storeCandFwd2(f, stored, config)
+ exists(Content c |
+ nodeCandFwd2Read(c, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(c, stored, config)
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- nodeCandFwd2(mid, _, stored, config) and
- flowIntoCallableNodeCand1(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (stored = false or allowsFieldFlow = true)
- )
+ // flow into a callable
+ nodeCandFwd2In(_, node, _, _, stored, config) and
+ fromArg = true and
+ if parameterThroughFlowNodeCand1(node, config)
+ then argStored = TBooleanSome(stored)
+ else argStored = TBooleanNone()
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- nodeCandFwd2(mid, false, stored, config) and
- flowOutOfCallableNodeCand1(mid, node, allowsFieldFlow, config) and
- fromArg = false and
- (stored = false or allowsFieldFlow = true)
+ // flow out of a callable
+ exists(DataFlowCall call |
+ nodeCandFwd2Out(call, node, fromArg, argStored, stored, config) and
+ fromArg = false
+ or
+ exists(boolean argStored0 |
+ nodeCandFwd2OutFromArg(call, node, argStored0, stored, config) and
+ nodeCandFwd2IsEntered(call, fromArg, argStored, argStored0, config)
+ )
)
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
*/
pragma[noinline]
-private predicate storeCandFwd2(Content f, boolean stored, Configuration config) {
- exists(NodeExt mid, NodeExt node |
+private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
+ exists(Node mid, Node node |
useFieldFlow(config) and
- node.isCand1(unbind(config)) and
- nodeCandFwd2(mid, _, stored, config) and
- storeExt(mid, f, node, config)
+ nodeCand1(node, unbind(config)) and
+ nodeCandFwd2(mid, _, _, stored, config) and
+ storeCand1(mid, c, node, config)
)
}
pragma[nomagic]
-private predicate nodeCandFwd2Read(Content f, NodeExt node, boolean fromArg, Configuration config) {
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, true, config) and
- readExt(mid, f, node, config)
+private predicate nodeCandFwd2Read(
+ Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+) {
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, true, config) and
+ read(mid, c, node, config)
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2In(
+ DataFlowCall call, ParameterNode p, boolean fromArg, BooleanOption argStored, boolean stored,
+ Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ nodeCandFwd2(arg, fromArg, argStored, stored, config) and
+ flowIntoCallNodeCand1(call, arg, p, allowsFieldFlow, config)
+ |
+ stored = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2Out(
+ DataFlowCall call, Node out, boolean fromArg, BooleanOption argStored, boolean stored,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ nodeCandFwd2(ret, fromArg, argStored, stored, config) and
+ flowOutOfCallNodeCand1(call, ret, out, allowsFieldFlow, config)
+ |
+ stored = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2OutFromArg(
+ DataFlowCall call, Node out, boolean argStored, boolean stored, Configuration config
+) {
+ nodeCandFwd2Out(call, out, true, TBooleanSome(argStored), stored, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `nodeCandFwd2`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd2IsEntered(
+ DataFlowCall call, boolean fromArg, BooleanOption argStored, boolean stored, Configuration config
+) {
+ exists(ParameterNode p |
+ nodeCandFwd2In(call, p, fromArg, argStored, stored, config) and
+ parameterThroughFlowNodeCand1(p, config)
)
}
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is part of a path from a source to a sink in the
+ * configuration `config`. The Boolean `read` records whether the tracked
+ * value must be read from a field of `node` in order to reach a sink.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnRead`
+ * records whether a field must be read from the returned value.
*/
-private predicate nodeCand2(NodeExt node, boolean toReturn, boolean read, Configuration config) {
- nodeCandFwd2(node, _, false, config) and
- config.isSink(node.getNode()) and
+private predicate nodeCand2(
+ Node node, boolean toReturn, BooleanOption returnRead, boolean read, Configuration config
+) {
+ nodeCandFwd2(node, _, _, false, config) and
+ config.isSink(node) and
toReturn = false and
+ returnRead = TBooleanNone() and
read = false
or
- nodeCandFwd2(node, _, unbindBool(read), unbind(config)) and
+ nodeCandFwd2(node, _, _, unbindBool(read), unbind(config)) and
(
- exists(NodeExt mid |
- localFlowStepOrFlowThroughCallable(node, mid, config) and
- nodeCand2(mid, toReturn, read, config)
+ exists(Node mid |
+ localFlowStepNodeCand1(node, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, read, config)
)
or
- exists(NodeExt mid |
- additionalLocalFlowStepOrFlowThroughCallable(node, mid, config) and
- nodeCand2(mid, toReturn, read, config) and
+ exists(Node mid |
+ additionalLocalFlowStepNodeCand1(node, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, read, config) and
read = false
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- nodeCand2(mid, _, read, config) and
- toReturn = false
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ nodeCand2(mid, _, _, read, config) and
+ toReturn = false and
+ returnRead = TBooleanNone()
)
or
- exists(NodeExt mid |
- additionalJumpStepExt(node, mid, config) and
- nodeCand2(mid, _, read, config) and
+ exists(Node mid |
+ additionalJumpStep(node, mid, config) and
+ nodeCand2(mid, _, _, read, config) and
toReturn = false and
+ returnRead = TBooleanNone() and
read = false
)
or
// store
- exists(Content f |
- nodeCand2Store(f, node, toReturn, read, config) and
- readCand2(f, read, config)
+ exists(Content c |
+ nodeCand2Store(c, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(c, read, config)
)
or
// read
- exists(NodeExt mid, Content f, boolean read0 |
- readExt(node, f, mid, config) and
- storeCandFwd2(f, unbindBool(read0), unbind(config)) and
- nodeCand2(mid, toReturn, read0, config) and
+ exists(Node mid, Content c, boolean read0 |
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
+ nodeCand2(mid, toReturn, returnRead, read0, config) and
read = true
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand1(node, mid, allowsFieldFlow, config) and
- nodeCand2(mid, false, read, config) and
- toReturn = false and
- (read = false or allowsFieldFlow = true)
+ // flow into a callable
+ exists(DataFlowCall call |
+ nodeCand2In(call, node, toReturn, returnRead, read, config) and
+ toReturn = false
+ or
+ exists(boolean returnRead0 |
+ nodeCand2InToReturn(call, node, returnRead0, read, config) and
+ nodeCand2IsReturned(call, toReturn, returnRead, returnRead0, config)
+ )
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand1(node, mid, allowsFieldFlow, config) and
- nodeCand2(mid, _, read, config) and
- toReturn = true and
- (read = false or allowsFieldFlow = true)
- )
+ // flow out of a callable
+ nodeCand2Out(_, node, _, _, read, config) and
+ toReturn = true and
+ if nodeCandFwd2(node, true, TBooleanSome(_), unbindBool(read), config)
+ then returnRead = TBooleanSome(read)
+ else returnRead = TBooleanNone()
)
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate readCand2(Content f, boolean read, Configuration config) {
- exists(NodeExt mid, NodeExt node |
+private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
+ exists(Node mid, Node node |
useFieldFlow(config) and
- nodeCandFwd2(node, _, true, unbind(config)) and
- readExt(node, f, mid, config) and
- storeCandFwd2(f, unbindBool(read), unbind(config)) and
- nodeCand2(mid, _, read, config)
+ nodeCandFwd2(node, _, _, true, unbind(config)) and
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
+ nodeCand2(mid, _, _, read, config)
)
}
pragma[nomagic]
private predicate nodeCand2Store(
- Content f, NodeExt node, boolean toReturn, boolean stored, Configuration config
+ Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
+ Configuration config
) {
- exists(NodeExt mid |
- storeExt(node, f, mid, config) and
- nodeCand2(mid, toReturn, true, config) and
- nodeCandFwd2(node, _, stored, unbind(config))
- )
-}
-
-pragma[nomagic]
-private predicate storeCand2(Content f, boolean stored, Configuration conf) {
- exists(NodeExt node |
- nodeCand2Store(f, node, _, stored, conf) and
- nodeCand2(node, _, stored, conf)
+ exists(Node mid |
+ storeCand1(node, c, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, true, config) and
+ nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
/**
- * Holds if `f` is the target of both a store and a read in the path graph
- * covered by `nodeCand2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
*/
-pragma[noinline]
-private predicate readStoreCand(Content f, Configuration conf) {
- exists(boolean apNonEmpty |
- storeCand2(f, apNonEmpty, conf) and
- readCand2(f, apNonEmpty, conf)
+pragma[nomagic]
+private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
+ exists(Node node |
+ nodeCand2Store(c, node, _, _, stored, conf) and
+ nodeCand2(node, _, _, stored, conf)
)
}
-private predicate nodeCand2(NodeExt node, Configuration config) { nodeCand2(node, _, _, config) }
+/**
+ * Holds if `c` is the target of both a store and a read in the path graph
+ * covered by `nodeCand2`.
+ */
+pragma[noinline]
+private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
+ exists(boolean apNonEmpty |
+ nodeCand2IsStored(c, apNonEmpty, conf) and
+ nodeCand2IsRead(c, apNonEmpty, conf)
+ )
+}
pragma[nomagic]
-private predicate flowOutOfCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate nodeCand2Out(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
) {
- flowOutOfCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ exists(Node out, boolean allowsFieldFlow |
+ nodeCand2(out, toReturn, returnRead, read, config) and
+ flowOutOfCallNodeCand1(call, ret, out, allowsFieldFlow, config)
+ |
+ read = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCand2In(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ nodeCand2(p, toReturn, returnRead, read, config) and
+ flowIntoCallNodeCand1(call, arg, p, allowsFieldFlow, config)
+ |
+ read = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCand2InToReturn(
+ DataFlowCall call, ArgumentNode arg, boolean returnRead, boolean read, Configuration config
+) {
+ nodeCand2In(call, arg, true, TBooleanSome(returnRead), read, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `nodeCand2`.
+ */
+pragma[nomagic]
+private predicate nodeCand2IsReturned(
+ DataFlowCall call, boolean toReturn, BooleanOption returnRead, boolean read, Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ nodeCand2Out(call, ret, toReturn, returnRead, read, config) and
+ nodeCandFwd2(ret, true, TBooleanSome(_), read, config)
+ )
+}
+
+private predicate nodeCand2(Node node, Configuration config) { nodeCand2(node, _, _, _, config) }
+
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand2(
+ DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
+) {
+ flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
nodeCand2(node2, config) and
nodeCand2(node1, unbind(config))
}
pragma[nomagic]
-private predicate flowIntoCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate flowIntoCallNodeCand2(
+ DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ Configuration config
) {
- flowIntoCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
nodeCand2(node2, config) and
nodeCand2(node1, unbind(config))
}
@@ -1284,15 +1056,15 @@ private module LocalFlowBigStep {
* flow steps in a dataflow path.
*/
private predicate localFlowEntry(Node node, Configuration config) {
- nodeCand2(TNormalNode(node), config) and
+ nodeCand2(node, config) and
(
config.isSource(node) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
node instanceof ParameterNode or
- node instanceof OutNode or
- node instanceof PostUpdateNode or
- readDirect(_, _, node) or
+ node instanceof OutNodeExt or
+ store(_, _, node, _) or
+ read(_, _, node) or
node instanceof CastNode
)
}
@@ -1302,15 +1074,13 @@ private module LocalFlowBigStep {
* flow steps in a dataflow path.
*/
private predicate localFlowExit(Node node, Configuration config) {
- exists(Node next | nodeCand2(TNormalNode(next), config) |
+ exists(Node next | nodeCand2(next, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
- flowIntoCallableNodeCand1(node, next, config) or
- flowOutOfCallableNodeCand1(node, next, config) or
- argumentFlowsThrough(node, next, _, _, _, config) or
- argumentValueFlowsThrough(_, node, TContentNone(), TContentNone(), next) or
- storeDirect(node, _, next) or
- readDirect(node, _, next)
+ flowIntoCallNodeCand1(_, node, next, config) or
+ flowOutOfCallNodeCand1(_, node, next, config) or
+ store(node, _, next, _) or
+ read(node, _, next)
)
or
node instanceof CastNode
@@ -1318,6 +1088,13 @@ private module LocalFlowBigStep {
config.isSink(node)
}
+ pragma[noinline]
+ private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
+ additionalLocalFlowStepNodeCand1(node1, node2, config) and
+ nodeCand2(node1, _, _, false, config) and
+ nodeCand2(node2, _, _, false, unbind(config))
+ }
+
/**
* Holds if the local path from `node1` to `node2` is a prefix of a maximal
* subsequence of local flow steps in a dataflow path.
@@ -1334,33 +1111,33 @@ private module LocalFlowBigStep {
(
localFlowEntry(node1, config) and
(
- localFlowStep(node1, node2, config) and
+ localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
t = getErasedNodeTypeBound(node1)
or
- additionalLocalFlowStep(node1, node2, config) and
+ additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
t = getErasedNodeTypeBound(node2)
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
or
exists(Node mid |
localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
- localFlowStep(mid, node2, config) and
+ localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof CastNode and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
)
or
exists(Node mid |
localFlowStepPlus(node1, mid, _, _, config, cc) and
- additionalLocalFlowStep(mid, node2, config) and
+ additionalLocalFlowStepNodeCand2(mid, node2, config) and
not mid instanceof CastNode and
preservesValue = false and
t = getErasedNodeTypeBound(node2) and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
)
)
}
@@ -1371,311 +1148,380 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config,
+ Node node1, Node node2, boolean preservesValue, AccessPathFrontNil apf, Configuration config,
LocalCallContext callContext
) {
- localFlowStepPlus(node1, node2, preservesValue, t, config, callContext) and
+ localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, config)
}
-
- pragma[nomagic]
- predicate localFlowBigStepExt(
- NodeExt node1, NodeExt node2, boolean preservesValue, AccessPathFrontNil apf,
- Configuration config
- ) {
- localFlowBigStep(node1.getNode(), node2.getNode(), preservesValue, apf.getType(), config, _)
- or
- additionalLocalFlowStepExt(node1, node2, apf.getType(), config) and
- nodeCand2(node1, config) and
- nodeCand2(node2, unbind(config)) and
- preservesValue = false
- }
}
private import LocalFlowBigStep
pragma[nomagic]
-private predicate readExtCand2(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- readExt(node1, f, node2, config) and
- nodeCand2(node1, _, true, unbind(config)) and
+private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
+ read(node1, c, node2, config) and
+ nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- readStoreCand(f, unbind(config))
+ nodeCand2IsReadAndStored(c, unbind(config))
}
pragma[nomagic]
-private predicate storeExtCand2(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- storeExt(node1, f, node2, config) and
+private predicate storeCand2(
+ Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+) {
+ store(node1, tc, node2, contentType) and
nodeCand2(node1, config) and
- nodeCand2(node2, _, true, unbind(config)) and
- readStoreCand(f, unbind(config))
-}
-
-private newtype TAccessPathFront =
- TFrontNil(DataFlowType t) or
- TFrontHead(Content f)
-
-/**
- * The front of an `AccessPath`. This is either a head or a nil.
- */
-abstract private class AccessPathFront extends TAccessPathFront {
- abstract string toString();
-
- abstract DataFlowType getType();
-
- abstract boolean toBoolNonEmpty();
-
- predicate headUsesContent(Content f) { this = TFrontHead(f) }
-}
-
-private class AccessPathFrontNil extends AccessPathFront, TFrontNil {
- override string toString() {
- exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
- }
-
- override DataFlowType getType() { this = TFrontNil(result) }
-
- override boolean toBoolNonEmpty() { result = false }
-}
-
-private class AccessPathFrontHead extends AccessPathFront, TFrontHead {
- override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) }
-
- override DataFlowType getType() {
- exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
- }
-
- override boolean toBoolNonEmpty() { result = true }
+ nodeCand2(node2, _, _, true, unbind(config)) and
+ nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
}
/**
- * Holds if data can flow from a source to `node` with the given `apf`.
+ * Holds if `node` is reachable with access path front `apf` from a
+ * source in the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argApf` records the front of the
+ * access path of that argument.
*/
pragma[nomagic]
private predicate flowCandFwd(
- NodeExt node, boolean fromArg, AccessPathFront apf, Configuration config
+ Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
) {
- flowCandFwd0(node, fromArg, apf, config) and
- if node instanceof CastingNodeExt
- then compatibleTypes(node.getErasedNodeTypeBound(), apf.getType())
+ flowCandFwd0(node, fromArg, argApf, apf, config) and
+ if node instanceof CastingNode
+ then compatibleTypes(getErasedNodeTypeBound(node), apf.getType())
else any()
}
pragma[nomagic]
private predicate flowCandFwd0(
- NodeExt node, boolean fromArg, AccessPathFront apf, Configuration config
+ Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
) {
- nodeCand2(node, _, false, config) and
- config.isSource(node.getNode()) and
+ nodeCand2(node, _, _, false, config) and
+ config.isSource(node) and
fromArg = false and
- apf = TFrontNil(node.getErasedNodeTypeBound())
+ argApf = TAccessPathFrontNone() and
+ apf = TFrontNil(getErasedNodeTypeBound(node))
or
- exists(NodeExt mid |
- flowCandFwd(mid, fromArg, apf, config) and
- localFlowBigStepExt(mid, node, true, _, config)
+ exists(Node mid |
+ flowCandFwd(mid, fromArg, argApf, apf, config) and
+ localFlowBigStep(mid, node, true, _, config, _)
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(mid, fromArg, nil, config) and
- localFlowBigStepExt(mid, node, false, apf, config)
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(mid, fromArg, argApf, nil, config) and
+ localFlowBigStep(mid, node, false, apf, config, _)
)
or
- nodeCand2(node, unbind(config)) and
- (
- exists(NodeExt mid |
- flowCandFwd(mid, _, apf, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(mid, _, nil, config) and
- additionalJumpStepExt(mid, node, config) and
- fromArg = false and
- apf = TFrontNil(node.getErasedNodeTypeBound())
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowCandFwd(mid, _, apf, config) and
- flowIntoCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowCandFwd(mid, false, apf, config) and
- flowOutOfCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = false and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- flowCandFwd(mid, fromArg, apf, config) and
- argumentValueFlowsThrough(mid, node)
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil, DataFlowType t |
- flowCandFwd(mid, fromArg, nil, config) and
- argumentFlowsThrough(mid, node, t, config) and
- apf = TFrontNil(t)
- )
+ exists(Node mid |
+ flowCandFwd(mid, _, _, apf, config) and
+ nodeCand2(node, unbind(config)) and
+ jumpStep(mid, node, config) and
+ fromArg = false and
+ argApf = TAccessPathFrontNone()
)
or
- exists(NodeExt mid, Content f |
- flowCandFwd(mid, fromArg, _, config) and
- storeExtCand2(mid, f, node, config) and
- nodeCand2(node, _, true, unbind(config)) and
- apf.headUsesContent(f)
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(mid, _, _, nil, config) and
+ nodeCand2(node, unbind(config)) and
+ additionalJumpStep(mid, node, config) and
+ fromArg = false and
+ argApf = TAccessPathFrontNone() and
+ apf = TFrontNil(getErasedNodeTypeBound(node))
)
or
- exists(Content f |
- flowCandFwdRead(f, node, fromArg, config) and
- consCandFwd(f, apf, config) and
- nodeCand2(node, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ // store
+ exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ storeCand2(mid, tc, node, contentType, config) and
+ nodeCand2(node, _, _, true, unbind(config)) and
+ apf.headUsesContent(tc) and
+ compatibleTypes(apf0.getType(), contentType)
+ )
+ or
+ // read
+ exists(TypedContent tc |
+ flowCandFwdRead(tc, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(tc, apf, config) and
+ nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ )
+ or
+ // flow into a callable
+ flowCandFwdIn(_, node, _, _, apf, config) and
+ fromArg = true and
+ if nodeCand2(node, true, _, unbindBool(apf.toBoolNonEmpty()), config)
+ then argApf = TAccessPathFrontSome(apf)
+ else argApf = TAccessPathFrontNone()
+ or
+ // flow out of a callable
+ exists(DataFlowCall call |
+ flowCandFwdOut(call, node, fromArg, argApf, apf, config) and
+ fromArg = false
+ or
+ exists(AccessPathFront argApf0 |
+ flowCandFwdOutFromArg(call, node, argApf0, apf, config) and
+ flowCandFwdIsEntered(call, fromArg, argApf, argApf0, config)
+ )
)
}
pragma[nomagic]
-private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) {
- exists(NodeExt mid, NodeExt n |
- flowCandFwd(mid, _, apf, config) and
- storeExtCand2(mid, f, n, config) and
- nodeCand2(n, _, true, unbind(config)) and
- compatibleTypes(apf.getType(), f.getType())
+private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n, DataFlowType contentType |
+ flowCandFwd(mid, _, _, apf, config) and
+ storeCand2(mid, tc, n, contentType, config) and
+ nodeCand2(n, _, _, true, unbind(config)) and
+ compatibleTypes(apf.getType(), contentType)
)
}
pragma[nomagic]
-private predicate flowCandFwdRead(Content f, NodeExt node, boolean fromArg, Configuration config) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowCandFwd(mid, fromArg, apf0, config) and
- readExtCand2(mid, f, node, config) and
- apf0.headUsesContent(f)
+private predicate flowCandFwdRead0(
+ Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFrontHead apf, Configuration config
+) {
+ flowCandFwd(node1, fromArg, argApf, apf, config) and
+ readCand2(node1, c, node2, config) and
+ apf.headUsesContent(tc)
+}
+
+pragma[nomagic]
+private predicate flowCandFwdRead(
+ TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+) {
+ flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
+}
+
+pragma[nomagic]
+private predicate flowCandFwdIn(
+ DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ flowCandFwd(arg, fromArg, argApf, apf, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ flowCandFwd(ret, fromArg, argApf, apf, config) and
+ flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandFwdOutFromArg(
+ DataFlowCall call, Node node, AccessPathFront argApf, AccessPathFront apf, Configuration config
+) {
+ flowCandFwdOut(call, node, true, TAccessPathFrontSome(argApf), apf, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `flowCandFwd`.
+ */
+pragma[nomagic]
+private predicate flowCandFwdIsEntered(
+ DataFlowCall call, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ParameterNode p |
+ flowCandFwdIn(call, p, fromArg, argApf, apf, config) and
+ nodeCand2(p, true, TBooleanSome(_), unbindBool(apf.toBoolNonEmpty()), config)
)
}
/**
- * Holds if data can flow from a source to `node` with the given `apf` and
- * from there flow to a sink.
+ * Holds if `node` with access path front `apf` is part of a path from a
+ * source to a sink in the configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnApf`
+ * records the front of the access path of the returned value.
*/
pragma[nomagic]
-private predicate flowCand(NodeExt node, boolean toReturn, AccessPathFront apf, Configuration config) {
- flowCand0(node, toReturn, apf, config) and
- flowCandFwd(node, _, apf, config)
+private predicate flowCand(
+ Node node, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ flowCand0(node, toReturn, returnApf, apf, config) and
+ flowCandFwd(node, _, _, apf, config)
}
pragma[nomagic]
private predicate flowCand0(
- NodeExt node, boolean toReturn, AccessPathFront apf, Configuration config
+ Node node, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
) {
- flowCandFwd(node, _, apf, config) and
- config.isSink(node.getNode()) and
+ flowCandFwd(node, _, _, apf, config) and
+ config.isSink(node) and
toReturn = false and
+ returnApf = TAccessPathFrontNone() and
apf instanceof AccessPathFrontNil
or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flowCand(mid, toReturn, apf, config)
+ exists(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flowCand(mid, toReturn, returnApf, apf, config)
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- localFlowBigStepExt(node, mid, false, _, config) and
- flowCand(mid, toReturn, nil, config) and
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(node, _, _, apf, config) and
+ localFlowBigStep(node, mid, false, _, config, _) and
+ flowCand(mid, toReturn, returnApf, nil, config) and
apf instanceof AccessPathFrontNil
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flowCand(mid, _, apf, config) and
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flowCand(mid, _, _, apf, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone()
+ )
+ or
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(node, _, _, apf, config) and
+ additionalJumpStep(node, mid, config) and
+ flowCand(mid, _, _, nil, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone() and
+ apf instanceof AccessPathFrontNil
+ )
+ or
+ // store
+ exists(TypedContent tc |
+ flowCandStore(node, tc, apf, toReturn, returnApf, config) and
+ flowCandConsCand(tc, apf, config)
+ )
+ or
+ // read
+ exists(TypedContent tc, AccessPathFront apf0 |
+ flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(tc, apf0, config)
+ )
+ or
+ // flow into a callable
+ exists(DataFlowCall call |
+ flowCandIn(call, node, toReturn, returnApf, apf, config) and
toReturn = false
+ or
+ exists(AccessPathFront returnApf0 |
+ flowCandInToReturn(call, node, returnApf0, apf, config) and
+ flowCandIsReturned(call, toReturn, returnApf, returnApf0, config)
+ )
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- additionalJumpStepExt(node, mid, config) and
- flowCand(mid, _, nil, config) and
- toReturn = false and
- apf instanceof AccessPathFrontNil
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flowCand(mid, false, apf, config) and
- toReturn = false and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flowCand(mid, _, apf, config) and
- toReturn = true and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- argumentValueFlowsThrough(node, mid) and
- flowCand(mid, toReturn, apf, config)
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil |
- argumentFlowsThrough(node, mid, _, config) and
- flowCand(mid, toReturn, nil, config) and
- apf instanceof AccessPathFrontNil and
- flowCandFwd(node, _, apf, config)
- )
- or
- exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, apf0, config) and
- apf0.headUsesContent(f) and
- consCand(f, apf, config)
- )
- or
- exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, config) and
- apf.headUsesContent(f)
- )
+ // flow out of a callable
+ flowCandOut(_, node, _, _, apf, config) and
+ toReturn = true and
+ if flowCandFwd(node, true, _, apf, config)
+ then returnApf = TAccessPathFrontSome(apf)
+ else returnApf = TAccessPathFrontNone()
+}
+
+pragma[nomagic]
+private predicate readCandFwd(
+ Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
+) {
+ flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
}
pragma[nomagic]
private predicate flowCandRead(
- NodeExt node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
) {
- exists(NodeExt mid |
- readExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ readCandFwd(node, tc, apf, mid, config) and
+ flowCand(mid, toReturn, returnApf, apf0, config)
)
}
pragma[nomagic]
private predicate flowCandStore(
- NodeExt node, Content f, boolean toReturn, AccessPathFrontHead apf0, Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, Configuration config
) {
- exists(NodeExt mid |
- storeExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ flowCandFwd(node, _, _, apf, config) and
+ storeCand2(node, tc, mid, _, unbind(config)) and
+ flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
)
}
pragma[nomagic]
-private predicate consCand(Content f, AccessPathFront apf, Configuration config) {
- consCandFwd(f, apf, config) and
- exists(NodeExt n, AccessPathFrontHead apf0 |
- flowCandFwd(n, _, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandRead(n, f, _, apf, config)
+private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(tc, apf, config) and
+ flowCandRead(_, tc, _, _, _, apf, config)
+}
+
+pragma[nomagic]
+private predicate flowCandOut(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathFrontOption returnApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(Node out, boolean allowsFieldFlow |
+ flowCand(out, toReturn, returnApf, apf, config) and
+ flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandIn(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathFrontOption returnApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ flowCand(p, toReturn, returnApf, apf, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandInToReturn(
+ DataFlowCall call, ArgumentNode arg, AccessPathFront returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ flowCandIn(call, arg, true, TAccessPathFrontSome(returnApf), apf, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `flowCand`.
+ */
+pragma[nomagic]
+private predicate flowCandIsReturned(
+ DataFlowCall call, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ flowCandOut(call, ret, toReturn, returnApf, apf, config) and
+ flowCandFwd(ret, true, TAccessPathFrontSome(_), apf, config)
)
}
private newtype TAccessPath =
TNil(DataFlowType t) or
- TConsNil(Content f, DataFlowType t) { consCand(f, TFrontNil(t), _) } or
- TConsCons(Content f1, Content f2, int len) {
- consCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
+ TConsCons(TypedContent tc1, TypedContent tc2, int len) {
+ flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
}
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first two
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
* elements of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -1684,7 +1530,7 @@ private newtype TAccessPath =
abstract private class AccessPath extends TAccessPath {
abstract string toString();
- abstract Content getHead();
+ abstract TypedContent getHead();
abstract int len();
@@ -1695,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
/**
* Holds if this access path has `head` at the front and may be followed by `tail`.
*/
- abstract predicate pop(Content head, AccessPath tail);
+ abstract predicate pop(TypedContent head, AccessPath tail);
}
private class AccessPathNil extends AccessPath, TNil {
@@ -1705,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
override string toString() { result = concat(": " + ppReprType(t)) }
- override Content getHead() { none() }
+ override TypedContent getHead() { none() }
override int len() { result = 0 }
@@ -1713,357 +1559,469 @@ private class AccessPathNil extends AccessPath, TNil {
override AccessPathFront getFront() { result = TFrontNil(t) }
- override predicate pop(Content head, AccessPath tail) { none() }
+ override predicate pop(TypedContent head, AccessPath tail) { none() }
}
abstract private class AccessPathCons extends AccessPath { }
private class AccessPathConsNil extends AccessPathCons, TConsNil {
- private Content f;
+ private TypedContent tc;
private DataFlowType t;
- AccessPathConsNil() { this = TConsNil(f, t) }
+ AccessPathConsNil() { this = TConsNil(tc, t) }
override string toString() {
// The `concat` becomes "" if `ppReprType` has no result.
- result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
+ result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
}
- override Content getHead() { result = f }
+ override TypedContent getHead() { result = tc }
override int len() { result = 1 }
- override DataFlowType getType() { result = f.getContainerType() }
+ override DataFlowType getType() { result = tc.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f) }
+ override AccessPathFront getFront() { result = TFrontHead(tc) }
- override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
+ override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
}
private class AccessPathConsCons extends AccessPathCons, TConsCons {
- private Content f1;
- private Content f2;
+ private TypedContent tc1;
+ private TypedContent tc2;
private int len;
- AccessPathConsCons() { this = TConsCons(f1, f2, len) }
+ AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
override string toString() {
if len = 2
- then result = "[" + f1.toString() + ", " + f2.toString() + "]"
- else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
+ else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
}
- override Content getHead() { result = f1 }
+ override TypedContent getHead() { result = tc1 }
override int len() { result = len }
- override DataFlowType getType() { result = f1.getContainerType() }
+ override DataFlowType getType() { result = tc1.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f1) }
+ override AccessPathFront getFront() { result = TFrontHead(tc1) }
- override predicate pop(Content head, AccessPath tail) {
- head = f1 and
+ override predicate pop(TypedContent head, AccessPath tail) {
+ head = tc1 and
(
- tail = TConsCons(f2, _, len - 1)
+ tail = TConsCons(tc2, _, len - 1)
or
len = 2 and
- tail = TConsNil(f2, _)
+ tail = TConsNil(tc2, _)
)
}
}
-/** Gets the access path obtained by popping `f` from `ap`, if any. */
-private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
+/** Gets the access path obtained by popping `tc` from `ap`, if any. */
+private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
-/** Gets the access path obtained by pushing `f` onto `ap`. */
-private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
+/** Gets the access path obtained by pushing `tc` onto `ap`. */
+private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
+
+private newtype TAccessPathOption =
+ TAccessPathNone() or
+ TAccessPathSome(AccessPath ap)
+
+private class AccessPathOption extends TAccessPathOption {
+ string toString() {
+ this = TAccessPathNone() and result = ""
+ or
+ this = TAccessPathSome(any(AccessPath ap | result = ap.toString()))
+ }
+}
/**
- * Holds if data can flow from a source to `node` with the given `ap`.
+ * Holds if `node` is reachable with access path `ap` from a source in
+ * the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argAp` records the access path of that
+ * argument.
*/
private predicate flowFwd(
- NodeExt node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config
+ Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
) {
- flowFwd0(node, fromArg, apf, ap, config) and
- flowCand(node, _, apf, config)
+ flowFwd0(node, fromArg, argAp, apf, ap, config) and
+ flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
- NodeExt node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config
+ Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
) {
- flowCand(node, _, _, config) and
- config.isSource(node.getNode()) and
+ flowCand(node, _, _, _, config) and
+ config.isSource(node) and
fromArg = false and
- ap = TNil(node.getErasedNodeTypeBound()) and
+ argAp = TAccessPathNone() and
+ ap = TNil(getErasedNodeTypeBound(node)) and
apf = ap.(AccessPathNil).getFront()
or
- flowCand(node, _, _, unbind(config)) and
+ flowCand(node, _, _, _, unbind(config)) and
(
- exists(NodeExt mid |
- flowFwd(mid, fromArg, apf, ap, config) and
- localFlowBigStepExt(mid, node, true, _, config)
+ exists(Node mid |
+ flowFwd(mid, fromArg, argAp, apf, ap, config) and
+ localFlowBigStep(mid, node, true, _, config, _)
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(mid, fromArg, _, nil, config) and
- localFlowBigStepExt(mid, node, false, apf, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(mid, fromArg, argAp, _, nil, config) and
+ localFlowBigStep(mid, node, false, apf, config, _) and
apf = ap.(AccessPathNil).getFront()
)
or
- exists(NodeExt mid |
- flowFwd(mid, _, apf, ap, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
- )
- or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(mid, _, _, nil, config) and
- additionalJumpStepExt(mid, node, config) and
+ exists(Node mid |
+ flowFwd(mid, _, _, apf, ap, config) and
+ jumpStep(mid, node, config) and
fromArg = false and
- ap = TNil(node.getErasedNodeTypeBound()) and
- apf = ap.(AccessPathNil).getFront()
+ argAp = TAccessPathNone()
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowFwd(mid, _, apf, ap, config) and
- flowIntoCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowFwd(mid, false, apf, ap, config) and
- flowOutOfCallableNodeCand2(mid, node, allowsFieldFlow, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(mid, _, _, _, nil, config) and
+ additionalJumpStep(mid, node, config) and
fromArg = false and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- flowFwd(mid, fromArg, apf, ap, config) and
- argumentValueFlowsThrough(mid, node)
- )
- or
- exists(NodeExt mid, AccessPathNil nil, DataFlowType t |
- flowFwd(mid, fromArg, _, nil, config) and
- argumentFlowsThrough(mid, node, t, config) and
- ap = TNil(t) and
+ argAp = TAccessPathNone() and
+ ap = TNil(getErasedNodeTypeBound(node)) and
apf = ap.(AccessPathNil).getFront()
)
)
or
- exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, config) and
- ap = push(f, ap0)
+ // store
+ exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
+ or
+ // read
+ exists(TypedContent tc |
+ flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
+ flowFwdConsCand(tc, apf, ap, config)
)
or
- exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, config) and
- flowConsCandFwd(f, apf, ap, config)
+ // flow into a callable
+ flowFwdIn(_, node, _, _, apf, ap, config) and
+ fromArg = true and
+ if flowCand(node, true, _, apf, config)
+ then argAp = TAccessPathSome(ap)
+ else argAp = TAccessPathNone()
+ or
+ // flow out of a callable
+ exists(DataFlowCall call |
+ flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
+ fromArg = false
+ or
+ exists(AccessPath argAp0 |
+ flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and
+ flowFwdIsEntered(call, fromArg, argAp, argAp0, config)
+ )
)
}
pragma[nomagic]
private predicate flowFwdStore(
- NodeExt node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
- Configuration config
+ Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFront apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- flowFwdStore1(mid, f, node, apf0, apf, config)
+ exists(Node mid, AccessPathFront apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
+ flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
pragma[nomagic]
-private predicate flowFwdStore0(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, Configuration config
+private predicate storeCand(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
+ Configuration config
) {
- storeExtCand2(mid, f, node, config) and
- flowCand(mid, _, apf0, config)
+ storeCand2(mid, tc, node, _, config) and
+ flowCand(mid, _, _, apf0, config) and
+ apf.headUsesContent(tc)
}
pragma[noinline]
-private predicate flowFwdStore1(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, AccessPathFrontHead apf,
+private predicate flowFwdStore0(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
- flowFwdStore0(mid, f, node, apf0, config) and
- consCand(f, apf0, config) and
- apf.headUsesContent(f) and
- flowCand(node, _, apf, unbind(config))
+ storeCand(mid, tc, node, apf0, apf, config) and
+ flowCandConsCand(tc, apf0, config) and
+ flowCand(node, _, _, apf, unbind(config))
+}
+
+pragma[nomagic]
+private predicate flowFwdRead0(
+ Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
+ boolean fromArg, AccessPathOption argAp, Configuration config
+) {
+ flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
+ readCandFwd(node1, tc, apf0, node2, config)
}
pragma[nomagic]
private predicate flowFwdRead(
- NodeExt node, Content f, AccessPath ap0, boolean fromArg, Configuration config
+ Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- readExtCand2(mid, f, node, config) and
- apf0.headUsesContent(f) and
- flowCand(node, _, _, unbind(config))
+ exists(Node mid, TypedContent tc |
+ flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
+ flowCand(node, _, _, apf, unbind(config)) and
+ flowCandConsCand(tc, apf, unbind(config))
)
}
pragma[nomagic]
-private predicate flowConsCandFwd(
- Content f, AccessPathFront apf, AccessPath ap, Configuration config
+private predicate flowFwdConsCand(
+ TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
) {
- exists(NodeExt n |
- flowFwd(n, _, apf, ap, config) and
- flowFwdStore1(n, f, _, apf, _, config)
+ exists(Node n |
+ flowFwd(n, _, _, apf, ap, config) and
+ flowFwdStore0(n, tc, _, apf, _, config)
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdIn(
+ DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ flowFwd(arg, fromArg, argAp, apf, ap, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
+ flowCand(p, _, _, _, unbind(config))
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ flowFwd(ret, fromArg, argAp, apf, ap, config) and
+ flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
+ flowCand(node, _, _, _, unbind(config))
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdOutFromArg(
+ DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
+) {
+ flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `flowFwd`.
+ */
+pragma[nomagic]
+private predicate flowFwdIsEntered(
+ DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
+) {
+ exists(ParameterNode p, AccessPathFront apf |
+ flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and
+ flowCand(p, true, TAccessPathFrontSome(_), apf, config)
)
}
/**
- * Holds if data can flow from a source to `node` with the given `ap` and
- * from there flow to a sink.
+ * Holds if `node` with access path `ap` is part of a path from a source to
+ * a sink in the configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnAp`
+ * records the access path of the returned value.
*/
-private predicate flow(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flow0(node, toReturn, ap, config) and
- flowFwd(node, _, _, ap, config)
+private predicate flow(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flow0(node, toReturn, returnAp, ap, config) and
+ flowFwd(node, _, _, _, ap, config)
}
-private predicate flow0(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flowFwd(node, _, _, ap, config) and
- config.isSink(node.getNode()) and
+private predicate flow0(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flowFwd(node, _, _, _, ap, config) and
+ config.isSink(node) and
toReturn = false and
+ returnAp = TAccessPathNone() and
ap instanceof AccessPathNil
or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flow(mid, toReturn, ap, config)
+ exists(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flow(mid, toReturn, returnAp, ap, config)
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(node, _, _, ap, config) and
- localFlowBigStepExt(node, mid, false, _, config) and
- flow(mid, toReturn, nil, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(node, _, _, _, ap, config) and
+ localFlowBigStep(node, mid, false, _, config, _) and
+ flow(mid, toReturn, returnAp, nil, config) and
ap instanceof AccessPathNil
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flow(mid, _, ap, config) and
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flow(mid, _, _, ap, config) and
+ toReturn = false and
+ returnAp = TAccessPathNone()
+ )
+ or
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(node, _, _, _, ap, config) and
+ additionalJumpStep(node, mid, config) and
+ flow(mid, _, _, nil, config) and
+ toReturn = false and
+ returnAp = TAccessPathNone() and
+ ap instanceof AccessPathNil
+ )
+ or
+ // store
+ exists(TypedContent tc |
+ flowStore(tc, node, toReturn, returnAp, ap, config) and
+ flowConsCand(tc, ap, config)
+ )
+ or
+ // read
+ exists(Node mid, AccessPath ap0 |
+ readFlowFwd(node, _, mid, ap, ap0, config) and
+ flow(mid, toReturn, returnAp, ap0, config)
+ )
+ or
+ // flow into a callable
+ exists(DataFlowCall call |
+ flowIn(call, node, toReturn, returnAp, ap, config) and
toReturn = false
+ or
+ exists(AccessPath returnAp0 |
+ flowInToReturn(call, node, returnAp0, ap, config) and
+ flowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(node, _, _, ap, config) and
- additionalJumpStepExt(node, mid, config) and
- flow(mid, _, nil, config) and
- toReturn = false and
- ap instanceof AccessPathNil
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flow(mid, false, ap, config) and
- toReturn = false and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flow(mid, _, ap, config) and
- toReturn = true and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- argumentValueFlowsThrough(node, mid) and
- flow(mid, toReturn, ap, config)
- )
- or
- exists(NodeExt mid, AccessPathNil nil |
- argumentFlowsThrough(node, mid, _, config) and
- flow(mid, toReturn, nil, config) and
- ap instanceof AccessPathNil and
- flowFwd(node, _, _, ap, config)
- )
- or
- exists(Content f |
- flowStore(f, node, toReturn, ap, config) and
- flowConsCand(f, ap, config)
- )
- or
- exists(NodeExt mid, AccessPath ap0 |
- readFwd(node, _, mid, ap, ap0, config) and
- flow(mid, toReturn, ap0, config)
- )
+ // flow out of a callable
+ flowOut(_, node, _, _, ap, config) and
+ toReturn = true and
+ if flowFwd(node, true, TAccessPathSome(_), _, ap, config)
+ then returnAp = TAccessPathSome(ap)
+ else returnAp = TAccessPathNone()
}
pragma[nomagic]
-private predicate storeFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate storeFlowFwd(
+ Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- storeExtCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, config) and
- ap0 = push(f, ap)
+ storeCand2(node1, tc, node2, _, config) and
+ flowFwdStore(node2, tc, ap, _, _, _, config) and
+ ap0 = push(tc, ap)
}
pragma[nomagic]
private predicate flowStore(
- Content f, NodeExt node, boolean toReturn, AccessPath ap, Configuration config
+ TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
) {
- exists(NodeExt mid, AccessPath ap0 |
- storeFwd(node, f, mid, ap, ap0, config) and
- flow(mid, toReturn, ap0, config)
+ exists(Node mid, AccessPath ap0 |
+ storeFlowFwd(node, tc, mid, ap, ap0, config) and
+ flow(mid, toReturn, returnAp, ap0, config)
)
}
pragma[nomagic]
-private predicate readFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate readFlowFwd(
+ Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- readExtCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, config) and
- ap0 = pop(f, ap) and
- flowConsCandFwd(f, _, ap0, unbind(config))
+ exists(AccessPathFrontHead apf |
+ readCandFwd(node1, tc, apf, node2, config) and
+ flowFwdRead(node2, apf, ap, _, _, _, config) and
+ ap0 = pop(tc, ap) and
+ flowFwdConsCand(tc, _, ap0, unbind(config))
+ )
}
pragma[nomagic]
-private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
- exists(NodeExt n, NodeExt mid |
- flow(mid, _, ap, config) and
- readFwd(n, f, mid, _, ap, config)
+private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
+ exists(Node n, Node mid |
+ flow(mid, _, _, ap, config) and
+ readFlowFwd(n, tc, mid, _, ap, config)
+ )
+}
+
+pragma[nomagic]
+private predicate flowOut(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(Node out, boolean allowsFieldFlow |
+ flow(out, toReturn, returnAp, ap, config) and
+ flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config)
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowIn(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ flow(p, toReturn, returnAp, ap, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowInToReturn(
+ DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config
+) {
+ flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `flow`.
+ */
+pragma[nomagic]
+private predicate flowIsReturned(
+ DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ flowOut(call, ret, toReturn, returnAp, ap, config) and
+ flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
)
}
bindingset[conf, result]
private Configuration unbind(Configuration conf) { result >= conf and result <= conf }
-private predicate flow(Node n, Configuration config) { flow(TNormalNode(n), _, _, config) }
+private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) }
+
+pragma[noinline]
+private predicate parameterFlow(
+ ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config
+) {
+ flow(p, true, _, ap, config) and
+ c = p.getEnclosingCallable()
+}
private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParameterNode p, AccessPath ap) {
- exists(ReturnNodeExt ret, Configuration config | flow(TNormalNode(p), true, ap, config) |
- exists(Summary summary |
- parameterFlowReturn(p, ret, _, _, _, summary, config) and
- flow(ret, unbind(config))
- |
- // taint through
- summary = TSummaryTaint() and
- ap instanceof AccessPathNil
- or
- // taint setter
- summary = TSummaryTaintStore(_) and
- ap instanceof AccessPathNil
- or
- // taint getter
- summary = TSummaryReadTaint(ap.(AccessPathConsNil).getHead())
- )
- or
- exists(ContentOption contentIn |
- parameterValueFlowReturn(p, ret, _, contentIn, _) and
- flow(ret, unbind(config))
- |
- // value through/setter
- contentIn = TContentNone()
- or
- // value getter (+ setter)
- contentIn = TContentSome(ap.getHead())
- )
+ exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 |
+ parameterFlow(p, ap, ret.getEnclosingCallable(), config) and
+ flow(ret, true, TAccessPathSome(_), ap0, config) and
+ flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config)
)
}
@@ -2113,7 +2071,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, cc, sc, ap) and
config = mid.getConfiguration() and
- flow(TNormalNode(node), _, ap, unbind(config))
+ flow(node, _, _, ap, unbind(config))
)
} or
TPathNodeSink(Node node, Configuration config) {
@@ -2164,14 +2122,31 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
+ private predicate isHidden() {
+ nodeIsHidden(this.getNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ }
+
+ private PathNode getASuccessorIfHidden() {
+ this.isHidden() and
+ result = this.(PathNodeImpl).getASuccessorImpl()
+ }
+
/** Gets a successor of this node, if any. */
- PathNode getASuccessor() { none() }
+ final PathNode getASuccessor() {
+ result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
+ not this.isHidden() and
+ not result.isHidden()
+ }
/** Holds if this node is a source. */
predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
+ abstract PathNode getASuccessorImpl();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2246,7 +2221,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
result.getConfiguration() = unbind(this.getConfiguration())
}
- override PathNodeImpl getASuccessor() {
+ override PathNodeImpl getASuccessorImpl() {
// an intermediate step to another intermediate node
result = getSuccMid()
or
@@ -2283,7 +2258,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNode getASuccessor() { none() }
+ override PathNode getASuccessorImpl() { none() }
override predicate isSource() { config.isSource(node) }
}
@@ -2293,17 +2268,18 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
* a callable is recorded by `cc`.
*/
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
- exists(
- AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
- LocalCallContext localCC
- |
- pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
- localCC = getLocalCallContext(cc, enclosing)
+ exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
+ midnode = mid.getNode() and
+ conf = mid.getConfiguration() and
+ cc = mid.getCallContext() and
+ sc = mid.getSummaryCtx() and
+ localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
ap = ap0
or
- localFlowBigStep(midnode, node, false, ap.(AccessPathNil).getType(), conf, localCC) and
+ localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -2318,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
mid.getAp() instanceof AccessPathNil and
ap = TNil(getErasedNodeTypeBound(node))
or
- exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
+ exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2332,44 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
}
pragma[nomagic]
-private predicate pathIntoLocalStep(
- PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
- AccessPath ap0, Configuration conf
-) {
- midnode = mid.getNode() and
- cc = mid.getCallContext() and
- conf = mid.getConfiguration() and
- localFlowBigStep(midnode, _, _, _, conf, _) and
- enclosing = midnode.getEnclosingCallable() and
- sc = mid.getSummaryCtx() and
- ap0 = mid.getAp()
-}
-
-pragma[nomagic]
-private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
- readDirect(node1, f, node2) and
+private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ readCandFwd(node1, tc, _, node2, config) and
flow(node2, config)
}
pragma[nomagic]
-private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
+private predicate pathReadStep(
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+) {
ap0 = mid.getAp() and
- readCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
-private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- storeDirect(node1, f, node2) and
+private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ storeCand2(node1, tc, node2, _, config) and
flow(node2, config)
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
@@ -2399,11 +2363,11 @@ private predicate pathOutOfCallable1(
}
pragma[noinline]
-private Node getAnOutNodeCand(
+private Node getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config
) {
result = kind.getAnOutNode(call) and
- flow(TNormalNode(result), _, ap, config)
+ flow(result, _, _, ap, config)
}
/**
@@ -2415,7 +2379,7 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config |
pathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = getAnOutNodeCand(kind, call, ap, config)
+ out = getAnOutNodeFlow(kind, call, ap, config)
)
}
@@ -2439,7 +2403,7 @@ private predicate parameterCand(
DataFlowCallable callable, int i, AccessPath ap, Configuration config
) {
exists(ParameterNode p |
- flow(TNormalNode(p), _, ap, config) and
+ flow(p, _, _, ap, config) and
p.isParameterOf(callable, i)
)
}
@@ -2514,7 +2478,7 @@ pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
exists(DataFlowCall call, ReturnKindExt kind |
pathThroughCallable0(call, mid, kind, cc, ap) and
- out = getAnOutNodeCand(kind, call, ap, mid.getConfiguration())
+ out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration())
)
}
@@ -2555,10 +2519,7 @@ private module FlowExploration {
viableParamArg(_, node2, node1)
or
// flow out of a callable
- exists(DataFlowCall call, ReturnKindExt kind |
- getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = kind.getAnOutNode(call)
- )
+ viableReturnPosOut(_, getReturnPosition(node1), node2)
|
c1 = node1.getEnclosingCallable() and
c2 = node2.getEnclosingCallable() and
@@ -2603,10 +2564,10 @@ private module FlowExploration {
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
- TPartialCons(Content f, int len) { len in [1 .. 5] }
+ TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
* element of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -2615,7 +2576,7 @@ private module FlowExploration {
private class PartialAccessPath extends TPartialAccessPath {
abstract string toString();
- Content getHead() { this = TPartialCons(result, _) }
+ TypedContent getHead() { this = TPartialCons(result, _) }
int len() {
this = TPartialNil(_) and result = 0
@@ -2626,7 +2587,7 @@ private module FlowExploration {
DataFlowType getType() {
this = TPartialNil(result)
or
- exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
+ exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
@@ -2644,15 +2605,15 @@ private module FlowExploration {
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
override string toString() {
- exists(Content f, int len | this = TPartialCons(f, len) |
+ exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
if len = 1
- then result = "[" + f.toString() + "]"
- else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc.toString() + "]"
+ else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
override AccessPathFront getFront() {
- exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
+ exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
}
}
@@ -2828,11 +2789,12 @@ private module FlowExploration {
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
- exists(PartialAccessPath ap0, Content f |
- partialPathReadStep(mid, ap0, f, node, cc, config) and
+ exists(PartialAccessPath ap0, TypedContent tc |
+ partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, f, ap0, config)
+ apConsFwd(ap, tc, ap0, config) and
+ compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -2851,35 +2813,42 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
+ PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
+ PartialAccessPath ap2
) {
- ap1 = mid.getAp() and
- storeDirect(mid.getNode(), f, node) and
- ap2.getHead() = f and
- ap2.len() = unbindInt(ap1.len() + 1) and
- compatibleTypes(ap1.getType(), f.getType())
+ exists(Node midNode, DataFlowType contentType |
+ midNode = mid.getNode() and
+ ap1 = mid.getAp() and
+ store(midNode, tc, node, contentType) and
+ ap2.getHead() = tc and
+ ap2.len() = unbindInt(ap1.len() + 1) and
+ compatibleTypes(ap1.getType(), contentType)
+ )
}
pragma[nomagic]
private predicate apConsFwd(
- PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
+ PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
- partialPathStoreStep(mid, ap1, f, _, ap2) and
+ partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
+ PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
- ap = mid.getAp() and
- readStep(mid.getNode(), f, node) and
- ap.getHead() = f and
- config = mid.getConfiguration() and
- cc = mid.getCallContext()
+ exists(Node midNode |
+ midNode = mid.getNode() and
+ ap = mid.getAp() and
+ read(midNode, tc.getContent(), node) and
+ ap.getHead() = tc and
+ config = mid.getConfiguration() and
+ cc = mid.getCallContext()
+ )
}
private predicate partialPathOutOfCallable0(
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
index a1daeb66411..1aeedf717f7 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
*/
predicate isBarrier(Node node) { none() }
- /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */
- deprecated predicate isBarrierEdge(Node node1, Node node2) { none() }
-
/** Holds if data flow into `node` is prohibited. */
predicate isBarrierIn(Node node) { none() }
@@ -251,15 +248,11 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
-pragma[noinline]
-private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
- viableCallable(call) = result.getCallable() and
- kind = result.getKind()
-}
-
/**
- * Holds if `node` is reachable from a source in the given configuration
- * taking simple call contexts into consideration.
+ * Holds if `node` is reachable from a source in the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call.
*/
private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) {
not fullBarrier(node, config) and
@@ -293,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
exists(Node mid |
useFieldFlow(config) and
nodeCandFwd1(mid, fromArg, config) and
- storeDirect(mid, _, node) and
+ store(mid, _, node, _) and
not outBarrier(mid, config)
)
or
// read
- exists(Content f |
- nodeCandFwd1Read(f, node, fromArg, config) and
- storeCandFwd1(f, config) and
+ exists(Content c |
+ nodeCandFwd1Read(c, node, fromArg, config) and
+ nodeCandFwd1IsStored(c, config) and
not inBarrier(node, config)
)
or
@@ -317,13 +310,35 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
fromArg = false
or
nodeCandFwd1OutFromArg(call, node, config) and
- flowOutCandFwd1(call, fromArg, config)
+ nodeCandFwd1IsEntered(call, fromArg, config)
)
)
}
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
+pragma[nomagic]
+private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
+ exists(Node mid |
+ nodeCandFwd1(mid, fromArg, config) and
+ read(mid, c, node)
+ )
+}
+
+/**
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
+ exists(Node mid, Node node, TypedContent tc |
+ not fullBarrier(node, config) and
+ useFieldFlow(config) and
+ nodeCandFwd1(mid, config) and
+ store(mid, tc, node, _) and
+ c = tc.getContent()
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1ReturnPosition(
ReturnPosition pos, boolean fromArg, Configuration config
@@ -335,43 +350,10 @@ private predicate nodeCandFwd1ReturnPosition(
}
pragma[nomagic]
-private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
- exists(Node mid |
- nodeCandFwd1(mid, fromArg, config) and
- readDirect(mid, f, node)
- )
-}
-
-/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
- */
-pragma[nomagic]
-private predicate storeCandFwd1(Content f, Configuration config) {
- exists(Node mid, Node node |
- not fullBarrier(node, config) and
- useFieldFlow(config) and
- nodeCandFwd1(mid, config) and
- storeDirect(mid, f, node)
- )
-}
-
-pragma[nomagic]
-private predicate nodeCandFwd1ReturnKind(
- DataFlowCall call, ReturnKindExt kind, boolean fromArg, Configuration config
-) {
+private predicate nodeCandFwd1Out(DataFlowCall call, Node out, boolean fromArg, Configuration config) {
exists(ReturnPosition pos |
nodeCandFwd1ReturnPosition(pos, fromArg, config) and
- pos = viableReturnPos(call, kind)
- )
-}
-
-pragma[nomagic]
-private predicate nodeCandFwd1Out(
- DataFlowCall call, Node node, boolean fromArg, Configuration config
-) {
- exists(ReturnKindExt kind |
- nodeCandFwd1ReturnKind(call, kind, fromArg, config) and
- node = kind.getAnOutNode(call)
+ viableReturnPosOut(call, pos, out)
)
}
@@ -384,7 +366,7 @@ private predicate nodeCandFwd1OutFromArg(DataFlowCall call, Node node, Configura
* Holds if an argument to `call` is reached in the flow covered by `nodeCandFwd1`.
*/
pragma[nomagic]
-private predicate flowOutCandFwd1(DataFlowCall call, boolean fromArg, Configuration config) {
+private predicate nodeCandFwd1IsEntered(DataFlowCall call, boolean fromArg, Configuration config) {
exists(ArgumentNode arg |
nodeCandFwd1(arg, fromArg, config) and
viableParamArg(call, _, arg)
@@ -395,8 +377,11 @@ bindingset[result, b]
private boolean unbindBool(boolean b) { result != b.booleanNot() }
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is part of a path from a source to a sink in the
+ * configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink.
*/
pragma[nomagic]
private predicate nodeCand1(Node node, boolean toReturn, Configuration config) {
@@ -433,81 +418,86 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
)
or
// store
- exists(Content f |
- nodeCand1Store(f, node, toReturn, config) and
- readCand1(f, config)
+ exists(Content c |
+ nodeCand1Store(c, node, toReturn, config) and
+ nodeCand1IsRead(c, config)
)
or
// read
- exists(Node mid, Content f |
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ exists(Node mid, Content c |
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, toReturn, config)
)
or
// flow into a callable
exists(DataFlowCall call |
- nodeCand1Arg(call, node, false, config) and
+ nodeCand1In(call, node, false, config) and
toReturn = false
or
- nodeCand1ArgToReturn(call, node, config) and
- flowInCand1(call, toReturn, config)
+ nodeCand1InToReturn(call, node, config) and
+ nodeCand1IsReturned(call, toReturn, config)
)
or
// flow out of a callable
exists(ReturnPosition pos |
- nodeCand1ReturnPosition(pos, config) and
+ nodeCand1Out(pos, config) and
getReturnPosition(node) = pos and
toReturn = true
)
}
-pragma[nomagic]
-private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _, config) }
-
-pragma[nomagic]
-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`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate readCand1(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content c, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd1(node, unbind(config)) and
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, _, config)
)
}
pragma[nomagic]
-private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
- exists(Node mid |
+private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
+ exists(Node mid, TypedContent tc |
nodeCand1(mid, toReturn, config) and
- storeCandFwd1(f, unbind(config)) and
- storeDirect(node, f, mid)
+ nodeCandFwd1IsStored(c, unbind(config)) and
+ store(node, tc, mid, _) and
+ c = tc.getContent()
)
}
/**
- * Holds if `f` is the target of both a read and a store in the flow covered
+ * Holds if `c` is the target of both a read and a store in the flow covered
* by `nodeCand1`.
*/
-private predicate readStoreCand1(Content f, Configuration conf) {
- readCand1(f, conf) and
- nodeCand1Store(f, _, _, conf)
+private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
+ nodeCand1IsRead(c, conf) and
+ nodeCand1Store(c, _, _, conf)
}
pragma[nomagic]
-private predicate viableParamArgCandFwd1(
+private predicate viableReturnPosOutNodeCandFwd1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+) {
+ nodeCandFwd1ReturnPosition(pos, _, config) and
+ viableReturnPosOut(call, pos, out)
+}
+
+pragma[nomagic]
+private predicate nodeCand1Out(ReturnPosition pos, Configuration config) {
+ exists(DataFlowCall call, Node out |
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, config)
+ )
+}
+
+pragma[nomagic]
+private predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
) {
viableParamArg(call, p, arg) and
@@ -515,32 +505,35 @@ private predicate viableParamArgCandFwd1(
}
pragma[nomagic]
-private predicate nodeCand1Arg(
+private predicate nodeCand1In(
DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config
) {
exists(ParameterNode p |
nodeCand1(p, toReturn, config) and
- viableParamArgCandFwd1(call, p, arg, config)
+ viableParamArgNodeCandFwd1(call, p, arg, config)
)
}
pragma[nomagic]
-private predicate nodeCand1ArgToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
- nodeCand1Arg(call, arg, true, config)
+private predicate nodeCand1InToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
+ nodeCand1In(call, arg, true, config)
}
/**
* Holds if an output from `call` is reached in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate flowInCand1(DataFlowCall call, boolean toReturn, Configuration config) {
+private predicate nodeCand1IsReturned(DataFlowCall call, boolean toReturn, Configuration config) {
exists(Node out |
nodeCand1(out, toReturn, config) and
nodeCandFwd1OutFromArg(call, out, config)
)
}
-private predicate throughFlowNodeCand(Node node, Configuration config) {
+pragma[nomagic]
+private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _, config) }
+
+private predicate throughFlowNodeCand1(Node node, Configuration config) {
nodeCand1(node, true, config) and
not fullBarrier(node, config) and
not inBarrier(node, config) and
@@ -549,11 +542,11 @@ private predicate throughFlowNodeCand(Node node, Configuration config) {
/** Holds if flow may return from `callable`. */
pragma[nomagic]
-private predicate returnFlowCallableCand(
+private predicate returnFlowCallableNodeCand1(
DataFlowCallable callable, ReturnKindExt kind, Configuration config
) {
exists(ReturnNodeExt ret |
- throughFlowNodeCand(ret, config) and
+ throughFlowNodeCand1(ret, config) and
callable = ret.getEnclosingCallable() and
kind = ret.getKind()
)
@@ -563,10 +556,10 @@ private predicate returnFlowCallableCand(
* Holds if flow may enter through `p` and reach a return node making `p` a
* candidate for the origin of a summary.
*/
-private predicate parameterThroughFlowCand(ParameterNode p, Configuration config) {
+private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration config) {
exists(ReturnKindExt kind |
- throughFlowNodeCand(p, config) and
- returnFlowCallableCand(p.getEnclosingCallable(), kind, config) and
+ throughFlowNodeCand1(p, config) and
+ returnFlowCallableNodeCand1(p.getEnclosingCallable(), kind, config) and
// we don't expect a parameter to return stored in itself
not exists(int pos |
kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos)
@@ -575,420 +568,77 @@ private predicate parameterThroughFlowCand(ParameterNode p, Configuration config
}
pragma[nomagic]
-private predicate store(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
- nodeCand1(n2, unbind(config)) and
- (
- storeDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentSome(f), n2)
- )
-}
-
-pragma[nomagic]
-private predicate read(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
- nodeCand1(n2, unbind(config)) and
- (
- readDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentSome(f), TContentNone(), n2)
- )
-}
-
-/**
- * Holds if `p` can flow to `node` in the same callable with `summary`
- * representing the flow path. The type of the tracked object is `t2`, and if
- * the summary includes a store step, `t1` is the tracked type just prior to the
- * store, that is, the type of the stored object, otherwise `t1` is equal to `t2`.
- */
-pragma[nomagic]
-private predicate parameterFlow(
- ParameterNode p, Node node, DataFlowType t1, DataFlowType t2, Summary summary,
- Configuration config
-) {
- parameterThroughFlowCand(p, config) and
- p = node and
- t1 = getErasedNodeTypeBound(node) and
- t1 = t2 and
- summary = TSummaryVal()
- or
- throughFlowNodeCand(node, unbind(config)) and
- (
- exists(Node mid |
- parameterFlow(p, mid, t1, t2, summary, config) and
- localFlowStep(mid, node, config) and
- compatibleTypes(t2, getErasedNodeTypeBound(node))
- )
- or
- exists(Node mid, Summary midsum |
- parameterFlow(p, mid, _, _, midsum, config) and
- additionalLocalFlowStep(mid, node, config) and
- t1 = getErasedNodeTypeBound(node) and
- t1 = t2 and
- summary = midsum.additionalStep()
- )
- or
- // read step
- exists(Node mid, Content f, Summary midsum |
- parameterFlow(p, mid, _, _, midsum, config) and
- read(mid, f, node, config) and
- summary = midsum.readStep(f) and
- t1 = f.getType() and
- t1 = t2
- )
- or
- // store step
- exists(Node mid, Content f, Summary midsum |
- parameterFlow(p, mid, t1, /* t1 */ _, midsum, config) and
- store(mid, f, node, config) and
- summary = midsum.storeStep(f) and
- compatibleTypes(t1, f.getType()) and
- t2 = f.getContainerType()
- )
- or
- // value flow through a callable
- exists(Node arg |
- parameterFlow(p, arg, t1, t2, summary, config) and
- argumentValueFlowsThrough(_, arg, TContentNone(), TContentNone(), node) and
- compatibleTypes(t2, getErasedNodeTypeBound(node))
- )
- or
- // flow through a callable
- exists(Node arg, Summary s1, Summary s2 |
- parameterFlow(p, arg, _, _, s1, config) and
- argumentFlowsThrough(arg, node, t1, t2, s2, config) and
- summary = s1.compose(s2)
- )
- )
-}
-
-private predicate viableParamArgCand(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
-) {
- viableParamArg(call, p, arg) and
- nodeCand1(arg, unbind(config)) and
- nodeCand1(p, config) and
- not outBarrier(arg, config) and
- not inBarrier(p, config)
-}
-
-pragma[nomagic]
-private predicate parameterFlowReturn(
- ParameterNode p, ReturnNodeExt ret, ReturnKindExt kind, DataFlowType t1, DataFlowType t2,
- Summary summary, Configuration config
-) {
- parameterFlow(p, ret, t1, t2, summary, config) and
- kind = ret.getKind() and
- not summary.isPartial() and
- not exists(int pos | kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos))
-}
-
-pragma[nomagic]
-private predicate argumentFlowsThrough0(
- DataFlowCall call, ArgumentNode arg, ReturnKindExt kind, DataFlowType t1, DataFlowType t2,
- Summary summary, Configuration config
-) {
- exists(ParameterNode p |
- viableParamArgCand(call, p, arg, config) and
- parameterFlowReturn(p, _, kind, t1, t2, summary, config)
- )
-}
-
-/**
- * Holds if data can flow from `arg` to `out` through a call with `summary`
- * representing the flow path. The type of the tracked object is `t2`, and if
- * the summary includes a store step, `t1` is the tracked type just prior to the
- * store, that is, the type of the stored object, otherwise `t1` is equal to `t2`.
- */
-private predicate argumentFlowsThrough(
- ArgumentNode arg, Node out, DataFlowType t1, DataFlowType t2, Summary summary,
- Configuration config
-) {
- nodeCand1(out, unbind(config)) and
- not inBarrier(out, config) and
- compatibleTypes(t2, getErasedNodeTypeBound(out)) and
- exists(DataFlowCall call, ReturnKindExt kind |
- argumentFlowsThrough0(call, arg, kind, t1, t2, summary, config) and
- out = kind.getAnOutNode(call)
- )
-}
-
-pragma[noinline]
-private predicate readStoreNode(
- DataFlowCall call, ArgumentNode arg, Content f1, Configuration config
-) {
- exists(Content f2, Node out |
- argumentValueFlowsThrough(call, arg, TContentSome(f1), TContentSome(f2), out) and
- nodeCand1(out, config) and
- readStoreCand1(f2, unbind(config))
- )
-}
-
-private newtype TNodeExt =
- TNormalNode(Node node) { nodeCand1(node, _) } or
- TReadStoreNode(DataFlowCall call, ArgumentNode arg, Content f1, Configuration config) {
- nodeCand1(arg, config) and
- readStoreNode(call, arg, f1, config) and
- readStoreCand1(f1, unbind(config))
- } or
- TReadTaintNode(ArgumentNode arg, Content f, Configuration config) {
- argumentFlowsThrough(arg, _, _, _, TSummaryReadTaint(f), config)
- } or
- TTaintStoreNode(ArgumentNode arg, DataFlowType t, Configuration config) {
- argumentFlowsThrough(arg, _, t, _, TSummaryTaintStore(_), config)
- }
-
-/**
- * An extended data flow node. Either a normal node, or an intermediate node
- * used to split up a summarized flow steps.
- *
- * This is purely an internal implementation detail.
- */
-abstract private class NodeExt extends TNodeExt {
- /** Gets the underlying (normal) node, if any. */
- abstract Node getNode();
-
- abstract DataFlowType getErasedNodeTypeBound();
-
- abstract DataFlowCallable getEnclosingCallable();
-
- abstract predicate isCand1(Configuration config);
-
- abstract string toString();
-
- abstract predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- );
-}
-
-/** A `Node` at which a cast can occur such that the type should be checked. */
-abstract private class CastingNodeExt extends NodeExt { }
-
-private class NormalNodeExt extends NodeExt, TNormalNode {
- override Node getNode() { this = TNormalNode(result) }
-
- override DataFlowType getErasedNodeTypeBound() {
- result = getErasedRepr(this.getNode().getTypeBound())
- }
-
- override DataFlowCallable getEnclosingCallable() {
- result = this.getNode().getEnclosingCallable()
- }
-
- override predicate isCand1(Configuration config) { nodeCand1(this.getNode(), config) }
-
- override string toString() { result = this.getNode().toString() }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class NormalCastingNodeExt extends CastingNodeExt, NormalNodeExt {
- NormalCastingNodeExt() { this.getNode() instanceof CastingNode }
-}
-
-private class ReadStoreNodeExt extends CastingNodeExt, TReadStoreNode {
- private DataFlowCall call;
- private ArgumentNode arg;
- private Content f1;
- private Configuration config0;
-
- ReadStoreNodeExt() { this = TReadStoreNode(call, arg, f1, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = f1.getType() }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = "(inside) " + call.toString() + " [read " + f1 + "]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class ReadTaintNode extends NodeExt, TReadTaintNode {
- private ArgumentNode arg;
- private Content f;
- private Configuration config0;
-
- ReadTaintNode() { this = TReadTaintNode(arg, f, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = f.getType() }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = arg.toString() + " [read taint " + f + "]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class TaintStoreNode extends NodeExt, TTaintStoreNode {
- private ArgumentNode arg;
- private DataFlowType t;
- private Configuration config0;
-
- TaintStoreNode() { this = TTaintStoreNode(arg, t, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = t }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = arg.toString() + " [taint store]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private predicate additionalLocalFlowStepExt(
- NodeExt node1, NodeExt node2, DataFlowType t, Configuration config
-) {
- exists(ArgumentNode arg, Content f |
- node1 = TReadTaintNode(arg, f, config) and
- argumentFlowsThrough(arg, node2.getNode(), _, t, TSummaryReadTaint(f), config)
- )
- or
- node2 = TTaintStoreNode(node1.getNode(), t, config)
-}
-
-pragma[nomagic]
-private predicate readExt(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- read(node1.getNode(), f, node2.getNode(), config)
- or
- node2 = TReadStoreNode(_, node1.getNode(), f, config)
- or
- node2 = TReadTaintNode(node1.getNode(), f, config)
-}
-
-pragma[nomagic]
-private predicate storeExt(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- store(node1.getNode(), f, node2.getNode(), config)
- or
- exists(DataFlowCall call, ArgumentNode arg, Content f1, Node n2 |
- node1 = TReadStoreNode(call, arg, f1, config) and
- n2 = node2.getNode() and
- argumentValueFlowsThrough(call, arg, TContentSome(f1), TContentSome(f), n2) and
+private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
+ exists(TypedContent tc |
+ nodeCand1IsReadAndStored(c, config) and
nodeCand1(n2, unbind(config)) and
- readStoreCand1(f, unbind(config))
- )
- or
- exists(ArgumentNode arg, DataFlowType t |
- node1 = TTaintStoreNode(arg, t, config) and
- argumentFlowsThrough(arg, node2.getNode(), t, _, TSummaryTaintStore(f), config)
+ store(n1, tc, n2, _) and
+ c = tc.getContent()
)
}
-private predicate jumpStepExt(NodeExt node1, NodeExt node2, Configuration config) {
- jumpStep(node1.getNode(), node2.getNode(), config)
-}
-
-private predicate additionalJumpStepExt(NodeExt node1, NodeExt node2, Configuration config) {
- additionalJumpStep(node1.getNode(), node2.getNode(), config)
-}
-
-private predicate argumentValueFlowsThrough(NodeExt node1, NodeExt node2) {
- argumentValueFlowsThrough(_, node1.getNode(), TContentNone(), TContentNone(), node2.getNode())
-}
-
-private predicate argumentFlowsThrough(
- NodeExt arg, NodeExt out, DataFlowType t, Configuration config
-) {
- argumentFlowsThrough(arg.getNode(), out.getNode(), _, t, TSummaryTaint(), config)
-}
-
-/**
- * Holds if data can flow from `node1` to `node2` in one local step or a step
- * through a callable.
- */
-pragma[noinline]
-private predicate localFlowStepOrFlowThroughCallable(
- NodeExt node1, NodeExt node2, Configuration config
-) {
- exists(Node n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode()
- |
- nodeCand1(n1, config) and
- localFlowStep(n1, n2, config)
- or
- nodeCand1(n1, config) and
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentNone(), n2)
- )
-}
-
-/**
- * Holds if data can flow from `node1` to `node2` in one local step or a step
- * through a callable, in both cases using an additional flow step from the
- * configuration.
- */
-pragma[noinline]
-private predicate additionalLocalFlowStepOrFlowThroughCallable(
- NodeExt node1, NodeExt node2, Configuration config
-) {
- exists(Node n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode()
- |
- nodeCand1(n1, config) and
- additionalLocalFlowStep(n1, n2, config)
- or
- argumentFlowsThrough(n1, n2, _, _, TSummaryTaint(), config)
- )
- or
- additionalLocalFlowStepExt(node1, node2, _, config)
+pragma[nomagic]
+private predicate read(Node n1, Content c, Node n2, Configuration config) {
+ nodeCand1IsReadAndStored(c, config) and
+ nodeCand1(n2, unbind(config)) and
+ read(n1, c, n2)
}
pragma[noinline]
-private ReturnPosition getReturnPosition1(ReturnNodeExt node, Configuration config) {
- result = getReturnPosition(node) and
- nodeCand1(node, config)
+private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ localFlowStep(node1, node2, config)
+}
+
+pragma[noinline]
+private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ additionalLocalFlowStep(node1, node2, config)
+}
+
+pragma[nomagic]
+private predicate viableReturnPosOutNodeCand1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+) {
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, config)
}
/**
- * Holds if data can flow out of a callable from `node1` to `node2`, either
+ * Holds if data can flow out of `call` from `ret` to `out`, 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 flowOutOfCallableNodeCand1(ReturnNodeExt node1, Node node2, Configuration config) {
- nodeCand1(node2, config) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- exists(DataFlowCall call, ReturnKindExt kind |
- getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
- node2 = kind.getAnOutNode(call)
- )
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand1(
+ DataFlowCall call, ReturnNodeExt ret, Node out, Configuration config
+) {
+ viableReturnPosOutNodeCand1(call, getReturnPosition(ret), out, config) and
+ nodeCand1(ret, config) and
+ not outBarrier(ret, config) and
+ not inBarrier(out, config)
+}
+
+pragma[nomagic]
+private predicate viableParamArgNodeCand1(
+ DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+) {
+ viableParamArgNodeCandFwd1(call, p, arg, config) and
+ nodeCand1(arg, config)
}
/**
- * Holds if data can flow into a callable and that this step is part of a
+ * Holds if data can flow into `call` and that this step is part of a
* path from a source to a sink.
*/
-private predicate flowIntoCallableNodeCand1(
- ArgumentNode node1, ParameterNode node2, Configuration config
+pragma[nomagic]
+private predicate flowIntoCallNodeCand1(
+ DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config
) {
- viableParamArgCand(_, node2, node1, config)
+ viableParamArgNodeCand1(call, p, arg, config) and
+ nodeCand1(p, config) and
+ not outBarrier(arg, config) and
+ not inBarrier(p, config)
}
/**
@@ -999,7 +649,7 @@ private predicate flowIntoCallableNodeCand1(
private int branch(Node n1, Configuration conf) {
result =
strictcount(Node n |
- flowOutOfCallableNodeCand1(n1, n, conf) or flowIntoCallableNodeCand1(n1, n, conf)
+ flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1011,269 +661,391 @@ private int branch(Node n1, Configuration conf) {
private int join(Node n2, Configuration conf) {
result =
strictcount(Node n |
- flowOutOfCallableNodeCand1(n, n2, conf) or flowIntoCallableNodeCand1(n, n2, conf)
+ flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
/**
- * Holds if data can flow out of a callable from `node1` to `node2`, either
+ * Holds if data can flow out of `call` from `ret` to `out`, 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. The
* `allowsFieldFlow` flag indicates whether the branching is within the limit
* specified by the configuration.
*/
-private predicate flowOutOfCallableNodeCand1(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand1(
+ DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config
) {
- exists(ReturnNodeExt n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode() and
- flowOutOfCallableNodeCand1(n1, n2, config) and
- exists(int b, int j |
- b = branch(n1, config) and
- j = join(n2, config) and
- if b.minimum(j) <= config.fieldFlowBranchLimit()
- then allowsFieldFlow = true
- else allowsFieldFlow = false
- )
+ flowOutOfCallNodeCand1(call, ret, out, config) and
+ exists(int b, int j |
+ b = branch(ret, config) and
+ j = join(out, config) and
+ if b.minimum(j) <= config.fieldFlowBranchLimit()
+ then allowsFieldFlow = true
+ else allowsFieldFlow = false
)
}
/**
- * Holds if data can flow into a callable and that this step is part of a
+ * Holds if data can flow into `call` and that this step is part of a
* path from a source to a sink. The `allowsFieldFlow` flag indicates whether
* the branching is within the limit specified by the configuration.
*/
-private predicate flowIntoCallableNodeCand1(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+pragma[nomagic]
+private predicate flowIntoCallNodeCand1(
+ DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow,
+ Configuration config
) {
- exists(ArgumentNode n1, ParameterNode n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode() and
- flowIntoCallableNodeCand1(n1, n2, config) and
- exists(int b, int j |
- b = branch(n1, config) and
- j = join(n2, config) and
- if b.minimum(j) <= config.fieldFlowBranchLimit()
- then allowsFieldFlow = true
- else allowsFieldFlow = false
- )
+ flowIntoCallNodeCand1(call, arg, p, config) and
+ exists(int b, int j |
+ b = branch(arg, config) and
+ j = join(p, config) and
+ if b.minimum(j) <= config.fieldFlowBranchLimit()
+ then allowsFieldFlow = true
+ else allowsFieldFlow = false
)
}
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is reachable from a source in the configuration `config`.
+ * The Boolean `stored` records whether the tracked value is stored into a
+ * field of `node`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argStored` records whether the tracked
+ * value was stored into a field of the argument.
*/
-private predicate nodeCandFwd2(NodeExt node, boolean fromArg, boolean stored, Configuration config) {
- nodeCand1(node.getNode(), config) and
- config.isSource(node.getNode()) and
+private predicate nodeCandFwd2(
+ Node node, boolean fromArg, BooleanOption argStored, boolean stored, Configuration config
+) {
+ nodeCand1(node, config) and
+ config.isSource(node) and
fromArg = false and
+ argStored = TBooleanNone() and
stored = false
or
- node.isCand1(unbind(config)) and
+ nodeCand1(node, unbind(config)) and
(
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, stored, config) and
- localFlowStepOrFlowThroughCallable(mid, node, config)
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, stored, config) and
+ localFlowStepNodeCand1(mid, node, config)
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, stored, config) and
- additionalLocalFlowStepOrFlowThroughCallable(mid, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, stored, config) and
+ additionalLocalFlowStepNodeCand1(mid, node, config) and
stored = false
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, _, stored, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
+ exists(Node mid |
+ nodeCandFwd2(mid, _, _, stored, config) and
+ jumpStep(mid, node, config) and
+ fromArg = false and
+ argStored = TBooleanNone()
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, _, stored, config) and
- additionalJumpStepExt(mid, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, _, _, stored, config) and
+ additionalJumpStep(mid, node, config) and
fromArg = false and
+ argStored = TBooleanNone() and
stored = false
)
or
// store
- exists(NodeExt mid, Content f |
- nodeCandFwd2(mid, fromArg, _, config) and
- storeExt(mid, f, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, _, config) and
+ storeCand1(mid, _, node, config) and
stored = true
)
or
// read
- exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, config) and
- storeCandFwd2(f, stored, config)
+ exists(Content c |
+ nodeCandFwd2Read(c, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(c, stored, config)
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- nodeCandFwd2(mid, _, stored, config) and
- flowIntoCallableNodeCand1(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (stored = false or allowsFieldFlow = true)
- )
+ // flow into a callable
+ nodeCandFwd2In(_, node, _, _, stored, config) and
+ fromArg = true and
+ if parameterThroughFlowNodeCand1(node, config)
+ then argStored = TBooleanSome(stored)
+ else argStored = TBooleanNone()
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- nodeCandFwd2(mid, false, stored, config) and
- flowOutOfCallableNodeCand1(mid, node, allowsFieldFlow, config) and
- fromArg = false and
- (stored = false or allowsFieldFlow = true)
+ // flow out of a callable
+ exists(DataFlowCall call |
+ nodeCandFwd2Out(call, node, fromArg, argStored, stored, config) and
+ fromArg = false
+ or
+ exists(boolean argStored0 |
+ nodeCandFwd2OutFromArg(call, node, argStored0, stored, config) and
+ nodeCandFwd2IsEntered(call, fromArg, argStored, argStored0, config)
+ )
)
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
*/
pragma[noinline]
-private predicate storeCandFwd2(Content f, boolean stored, Configuration config) {
- exists(NodeExt mid, NodeExt node |
+private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
+ exists(Node mid, Node node |
useFieldFlow(config) and
- node.isCand1(unbind(config)) and
- nodeCandFwd2(mid, _, stored, config) and
- storeExt(mid, f, node, config)
+ nodeCand1(node, unbind(config)) and
+ nodeCandFwd2(mid, _, _, stored, config) and
+ storeCand1(mid, c, node, config)
)
}
pragma[nomagic]
-private predicate nodeCandFwd2Read(Content f, NodeExt node, boolean fromArg, Configuration config) {
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, true, config) and
- readExt(mid, f, node, config)
+private predicate nodeCandFwd2Read(
+ Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+) {
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, true, config) and
+ read(mid, c, node, config)
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2In(
+ DataFlowCall call, ParameterNode p, boolean fromArg, BooleanOption argStored, boolean stored,
+ Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ nodeCandFwd2(arg, fromArg, argStored, stored, config) and
+ flowIntoCallNodeCand1(call, arg, p, allowsFieldFlow, config)
+ |
+ stored = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2Out(
+ DataFlowCall call, Node out, boolean fromArg, BooleanOption argStored, boolean stored,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ nodeCandFwd2(ret, fromArg, argStored, stored, config) and
+ flowOutOfCallNodeCand1(call, ret, out, allowsFieldFlow, config)
+ |
+ stored = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2OutFromArg(
+ DataFlowCall call, Node out, boolean argStored, boolean stored, Configuration config
+) {
+ nodeCandFwd2Out(call, out, true, TBooleanSome(argStored), stored, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `nodeCandFwd2`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd2IsEntered(
+ DataFlowCall call, boolean fromArg, BooleanOption argStored, boolean stored, Configuration config
+) {
+ exists(ParameterNode p |
+ nodeCandFwd2In(call, p, fromArg, argStored, stored, config) and
+ parameterThroughFlowNodeCand1(p, config)
)
}
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is part of a path from a source to a sink in the
+ * configuration `config`. The Boolean `read` records whether the tracked
+ * value must be read from a field of `node` in order to reach a sink.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnRead`
+ * records whether a field must be read from the returned value.
*/
-private predicate nodeCand2(NodeExt node, boolean toReturn, boolean read, Configuration config) {
- nodeCandFwd2(node, _, false, config) and
- config.isSink(node.getNode()) and
+private predicate nodeCand2(
+ Node node, boolean toReturn, BooleanOption returnRead, boolean read, Configuration config
+) {
+ nodeCandFwd2(node, _, _, false, config) and
+ config.isSink(node) and
toReturn = false and
+ returnRead = TBooleanNone() and
read = false
or
- nodeCandFwd2(node, _, unbindBool(read), unbind(config)) and
+ nodeCandFwd2(node, _, _, unbindBool(read), unbind(config)) and
(
- exists(NodeExt mid |
- localFlowStepOrFlowThroughCallable(node, mid, config) and
- nodeCand2(mid, toReturn, read, config)
+ exists(Node mid |
+ localFlowStepNodeCand1(node, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, read, config)
)
or
- exists(NodeExt mid |
- additionalLocalFlowStepOrFlowThroughCallable(node, mid, config) and
- nodeCand2(mid, toReturn, read, config) and
+ exists(Node mid |
+ additionalLocalFlowStepNodeCand1(node, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, read, config) and
read = false
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- nodeCand2(mid, _, read, config) and
- toReturn = false
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ nodeCand2(mid, _, _, read, config) and
+ toReturn = false and
+ returnRead = TBooleanNone()
)
or
- exists(NodeExt mid |
- additionalJumpStepExt(node, mid, config) and
- nodeCand2(mid, _, read, config) and
+ exists(Node mid |
+ additionalJumpStep(node, mid, config) and
+ nodeCand2(mid, _, _, read, config) and
toReturn = false and
+ returnRead = TBooleanNone() and
read = false
)
or
// store
- exists(Content f |
- nodeCand2Store(f, node, toReturn, read, config) and
- readCand2(f, read, config)
+ exists(Content c |
+ nodeCand2Store(c, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(c, read, config)
)
or
// read
- exists(NodeExt mid, Content f, boolean read0 |
- readExt(node, f, mid, config) and
- storeCandFwd2(f, unbindBool(read0), unbind(config)) and
- nodeCand2(mid, toReturn, read0, config) and
+ exists(Node mid, Content c, boolean read0 |
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
+ nodeCand2(mid, toReturn, returnRead, read0, config) and
read = true
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand1(node, mid, allowsFieldFlow, config) and
- nodeCand2(mid, false, read, config) and
- toReturn = false and
- (read = false or allowsFieldFlow = true)
+ // flow into a callable
+ exists(DataFlowCall call |
+ nodeCand2In(call, node, toReturn, returnRead, read, config) and
+ toReturn = false
+ or
+ exists(boolean returnRead0 |
+ nodeCand2InToReturn(call, node, returnRead0, read, config) and
+ nodeCand2IsReturned(call, toReturn, returnRead, returnRead0, config)
+ )
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand1(node, mid, allowsFieldFlow, config) and
- nodeCand2(mid, _, read, config) and
- toReturn = true and
- (read = false or allowsFieldFlow = true)
- )
+ // flow out of a callable
+ nodeCand2Out(_, node, _, _, read, config) and
+ toReturn = true and
+ if nodeCandFwd2(node, true, TBooleanSome(_), unbindBool(read), config)
+ then returnRead = TBooleanSome(read)
+ else returnRead = TBooleanNone()
)
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate readCand2(Content f, boolean read, Configuration config) {
- exists(NodeExt mid, NodeExt node |
+private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
+ exists(Node mid, Node node |
useFieldFlow(config) and
- nodeCandFwd2(node, _, true, unbind(config)) and
- readExt(node, f, mid, config) and
- storeCandFwd2(f, unbindBool(read), unbind(config)) and
- nodeCand2(mid, _, read, config)
+ nodeCandFwd2(node, _, _, true, unbind(config)) and
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
+ nodeCand2(mid, _, _, read, config)
)
}
pragma[nomagic]
private predicate nodeCand2Store(
- Content f, NodeExt node, boolean toReturn, boolean stored, Configuration config
+ Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
+ Configuration config
) {
- exists(NodeExt mid |
- storeExt(node, f, mid, config) and
- nodeCand2(mid, toReturn, true, config) and
- nodeCandFwd2(node, _, stored, unbind(config))
- )
-}
-
-pragma[nomagic]
-private predicate storeCand2(Content f, boolean stored, Configuration conf) {
- exists(NodeExt node |
- nodeCand2Store(f, node, _, stored, conf) and
- nodeCand2(node, _, stored, conf)
+ exists(Node mid |
+ storeCand1(node, c, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, true, config) and
+ nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
/**
- * Holds if `f` is the target of both a store and a read in the path graph
- * covered by `nodeCand2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
*/
-pragma[noinline]
-private predicate readStoreCand(Content f, Configuration conf) {
- exists(boolean apNonEmpty |
- storeCand2(f, apNonEmpty, conf) and
- readCand2(f, apNonEmpty, conf)
+pragma[nomagic]
+private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
+ exists(Node node |
+ nodeCand2Store(c, node, _, _, stored, conf) and
+ nodeCand2(node, _, _, stored, conf)
)
}
-private predicate nodeCand2(NodeExt node, Configuration config) { nodeCand2(node, _, _, config) }
+/**
+ * Holds if `c` is the target of both a store and a read in the path graph
+ * covered by `nodeCand2`.
+ */
+pragma[noinline]
+private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
+ exists(boolean apNonEmpty |
+ nodeCand2IsStored(c, apNonEmpty, conf) and
+ nodeCand2IsRead(c, apNonEmpty, conf)
+ )
+}
pragma[nomagic]
-private predicate flowOutOfCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate nodeCand2Out(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
) {
- flowOutOfCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ exists(Node out, boolean allowsFieldFlow |
+ nodeCand2(out, toReturn, returnRead, read, config) and
+ flowOutOfCallNodeCand1(call, ret, out, allowsFieldFlow, config)
+ |
+ read = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCand2In(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ nodeCand2(p, toReturn, returnRead, read, config) and
+ flowIntoCallNodeCand1(call, arg, p, allowsFieldFlow, config)
+ |
+ read = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCand2InToReturn(
+ DataFlowCall call, ArgumentNode arg, boolean returnRead, boolean read, Configuration config
+) {
+ nodeCand2In(call, arg, true, TBooleanSome(returnRead), read, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `nodeCand2`.
+ */
+pragma[nomagic]
+private predicate nodeCand2IsReturned(
+ DataFlowCall call, boolean toReturn, BooleanOption returnRead, boolean read, Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ nodeCand2Out(call, ret, toReturn, returnRead, read, config) and
+ nodeCandFwd2(ret, true, TBooleanSome(_), read, config)
+ )
+}
+
+private predicate nodeCand2(Node node, Configuration config) { nodeCand2(node, _, _, _, config) }
+
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand2(
+ DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
+) {
+ flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
nodeCand2(node2, config) and
nodeCand2(node1, unbind(config))
}
pragma[nomagic]
-private predicate flowIntoCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate flowIntoCallNodeCand2(
+ DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ Configuration config
) {
- flowIntoCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
nodeCand2(node2, config) and
nodeCand2(node1, unbind(config))
}
@@ -1284,15 +1056,15 @@ private module LocalFlowBigStep {
* flow steps in a dataflow path.
*/
private predicate localFlowEntry(Node node, Configuration config) {
- nodeCand2(TNormalNode(node), config) and
+ nodeCand2(node, config) and
(
config.isSource(node) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
node instanceof ParameterNode or
- node instanceof OutNode or
- node instanceof PostUpdateNode or
- readDirect(_, _, node) or
+ node instanceof OutNodeExt or
+ store(_, _, node, _) or
+ read(_, _, node) or
node instanceof CastNode
)
}
@@ -1302,15 +1074,13 @@ private module LocalFlowBigStep {
* flow steps in a dataflow path.
*/
private predicate localFlowExit(Node node, Configuration config) {
- exists(Node next | nodeCand2(TNormalNode(next), config) |
+ exists(Node next | nodeCand2(next, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
- flowIntoCallableNodeCand1(node, next, config) or
- flowOutOfCallableNodeCand1(node, next, config) or
- argumentFlowsThrough(node, next, _, _, _, config) or
- argumentValueFlowsThrough(_, node, TContentNone(), TContentNone(), next) or
- storeDirect(node, _, next) or
- readDirect(node, _, next)
+ flowIntoCallNodeCand1(_, node, next, config) or
+ flowOutOfCallNodeCand1(_, node, next, config) or
+ store(node, _, next, _) or
+ read(node, _, next)
)
or
node instanceof CastNode
@@ -1318,6 +1088,13 @@ private module LocalFlowBigStep {
config.isSink(node)
}
+ pragma[noinline]
+ private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
+ additionalLocalFlowStepNodeCand1(node1, node2, config) and
+ nodeCand2(node1, _, _, false, config) and
+ nodeCand2(node2, _, _, false, unbind(config))
+ }
+
/**
* Holds if the local path from `node1` to `node2` is a prefix of a maximal
* subsequence of local flow steps in a dataflow path.
@@ -1334,33 +1111,33 @@ private module LocalFlowBigStep {
(
localFlowEntry(node1, config) and
(
- localFlowStep(node1, node2, config) and
+ localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
t = getErasedNodeTypeBound(node1)
or
- additionalLocalFlowStep(node1, node2, config) and
+ additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
t = getErasedNodeTypeBound(node2)
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
or
exists(Node mid |
localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
- localFlowStep(mid, node2, config) and
+ localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof CastNode and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
)
or
exists(Node mid |
localFlowStepPlus(node1, mid, _, _, config, cc) and
- additionalLocalFlowStep(mid, node2, config) and
+ additionalLocalFlowStepNodeCand2(mid, node2, config) and
not mid instanceof CastNode and
preservesValue = false and
t = getErasedNodeTypeBound(node2) and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
)
)
}
@@ -1371,311 +1148,380 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config,
+ Node node1, Node node2, boolean preservesValue, AccessPathFrontNil apf, Configuration config,
LocalCallContext callContext
) {
- localFlowStepPlus(node1, node2, preservesValue, t, config, callContext) and
+ localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, config)
}
-
- pragma[nomagic]
- predicate localFlowBigStepExt(
- NodeExt node1, NodeExt node2, boolean preservesValue, AccessPathFrontNil apf,
- Configuration config
- ) {
- localFlowBigStep(node1.getNode(), node2.getNode(), preservesValue, apf.getType(), config, _)
- or
- additionalLocalFlowStepExt(node1, node2, apf.getType(), config) and
- nodeCand2(node1, config) and
- nodeCand2(node2, unbind(config)) and
- preservesValue = false
- }
}
private import LocalFlowBigStep
pragma[nomagic]
-private predicate readExtCand2(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- readExt(node1, f, node2, config) and
- nodeCand2(node1, _, true, unbind(config)) and
+private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
+ read(node1, c, node2, config) and
+ nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- readStoreCand(f, unbind(config))
+ nodeCand2IsReadAndStored(c, unbind(config))
}
pragma[nomagic]
-private predicate storeExtCand2(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- storeExt(node1, f, node2, config) and
+private predicate storeCand2(
+ Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+) {
+ store(node1, tc, node2, contentType) and
nodeCand2(node1, config) and
- nodeCand2(node2, _, true, unbind(config)) and
- readStoreCand(f, unbind(config))
-}
-
-private newtype TAccessPathFront =
- TFrontNil(DataFlowType t) or
- TFrontHead(Content f)
-
-/**
- * The front of an `AccessPath`. This is either a head or a nil.
- */
-abstract private class AccessPathFront extends TAccessPathFront {
- abstract string toString();
-
- abstract DataFlowType getType();
-
- abstract boolean toBoolNonEmpty();
-
- predicate headUsesContent(Content f) { this = TFrontHead(f) }
-}
-
-private class AccessPathFrontNil extends AccessPathFront, TFrontNil {
- override string toString() {
- exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
- }
-
- override DataFlowType getType() { this = TFrontNil(result) }
-
- override boolean toBoolNonEmpty() { result = false }
-}
-
-private class AccessPathFrontHead extends AccessPathFront, TFrontHead {
- override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) }
-
- override DataFlowType getType() {
- exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
- }
-
- override boolean toBoolNonEmpty() { result = true }
+ nodeCand2(node2, _, _, true, unbind(config)) and
+ nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
}
/**
- * Holds if data can flow from a source to `node` with the given `apf`.
+ * Holds if `node` is reachable with access path front `apf` from a
+ * source in the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argApf` records the front of the
+ * access path of that argument.
*/
pragma[nomagic]
private predicate flowCandFwd(
- NodeExt node, boolean fromArg, AccessPathFront apf, Configuration config
+ Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
) {
- flowCandFwd0(node, fromArg, apf, config) and
- if node instanceof CastingNodeExt
- then compatibleTypes(node.getErasedNodeTypeBound(), apf.getType())
+ flowCandFwd0(node, fromArg, argApf, apf, config) and
+ if node instanceof CastingNode
+ then compatibleTypes(getErasedNodeTypeBound(node), apf.getType())
else any()
}
pragma[nomagic]
private predicate flowCandFwd0(
- NodeExt node, boolean fromArg, AccessPathFront apf, Configuration config
+ Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
) {
- nodeCand2(node, _, false, config) and
- config.isSource(node.getNode()) and
+ nodeCand2(node, _, _, false, config) and
+ config.isSource(node) and
fromArg = false and
- apf = TFrontNil(node.getErasedNodeTypeBound())
+ argApf = TAccessPathFrontNone() and
+ apf = TFrontNil(getErasedNodeTypeBound(node))
or
- exists(NodeExt mid |
- flowCandFwd(mid, fromArg, apf, config) and
- localFlowBigStepExt(mid, node, true, _, config)
+ exists(Node mid |
+ flowCandFwd(mid, fromArg, argApf, apf, config) and
+ localFlowBigStep(mid, node, true, _, config, _)
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(mid, fromArg, nil, config) and
- localFlowBigStepExt(mid, node, false, apf, config)
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(mid, fromArg, argApf, nil, config) and
+ localFlowBigStep(mid, node, false, apf, config, _)
)
or
- nodeCand2(node, unbind(config)) and
- (
- exists(NodeExt mid |
- flowCandFwd(mid, _, apf, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(mid, _, nil, config) and
- additionalJumpStepExt(mid, node, config) and
- fromArg = false and
- apf = TFrontNil(node.getErasedNodeTypeBound())
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowCandFwd(mid, _, apf, config) and
- flowIntoCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowCandFwd(mid, false, apf, config) and
- flowOutOfCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = false and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- flowCandFwd(mid, fromArg, apf, config) and
- argumentValueFlowsThrough(mid, node)
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil, DataFlowType t |
- flowCandFwd(mid, fromArg, nil, config) and
- argumentFlowsThrough(mid, node, t, config) and
- apf = TFrontNil(t)
- )
+ exists(Node mid |
+ flowCandFwd(mid, _, _, apf, config) and
+ nodeCand2(node, unbind(config)) and
+ jumpStep(mid, node, config) and
+ fromArg = false and
+ argApf = TAccessPathFrontNone()
)
or
- exists(NodeExt mid, Content f |
- flowCandFwd(mid, fromArg, _, config) and
- storeExtCand2(mid, f, node, config) and
- nodeCand2(node, _, true, unbind(config)) and
- apf.headUsesContent(f)
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(mid, _, _, nil, config) and
+ nodeCand2(node, unbind(config)) and
+ additionalJumpStep(mid, node, config) and
+ fromArg = false and
+ argApf = TAccessPathFrontNone() and
+ apf = TFrontNil(getErasedNodeTypeBound(node))
)
or
- exists(Content f |
- flowCandFwdRead(f, node, fromArg, config) and
- consCandFwd(f, apf, config) and
- nodeCand2(node, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ // store
+ exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ storeCand2(mid, tc, node, contentType, config) and
+ nodeCand2(node, _, _, true, unbind(config)) and
+ apf.headUsesContent(tc) and
+ compatibleTypes(apf0.getType(), contentType)
+ )
+ or
+ // read
+ exists(TypedContent tc |
+ flowCandFwdRead(tc, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(tc, apf, config) and
+ nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ )
+ or
+ // flow into a callable
+ flowCandFwdIn(_, node, _, _, apf, config) and
+ fromArg = true and
+ if nodeCand2(node, true, _, unbindBool(apf.toBoolNonEmpty()), config)
+ then argApf = TAccessPathFrontSome(apf)
+ else argApf = TAccessPathFrontNone()
+ or
+ // flow out of a callable
+ exists(DataFlowCall call |
+ flowCandFwdOut(call, node, fromArg, argApf, apf, config) and
+ fromArg = false
+ or
+ exists(AccessPathFront argApf0 |
+ flowCandFwdOutFromArg(call, node, argApf0, apf, config) and
+ flowCandFwdIsEntered(call, fromArg, argApf, argApf0, config)
+ )
)
}
pragma[nomagic]
-private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) {
- exists(NodeExt mid, NodeExt n |
- flowCandFwd(mid, _, apf, config) and
- storeExtCand2(mid, f, n, config) and
- nodeCand2(n, _, true, unbind(config)) and
- compatibleTypes(apf.getType(), f.getType())
+private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n, DataFlowType contentType |
+ flowCandFwd(mid, _, _, apf, config) and
+ storeCand2(mid, tc, n, contentType, config) and
+ nodeCand2(n, _, _, true, unbind(config)) and
+ compatibleTypes(apf.getType(), contentType)
)
}
pragma[nomagic]
-private predicate flowCandFwdRead(Content f, NodeExt node, boolean fromArg, Configuration config) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowCandFwd(mid, fromArg, apf0, config) and
- readExtCand2(mid, f, node, config) and
- apf0.headUsesContent(f)
+private predicate flowCandFwdRead0(
+ Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFrontHead apf, Configuration config
+) {
+ flowCandFwd(node1, fromArg, argApf, apf, config) and
+ readCand2(node1, c, node2, config) and
+ apf.headUsesContent(tc)
+}
+
+pragma[nomagic]
+private predicate flowCandFwdRead(
+ TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+) {
+ flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
+}
+
+pragma[nomagic]
+private predicate flowCandFwdIn(
+ DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ flowCandFwd(arg, fromArg, argApf, apf, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ flowCandFwd(ret, fromArg, argApf, apf, config) and
+ flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandFwdOutFromArg(
+ DataFlowCall call, Node node, AccessPathFront argApf, AccessPathFront apf, Configuration config
+) {
+ flowCandFwdOut(call, node, true, TAccessPathFrontSome(argApf), apf, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `flowCandFwd`.
+ */
+pragma[nomagic]
+private predicate flowCandFwdIsEntered(
+ DataFlowCall call, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ParameterNode p |
+ flowCandFwdIn(call, p, fromArg, argApf, apf, config) and
+ nodeCand2(p, true, TBooleanSome(_), unbindBool(apf.toBoolNonEmpty()), config)
)
}
/**
- * Holds if data can flow from a source to `node` with the given `apf` and
- * from there flow to a sink.
+ * Holds if `node` with access path front `apf` is part of a path from a
+ * source to a sink in the configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnApf`
+ * records the front of the access path of the returned value.
*/
pragma[nomagic]
-private predicate flowCand(NodeExt node, boolean toReturn, AccessPathFront apf, Configuration config) {
- flowCand0(node, toReturn, apf, config) and
- flowCandFwd(node, _, apf, config)
+private predicate flowCand(
+ Node node, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ flowCand0(node, toReturn, returnApf, apf, config) and
+ flowCandFwd(node, _, _, apf, config)
}
pragma[nomagic]
private predicate flowCand0(
- NodeExt node, boolean toReturn, AccessPathFront apf, Configuration config
+ Node node, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
) {
- flowCandFwd(node, _, apf, config) and
- config.isSink(node.getNode()) and
+ flowCandFwd(node, _, _, apf, config) and
+ config.isSink(node) and
toReturn = false and
+ returnApf = TAccessPathFrontNone() and
apf instanceof AccessPathFrontNil
or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flowCand(mid, toReturn, apf, config)
+ exists(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flowCand(mid, toReturn, returnApf, apf, config)
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- localFlowBigStepExt(node, mid, false, _, config) and
- flowCand(mid, toReturn, nil, config) and
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(node, _, _, apf, config) and
+ localFlowBigStep(node, mid, false, _, config, _) and
+ flowCand(mid, toReturn, returnApf, nil, config) and
apf instanceof AccessPathFrontNil
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flowCand(mid, _, apf, config) and
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flowCand(mid, _, _, apf, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone()
+ )
+ or
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(node, _, _, apf, config) and
+ additionalJumpStep(node, mid, config) and
+ flowCand(mid, _, _, nil, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone() and
+ apf instanceof AccessPathFrontNil
+ )
+ or
+ // store
+ exists(TypedContent tc |
+ flowCandStore(node, tc, apf, toReturn, returnApf, config) and
+ flowCandConsCand(tc, apf, config)
+ )
+ or
+ // read
+ exists(TypedContent tc, AccessPathFront apf0 |
+ flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(tc, apf0, config)
+ )
+ or
+ // flow into a callable
+ exists(DataFlowCall call |
+ flowCandIn(call, node, toReturn, returnApf, apf, config) and
toReturn = false
+ or
+ exists(AccessPathFront returnApf0 |
+ flowCandInToReturn(call, node, returnApf0, apf, config) and
+ flowCandIsReturned(call, toReturn, returnApf, returnApf0, config)
+ )
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- additionalJumpStepExt(node, mid, config) and
- flowCand(mid, _, nil, config) and
- toReturn = false and
- apf instanceof AccessPathFrontNil
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flowCand(mid, false, apf, config) and
- toReturn = false and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flowCand(mid, _, apf, config) and
- toReturn = true and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- argumentValueFlowsThrough(node, mid) and
- flowCand(mid, toReturn, apf, config)
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil |
- argumentFlowsThrough(node, mid, _, config) and
- flowCand(mid, toReturn, nil, config) and
- apf instanceof AccessPathFrontNil and
- flowCandFwd(node, _, apf, config)
- )
- or
- exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, apf0, config) and
- apf0.headUsesContent(f) and
- consCand(f, apf, config)
- )
- or
- exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, config) and
- apf.headUsesContent(f)
- )
+ // flow out of a callable
+ flowCandOut(_, node, _, _, apf, config) and
+ toReturn = true and
+ if flowCandFwd(node, true, _, apf, config)
+ then returnApf = TAccessPathFrontSome(apf)
+ else returnApf = TAccessPathFrontNone()
+}
+
+pragma[nomagic]
+private predicate readCandFwd(
+ Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
+) {
+ flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
}
pragma[nomagic]
private predicate flowCandRead(
- NodeExt node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
) {
- exists(NodeExt mid |
- readExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ readCandFwd(node, tc, apf, mid, config) and
+ flowCand(mid, toReturn, returnApf, apf0, config)
)
}
pragma[nomagic]
private predicate flowCandStore(
- NodeExt node, Content f, boolean toReturn, AccessPathFrontHead apf0, Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, Configuration config
) {
- exists(NodeExt mid |
- storeExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ flowCandFwd(node, _, _, apf, config) and
+ storeCand2(node, tc, mid, _, unbind(config)) and
+ flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
)
}
pragma[nomagic]
-private predicate consCand(Content f, AccessPathFront apf, Configuration config) {
- consCandFwd(f, apf, config) and
- exists(NodeExt n, AccessPathFrontHead apf0 |
- flowCandFwd(n, _, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandRead(n, f, _, apf, config)
+private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(tc, apf, config) and
+ flowCandRead(_, tc, _, _, _, apf, config)
+}
+
+pragma[nomagic]
+private predicate flowCandOut(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathFrontOption returnApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(Node out, boolean allowsFieldFlow |
+ flowCand(out, toReturn, returnApf, apf, config) and
+ flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandIn(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathFrontOption returnApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ flowCand(p, toReturn, returnApf, apf, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandInToReturn(
+ DataFlowCall call, ArgumentNode arg, AccessPathFront returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ flowCandIn(call, arg, true, TAccessPathFrontSome(returnApf), apf, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `flowCand`.
+ */
+pragma[nomagic]
+private predicate flowCandIsReturned(
+ DataFlowCall call, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ flowCandOut(call, ret, toReturn, returnApf, apf, config) and
+ flowCandFwd(ret, true, TAccessPathFrontSome(_), apf, config)
)
}
private newtype TAccessPath =
TNil(DataFlowType t) or
- TConsNil(Content f, DataFlowType t) { consCand(f, TFrontNil(t), _) } or
- TConsCons(Content f1, Content f2, int len) {
- consCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
+ TConsCons(TypedContent tc1, TypedContent tc2, int len) {
+ flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
}
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first two
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
* elements of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -1684,7 +1530,7 @@ private newtype TAccessPath =
abstract private class AccessPath extends TAccessPath {
abstract string toString();
- abstract Content getHead();
+ abstract TypedContent getHead();
abstract int len();
@@ -1695,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
/**
* Holds if this access path has `head` at the front and may be followed by `tail`.
*/
- abstract predicate pop(Content head, AccessPath tail);
+ abstract predicate pop(TypedContent head, AccessPath tail);
}
private class AccessPathNil extends AccessPath, TNil {
@@ -1705,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
override string toString() { result = concat(": " + ppReprType(t)) }
- override Content getHead() { none() }
+ override TypedContent getHead() { none() }
override int len() { result = 0 }
@@ -1713,357 +1559,469 @@ private class AccessPathNil extends AccessPath, TNil {
override AccessPathFront getFront() { result = TFrontNil(t) }
- override predicate pop(Content head, AccessPath tail) { none() }
+ override predicate pop(TypedContent head, AccessPath tail) { none() }
}
abstract private class AccessPathCons extends AccessPath { }
private class AccessPathConsNil extends AccessPathCons, TConsNil {
- private Content f;
+ private TypedContent tc;
private DataFlowType t;
- AccessPathConsNil() { this = TConsNil(f, t) }
+ AccessPathConsNil() { this = TConsNil(tc, t) }
override string toString() {
// The `concat` becomes "" if `ppReprType` has no result.
- result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
+ result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
}
- override Content getHead() { result = f }
+ override TypedContent getHead() { result = tc }
override int len() { result = 1 }
- override DataFlowType getType() { result = f.getContainerType() }
+ override DataFlowType getType() { result = tc.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f) }
+ override AccessPathFront getFront() { result = TFrontHead(tc) }
- override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
+ override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
}
private class AccessPathConsCons extends AccessPathCons, TConsCons {
- private Content f1;
- private Content f2;
+ private TypedContent tc1;
+ private TypedContent tc2;
private int len;
- AccessPathConsCons() { this = TConsCons(f1, f2, len) }
+ AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
override string toString() {
if len = 2
- then result = "[" + f1.toString() + ", " + f2.toString() + "]"
- else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
+ else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
}
- override Content getHead() { result = f1 }
+ override TypedContent getHead() { result = tc1 }
override int len() { result = len }
- override DataFlowType getType() { result = f1.getContainerType() }
+ override DataFlowType getType() { result = tc1.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f1) }
+ override AccessPathFront getFront() { result = TFrontHead(tc1) }
- override predicate pop(Content head, AccessPath tail) {
- head = f1 and
+ override predicate pop(TypedContent head, AccessPath tail) {
+ head = tc1 and
(
- tail = TConsCons(f2, _, len - 1)
+ tail = TConsCons(tc2, _, len - 1)
or
len = 2 and
- tail = TConsNil(f2, _)
+ tail = TConsNil(tc2, _)
)
}
}
-/** Gets the access path obtained by popping `f` from `ap`, if any. */
-private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
+/** Gets the access path obtained by popping `tc` from `ap`, if any. */
+private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
-/** Gets the access path obtained by pushing `f` onto `ap`. */
-private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
+/** Gets the access path obtained by pushing `tc` onto `ap`. */
+private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
+
+private newtype TAccessPathOption =
+ TAccessPathNone() or
+ TAccessPathSome(AccessPath ap)
+
+private class AccessPathOption extends TAccessPathOption {
+ string toString() {
+ this = TAccessPathNone() and result = ""
+ or
+ this = TAccessPathSome(any(AccessPath ap | result = ap.toString()))
+ }
+}
/**
- * Holds if data can flow from a source to `node` with the given `ap`.
+ * Holds if `node` is reachable with access path `ap` from a source in
+ * the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argAp` records the access path of that
+ * argument.
*/
private predicate flowFwd(
- NodeExt node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config
+ Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
) {
- flowFwd0(node, fromArg, apf, ap, config) and
- flowCand(node, _, apf, config)
+ flowFwd0(node, fromArg, argAp, apf, ap, config) and
+ flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
- NodeExt node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config
+ Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
) {
- flowCand(node, _, _, config) and
- config.isSource(node.getNode()) and
+ flowCand(node, _, _, _, config) and
+ config.isSource(node) and
fromArg = false and
- ap = TNil(node.getErasedNodeTypeBound()) and
+ argAp = TAccessPathNone() and
+ ap = TNil(getErasedNodeTypeBound(node)) and
apf = ap.(AccessPathNil).getFront()
or
- flowCand(node, _, _, unbind(config)) and
+ flowCand(node, _, _, _, unbind(config)) and
(
- exists(NodeExt mid |
- flowFwd(mid, fromArg, apf, ap, config) and
- localFlowBigStepExt(mid, node, true, _, config)
+ exists(Node mid |
+ flowFwd(mid, fromArg, argAp, apf, ap, config) and
+ localFlowBigStep(mid, node, true, _, config, _)
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(mid, fromArg, _, nil, config) and
- localFlowBigStepExt(mid, node, false, apf, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(mid, fromArg, argAp, _, nil, config) and
+ localFlowBigStep(mid, node, false, apf, config, _) and
apf = ap.(AccessPathNil).getFront()
)
or
- exists(NodeExt mid |
- flowFwd(mid, _, apf, ap, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
- )
- or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(mid, _, _, nil, config) and
- additionalJumpStepExt(mid, node, config) and
+ exists(Node mid |
+ flowFwd(mid, _, _, apf, ap, config) and
+ jumpStep(mid, node, config) and
fromArg = false and
- ap = TNil(node.getErasedNodeTypeBound()) and
- apf = ap.(AccessPathNil).getFront()
+ argAp = TAccessPathNone()
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowFwd(mid, _, apf, ap, config) and
- flowIntoCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowFwd(mid, false, apf, ap, config) and
- flowOutOfCallableNodeCand2(mid, node, allowsFieldFlow, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(mid, _, _, _, nil, config) and
+ additionalJumpStep(mid, node, config) and
fromArg = false and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- flowFwd(mid, fromArg, apf, ap, config) and
- argumentValueFlowsThrough(mid, node)
- )
- or
- exists(NodeExt mid, AccessPathNil nil, DataFlowType t |
- flowFwd(mid, fromArg, _, nil, config) and
- argumentFlowsThrough(mid, node, t, config) and
- ap = TNil(t) and
+ argAp = TAccessPathNone() and
+ ap = TNil(getErasedNodeTypeBound(node)) and
apf = ap.(AccessPathNil).getFront()
)
)
or
- exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, config) and
- ap = push(f, ap0)
+ // store
+ exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
+ or
+ // read
+ exists(TypedContent tc |
+ flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
+ flowFwdConsCand(tc, apf, ap, config)
)
or
- exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, config) and
- flowConsCandFwd(f, apf, ap, config)
+ // flow into a callable
+ flowFwdIn(_, node, _, _, apf, ap, config) and
+ fromArg = true and
+ if flowCand(node, true, _, apf, config)
+ then argAp = TAccessPathSome(ap)
+ else argAp = TAccessPathNone()
+ or
+ // flow out of a callable
+ exists(DataFlowCall call |
+ flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
+ fromArg = false
+ or
+ exists(AccessPath argAp0 |
+ flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and
+ flowFwdIsEntered(call, fromArg, argAp, argAp0, config)
+ )
)
}
pragma[nomagic]
private predicate flowFwdStore(
- NodeExt node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
- Configuration config
+ Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFront apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- flowFwdStore1(mid, f, node, apf0, apf, config)
+ exists(Node mid, AccessPathFront apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
+ flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
pragma[nomagic]
-private predicate flowFwdStore0(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, Configuration config
+private predicate storeCand(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
+ Configuration config
) {
- storeExtCand2(mid, f, node, config) and
- flowCand(mid, _, apf0, config)
+ storeCand2(mid, tc, node, _, config) and
+ flowCand(mid, _, _, apf0, config) and
+ apf.headUsesContent(tc)
}
pragma[noinline]
-private predicate flowFwdStore1(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, AccessPathFrontHead apf,
+private predicate flowFwdStore0(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
- flowFwdStore0(mid, f, node, apf0, config) and
- consCand(f, apf0, config) and
- apf.headUsesContent(f) and
- flowCand(node, _, apf, unbind(config))
+ storeCand(mid, tc, node, apf0, apf, config) and
+ flowCandConsCand(tc, apf0, config) and
+ flowCand(node, _, _, apf, unbind(config))
+}
+
+pragma[nomagic]
+private predicate flowFwdRead0(
+ Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
+ boolean fromArg, AccessPathOption argAp, Configuration config
+) {
+ flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
+ readCandFwd(node1, tc, apf0, node2, config)
}
pragma[nomagic]
private predicate flowFwdRead(
- NodeExt node, Content f, AccessPath ap0, boolean fromArg, Configuration config
+ Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- readExtCand2(mid, f, node, config) and
- apf0.headUsesContent(f) and
- flowCand(node, _, _, unbind(config))
+ exists(Node mid, TypedContent tc |
+ flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
+ flowCand(node, _, _, apf, unbind(config)) and
+ flowCandConsCand(tc, apf, unbind(config))
)
}
pragma[nomagic]
-private predicate flowConsCandFwd(
- Content f, AccessPathFront apf, AccessPath ap, Configuration config
+private predicate flowFwdConsCand(
+ TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
) {
- exists(NodeExt n |
- flowFwd(n, _, apf, ap, config) and
- flowFwdStore1(n, f, _, apf, _, config)
+ exists(Node n |
+ flowFwd(n, _, _, apf, ap, config) and
+ flowFwdStore0(n, tc, _, apf, _, config)
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdIn(
+ DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ flowFwd(arg, fromArg, argAp, apf, ap, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
+ flowCand(p, _, _, _, unbind(config))
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ flowFwd(ret, fromArg, argAp, apf, ap, config) and
+ flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
+ flowCand(node, _, _, _, unbind(config))
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdOutFromArg(
+ DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
+) {
+ flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `flowFwd`.
+ */
+pragma[nomagic]
+private predicate flowFwdIsEntered(
+ DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
+) {
+ exists(ParameterNode p, AccessPathFront apf |
+ flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and
+ flowCand(p, true, TAccessPathFrontSome(_), apf, config)
)
}
/**
- * Holds if data can flow from a source to `node` with the given `ap` and
- * from there flow to a sink.
+ * Holds if `node` with access path `ap` is part of a path from a source to
+ * a sink in the configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnAp`
+ * records the access path of the returned value.
*/
-private predicate flow(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flow0(node, toReturn, ap, config) and
- flowFwd(node, _, _, ap, config)
+private predicate flow(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flow0(node, toReturn, returnAp, ap, config) and
+ flowFwd(node, _, _, _, ap, config)
}
-private predicate flow0(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flowFwd(node, _, _, ap, config) and
- config.isSink(node.getNode()) and
+private predicate flow0(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flowFwd(node, _, _, _, ap, config) and
+ config.isSink(node) and
toReturn = false and
+ returnAp = TAccessPathNone() and
ap instanceof AccessPathNil
or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flow(mid, toReturn, ap, config)
+ exists(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flow(mid, toReturn, returnAp, ap, config)
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(node, _, _, ap, config) and
- localFlowBigStepExt(node, mid, false, _, config) and
- flow(mid, toReturn, nil, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(node, _, _, _, ap, config) and
+ localFlowBigStep(node, mid, false, _, config, _) and
+ flow(mid, toReturn, returnAp, nil, config) and
ap instanceof AccessPathNil
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flow(mid, _, ap, config) and
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flow(mid, _, _, ap, config) and
+ toReturn = false and
+ returnAp = TAccessPathNone()
+ )
+ or
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(node, _, _, _, ap, config) and
+ additionalJumpStep(node, mid, config) and
+ flow(mid, _, _, nil, config) and
+ toReturn = false and
+ returnAp = TAccessPathNone() and
+ ap instanceof AccessPathNil
+ )
+ or
+ // store
+ exists(TypedContent tc |
+ flowStore(tc, node, toReturn, returnAp, ap, config) and
+ flowConsCand(tc, ap, config)
+ )
+ or
+ // read
+ exists(Node mid, AccessPath ap0 |
+ readFlowFwd(node, _, mid, ap, ap0, config) and
+ flow(mid, toReturn, returnAp, ap0, config)
+ )
+ or
+ // flow into a callable
+ exists(DataFlowCall call |
+ flowIn(call, node, toReturn, returnAp, ap, config) and
toReturn = false
+ or
+ exists(AccessPath returnAp0 |
+ flowInToReturn(call, node, returnAp0, ap, config) and
+ flowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(node, _, _, ap, config) and
- additionalJumpStepExt(node, mid, config) and
- flow(mid, _, nil, config) and
- toReturn = false and
- ap instanceof AccessPathNil
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flow(mid, false, ap, config) and
- toReturn = false and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flow(mid, _, ap, config) and
- toReturn = true and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- argumentValueFlowsThrough(node, mid) and
- flow(mid, toReturn, ap, config)
- )
- or
- exists(NodeExt mid, AccessPathNil nil |
- argumentFlowsThrough(node, mid, _, config) and
- flow(mid, toReturn, nil, config) and
- ap instanceof AccessPathNil and
- flowFwd(node, _, _, ap, config)
- )
- or
- exists(Content f |
- flowStore(f, node, toReturn, ap, config) and
- flowConsCand(f, ap, config)
- )
- or
- exists(NodeExt mid, AccessPath ap0 |
- readFwd(node, _, mid, ap, ap0, config) and
- flow(mid, toReturn, ap0, config)
- )
+ // flow out of a callable
+ flowOut(_, node, _, _, ap, config) and
+ toReturn = true and
+ if flowFwd(node, true, TAccessPathSome(_), _, ap, config)
+ then returnAp = TAccessPathSome(ap)
+ else returnAp = TAccessPathNone()
}
pragma[nomagic]
-private predicate storeFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate storeFlowFwd(
+ Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- storeExtCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, config) and
- ap0 = push(f, ap)
+ storeCand2(node1, tc, node2, _, config) and
+ flowFwdStore(node2, tc, ap, _, _, _, config) and
+ ap0 = push(tc, ap)
}
pragma[nomagic]
private predicate flowStore(
- Content f, NodeExt node, boolean toReturn, AccessPath ap, Configuration config
+ TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
) {
- exists(NodeExt mid, AccessPath ap0 |
- storeFwd(node, f, mid, ap, ap0, config) and
- flow(mid, toReturn, ap0, config)
+ exists(Node mid, AccessPath ap0 |
+ storeFlowFwd(node, tc, mid, ap, ap0, config) and
+ flow(mid, toReturn, returnAp, ap0, config)
)
}
pragma[nomagic]
-private predicate readFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate readFlowFwd(
+ Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- readExtCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, config) and
- ap0 = pop(f, ap) and
- flowConsCandFwd(f, _, ap0, unbind(config))
+ exists(AccessPathFrontHead apf |
+ readCandFwd(node1, tc, apf, node2, config) and
+ flowFwdRead(node2, apf, ap, _, _, _, config) and
+ ap0 = pop(tc, ap) and
+ flowFwdConsCand(tc, _, ap0, unbind(config))
+ )
}
pragma[nomagic]
-private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
- exists(NodeExt n, NodeExt mid |
- flow(mid, _, ap, config) and
- readFwd(n, f, mid, _, ap, config)
+private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
+ exists(Node n, Node mid |
+ flow(mid, _, _, ap, config) and
+ readFlowFwd(n, tc, mid, _, ap, config)
+ )
+}
+
+pragma[nomagic]
+private predicate flowOut(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(Node out, boolean allowsFieldFlow |
+ flow(out, toReturn, returnAp, ap, config) and
+ flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config)
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowIn(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ flow(p, toReturn, returnAp, ap, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowInToReturn(
+ DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config
+) {
+ flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `flow`.
+ */
+pragma[nomagic]
+private predicate flowIsReturned(
+ DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ flowOut(call, ret, toReturn, returnAp, ap, config) and
+ flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
)
}
bindingset[conf, result]
private Configuration unbind(Configuration conf) { result >= conf and result <= conf }
-private predicate flow(Node n, Configuration config) { flow(TNormalNode(n), _, _, config) }
+private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) }
+
+pragma[noinline]
+private predicate parameterFlow(
+ ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config
+) {
+ flow(p, true, _, ap, config) and
+ c = p.getEnclosingCallable()
+}
private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParameterNode p, AccessPath ap) {
- exists(ReturnNodeExt ret, Configuration config | flow(TNormalNode(p), true, ap, config) |
- exists(Summary summary |
- parameterFlowReturn(p, ret, _, _, _, summary, config) and
- flow(ret, unbind(config))
- |
- // taint through
- summary = TSummaryTaint() and
- ap instanceof AccessPathNil
- or
- // taint setter
- summary = TSummaryTaintStore(_) and
- ap instanceof AccessPathNil
- or
- // taint getter
- summary = TSummaryReadTaint(ap.(AccessPathConsNil).getHead())
- )
- or
- exists(ContentOption contentIn |
- parameterValueFlowReturn(p, ret, _, contentIn, _) and
- flow(ret, unbind(config))
- |
- // value through/setter
- contentIn = TContentNone()
- or
- // value getter (+ setter)
- contentIn = TContentSome(ap.getHead())
- )
+ exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 |
+ parameterFlow(p, ap, ret.getEnclosingCallable(), config) and
+ flow(ret, true, TAccessPathSome(_), ap0, config) and
+ flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config)
)
}
@@ -2113,7 +2071,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, cc, sc, ap) and
config = mid.getConfiguration() and
- flow(TNormalNode(node), _, ap, unbind(config))
+ flow(node, _, _, ap, unbind(config))
)
} or
TPathNodeSink(Node node, Configuration config) {
@@ -2164,14 +2122,31 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
+ private predicate isHidden() {
+ nodeIsHidden(this.getNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ }
+
+ private PathNode getASuccessorIfHidden() {
+ this.isHidden() and
+ result = this.(PathNodeImpl).getASuccessorImpl()
+ }
+
/** Gets a successor of this node, if any. */
- PathNode getASuccessor() { none() }
+ final PathNode getASuccessor() {
+ result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
+ not this.isHidden() and
+ not result.isHidden()
+ }
/** Holds if this node is a source. */
predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
+ abstract PathNode getASuccessorImpl();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2246,7 +2221,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
result.getConfiguration() = unbind(this.getConfiguration())
}
- override PathNodeImpl getASuccessor() {
+ override PathNodeImpl getASuccessorImpl() {
// an intermediate step to another intermediate node
result = getSuccMid()
or
@@ -2283,7 +2258,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNode getASuccessor() { none() }
+ override PathNode getASuccessorImpl() { none() }
override predicate isSource() { config.isSource(node) }
}
@@ -2293,17 +2268,18 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
* a callable is recorded by `cc`.
*/
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
- exists(
- AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
- LocalCallContext localCC
- |
- pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
- localCC = getLocalCallContext(cc, enclosing)
+ exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
+ midnode = mid.getNode() and
+ conf = mid.getConfiguration() and
+ cc = mid.getCallContext() and
+ sc = mid.getSummaryCtx() and
+ localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
ap = ap0
or
- localFlowBigStep(midnode, node, false, ap.(AccessPathNil).getType(), conf, localCC) and
+ localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -2318,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
mid.getAp() instanceof AccessPathNil and
ap = TNil(getErasedNodeTypeBound(node))
or
- exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
+ exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2332,44 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
}
pragma[nomagic]
-private predicate pathIntoLocalStep(
- PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
- AccessPath ap0, Configuration conf
-) {
- midnode = mid.getNode() and
- cc = mid.getCallContext() and
- conf = mid.getConfiguration() and
- localFlowBigStep(midnode, _, _, _, conf, _) and
- enclosing = midnode.getEnclosingCallable() and
- sc = mid.getSummaryCtx() and
- ap0 = mid.getAp()
-}
-
-pragma[nomagic]
-private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
- readDirect(node1, f, node2) and
+private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ readCandFwd(node1, tc, _, node2, config) and
flow(node2, config)
}
pragma[nomagic]
-private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
+private predicate pathReadStep(
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+) {
ap0 = mid.getAp() and
- readCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
-private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- storeDirect(node1, f, node2) and
+private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ storeCand2(node1, tc, node2, _, config) and
flow(node2, config)
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
@@ -2399,11 +2363,11 @@ private predicate pathOutOfCallable1(
}
pragma[noinline]
-private Node getAnOutNodeCand(
+private Node getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config
) {
result = kind.getAnOutNode(call) and
- flow(TNormalNode(result), _, ap, config)
+ flow(result, _, _, ap, config)
}
/**
@@ -2415,7 +2379,7 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config |
pathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = getAnOutNodeCand(kind, call, ap, config)
+ out = getAnOutNodeFlow(kind, call, ap, config)
)
}
@@ -2439,7 +2403,7 @@ private predicate parameterCand(
DataFlowCallable callable, int i, AccessPath ap, Configuration config
) {
exists(ParameterNode p |
- flow(TNormalNode(p), _, ap, config) and
+ flow(p, _, _, ap, config) and
p.isParameterOf(callable, i)
)
}
@@ -2514,7 +2478,7 @@ pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
exists(DataFlowCall call, ReturnKindExt kind |
pathThroughCallable0(call, mid, kind, cc, ap) and
- out = getAnOutNodeCand(kind, call, ap, mid.getConfiguration())
+ out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration())
)
}
@@ -2555,10 +2519,7 @@ private module FlowExploration {
viableParamArg(_, node2, node1)
or
// flow out of a callable
- exists(DataFlowCall call, ReturnKindExt kind |
- getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = kind.getAnOutNode(call)
- )
+ viableReturnPosOut(_, getReturnPosition(node1), node2)
|
c1 = node1.getEnclosingCallable() and
c2 = node2.getEnclosingCallable() and
@@ -2603,10 +2564,10 @@ private module FlowExploration {
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
- TPartialCons(Content f, int len) { len in [1 .. 5] }
+ TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
* element of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -2615,7 +2576,7 @@ private module FlowExploration {
private class PartialAccessPath extends TPartialAccessPath {
abstract string toString();
- Content getHead() { this = TPartialCons(result, _) }
+ TypedContent getHead() { this = TPartialCons(result, _) }
int len() {
this = TPartialNil(_) and result = 0
@@ -2626,7 +2587,7 @@ private module FlowExploration {
DataFlowType getType() {
this = TPartialNil(result)
or
- exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
+ exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
@@ -2644,15 +2605,15 @@ private module FlowExploration {
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
override string toString() {
- exists(Content f, int len | this = TPartialCons(f, len) |
+ exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
if len = 1
- then result = "[" + f.toString() + "]"
- else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc.toString() + "]"
+ else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
override AccessPathFront getFront() {
- exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
+ exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
}
}
@@ -2828,11 +2789,12 @@ private module FlowExploration {
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
- exists(PartialAccessPath ap0, Content f |
- partialPathReadStep(mid, ap0, f, node, cc, config) and
+ exists(PartialAccessPath ap0, TypedContent tc |
+ partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, f, ap0, config)
+ apConsFwd(ap, tc, ap0, config) and
+ compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -2851,35 +2813,42 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
+ PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
+ PartialAccessPath ap2
) {
- ap1 = mid.getAp() and
- storeDirect(mid.getNode(), f, node) and
- ap2.getHead() = f and
- ap2.len() = unbindInt(ap1.len() + 1) and
- compatibleTypes(ap1.getType(), f.getType())
+ exists(Node midNode, DataFlowType contentType |
+ midNode = mid.getNode() and
+ ap1 = mid.getAp() and
+ store(midNode, tc, node, contentType) and
+ ap2.getHead() = tc and
+ ap2.len() = unbindInt(ap1.len() + 1) and
+ compatibleTypes(ap1.getType(), contentType)
+ )
}
pragma[nomagic]
private predicate apConsFwd(
- PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
+ PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
- partialPathStoreStep(mid, ap1, f, _, ap2) and
+ partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
+ PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
- ap = mid.getAp() and
- readStep(mid.getNode(), f, node) and
- ap.getHead() = f and
- config = mid.getConfiguration() and
- cc = mid.getCallContext()
+ exists(Node midNode |
+ midNode = mid.getNode() and
+ ap = mid.getAp() and
+ read(midNode, tc.getContent(), node) and
+ ap.getHead() = tc and
+ config = mid.getConfiguration() and
+ cc = mid.getCallContext()
+ )
}
private predicate partialPathOutOfCallable0(
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
index 783ac641e6e..1f42c21d5a7 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
@@ -26,13 +26,30 @@ private module Cached {
)
}
- /** Provides predicates for calculating flow-through summaries. */
+ pragma[nomagic]
+ private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
+ viableCallable(call) = result.getCallable() and
+ kind = result.getKind()
+ }
+
+ /**
+ * Holds if a value at return position `pos` can be returned to `out` via `call`,
+ * taking virtual dispatch into account.
+ */
cached
+ predicate viableReturnPosOut(DataFlowCall call, ReturnPosition pos, Node out) {
+ exists(ReturnKindExt kind |
+ pos = viableReturnPos(call, kind) and
+ out = kind.getAnOutNode(call)
+ )
+ }
+
+ /** Provides predicates for calculating flow-through summaries. */
private module FlowThrough {
/**
* The first flow-through approximation:
*
- * - Input/output access paths are abstracted with a Boolean parameter
+ * - Input access paths are abstracted with a Boolean parameter
* that indicates (non-)emptiness.
*/
private module Cand {
@@ -40,83 +57,47 @@ private module Cached {
* Holds if `p` can flow to `node` in the same callable using only
* value-preserving steps.
*
- * `read` indicates whether it is contents of `p` that can flow to `node`,
- * and `stored` indicates whether it flows to contents of `node`.
+ * `read` indicates whether it is contents of `p` that can flow to `node`.
*/
pragma[nomagic]
- private predicate parameterValueFlowCand(
- ParameterNode p, Node node, boolean read, boolean stored
- ) {
+ private predicate parameterValueFlowCand(ParameterNode p, Node node, boolean read) {
p = node and
- read = false and
- stored = false
+ read = false
or
// local flow
exists(Node mid |
- parameterValueFlowCand(p, mid, read, stored) and
+ parameterValueFlowCand(p, mid, read) and
simpleLocalFlowStep(mid, node)
)
or
// read
- exists(Node mid, boolean readMid, boolean storedMid |
- parameterValueFlowCand(p, mid, readMid, storedMid) and
- readStep(mid, _, node) and
- stored = false
- |
- // value neither read nor stored prior to read
- readMid = false and
- storedMid = false and
- read = true
- or
- // value (possibly read and then) stored prior to read (same content)
- read = readMid and
- storedMid = true
- )
- or
- // store
exists(Node mid |
- parameterValueFlowCand(p, mid, read, false) and
- storeStep(mid, _, node) and
- stored = true
+ parameterValueFlowCand(p, mid, false) and
+ readStep(mid, _, node) and
+ read = true
)
or
- // flow through: no prior read or store
+ // flow through: no prior read
exists(ArgumentNode arg |
- parameterValueFlowArgCand(p, arg, false, false) and
- argumentValueFlowsThroughCand(arg, node, read, stored)
+ parameterValueFlowArgCand(p, arg, false) and
+ argumentValueFlowsThroughCand(arg, node, read)
)
or
- // flow through: no read or store inside method
+ // flow through: no read inside method
exists(ArgumentNode arg |
- parameterValueFlowArgCand(p, arg, read, stored) and
- argumentValueFlowsThroughCand(arg, node, false, false)
- )
- or
- // flow through: possible prior read and prior store with compatible
- // flow-through method
- exists(ArgumentNode arg, boolean mid |
- parameterValueFlowArgCand(p, arg, read, mid) and
- argumentValueFlowsThroughCand(arg, node, mid, stored)
+ parameterValueFlowArgCand(p, arg, read) and
+ argumentValueFlowsThroughCand(arg, node, false)
)
}
pragma[nomagic]
- private predicate parameterValueFlowArgCand(
- ParameterNode p, ArgumentNode arg, boolean read, boolean stored
- ) {
- parameterValueFlowCand(p, arg, read, stored)
+ private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) {
+ parameterValueFlowCand(p, arg, read)
}
pragma[nomagic]
predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) {
- parameterValueFlowCand(p, n.getPreUpdateNode(), false, false)
- }
-
- pragma[nomagic]
- private predicate parameterValueFlowsToPostUpdateCand(
- ParameterNode p, PostUpdateNode n, boolean read
- ) {
- parameterValueFlowCand(p, n, read, true)
+ parameterValueFlowCand(p, n.getPreUpdateNode(), false)
}
/**
@@ -125,33 +106,21 @@ private module Cached {
* into account.
*
* `read` indicates whether it is contents of `p` that can flow to the return
- * node, and `stored` indicates whether it flows to contents of the return
* node.
*/
- predicate parameterValueFlowReturnCand(
- ParameterNode p, ReturnKindExt kind, boolean read, boolean stored
- ) {
+ predicate parameterValueFlowReturnCand(ParameterNode p, ReturnKind kind, boolean read) {
exists(ReturnNode ret |
- parameterValueFlowCand(p, ret, read, stored) and
- kind = TValueReturn(ret.getKind())
- )
- or
- exists(ParameterNode p2, int pos2, PostUpdateNode n |
- parameterValueFlowsToPostUpdateCand(p, n, read) and
- parameterValueFlowsToPreUpdateCand(p2, n) and
- p2.isParameterOf(_, pos2) and
- kind = TParamUpdate(pos2) and
- p != p2 and
- stored = true
+ parameterValueFlowCand(p, ret, read) and
+ kind = ret.getKind()
)
}
pragma[nomagic]
private predicate argumentValueFlowsThroughCand0(
- DataFlowCall call, ArgumentNode arg, ReturnKindExt kind, boolean read, boolean stored
+ DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read
) {
exists(ParameterNode param | viableParamArg(call, param, arg) |
- parameterValueFlowReturnCand(param, kind, read, stored)
+ parameterValueFlowReturnCand(param, kind, read)
)
}
@@ -159,22 +128,19 @@ private module Cached {
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
* not taking call contexts into account.
*
- * `read` indicates whether it is contents of `arg` that can flow to `out`, and
- * `stored` indicates whether it flows to contents of `out`.
+ * `read` indicates whether it is contents of `arg` that can flow to `out`.
*/
- predicate argumentValueFlowsThroughCand(
- ArgumentNode arg, Node out, boolean read, boolean stored
- ) {
- exists(DataFlowCall call, ReturnKindExt kind |
- argumentValueFlowsThroughCand0(call, arg, kind, read, stored) and
- out = kind.getAnOutNode(call)
+ predicate argumentValueFlowsThroughCand(ArgumentNode arg, Node out, boolean read) {
+ exists(DataFlowCall call, ReturnKind kind |
+ argumentValueFlowsThroughCand0(call, arg, kind, read) and
+ out = getAnOutNode(call, kind)
)
}
predicate cand(ParameterNode p, Node n) {
- parameterValueFlowCand(p, n, _, _) and
+ parameterValueFlowCand(p, n, _) and
(
- parameterValueFlowReturnCand(p, _, _, _)
+ parameterValueFlowReturnCand(p, _, _)
or
parameterValueFlowsToPreUpdateCand(p, _)
)
@@ -187,7 +153,6 @@ private module Cached {
(
n instanceof ParameterNode or
n instanceof OutNode or
- n instanceof PostUpdateNode or
readStep(_, _, n) or
n instanceof CastNode
)
@@ -200,10 +165,6 @@ private module Cached {
or
n instanceof ReturnNode
or
- Cand::parameterValueFlowsToPreUpdateCand(_, n)
- or
- storeStep(n, _, _)
- or
readStep(n, _, _)
or
n instanceof CastNode
@@ -237,230 +198,167 @@ private module Cached {
/**
* The final flow-through calculation:
*
- * - Input/output access paths are abstracted with a `ContentOption` parameter
- * that represents the head of the access path. `TContentNone()` means that
- * the access path is unrestricted.
+ * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`)
+ * or summarized as a single read step with before and after types recorded
+ * in the `ReadStepTypesOption` parameter.
* - Types are checked using the `compatibleTypes()` relation.
*/
- cached
private module Final {
/**
* Holds if `p` can flow to `node` in the same callable using only
- * value-preserving steps, not taking call contexts into account.
+ * value-preserving steps and possibly a single read step, not taking
+ * call contexts into account.
*
- * `contentIn` describes the content of `p` that can flow to `node`
- * (if any), and `contentOut` describes the content of `node` that
- * it flows to (if any).
+ * If a read step was taken, then `read` captures the `Content`, the
+ * container type, and the content type.
*/
- private predicate parameterValueFlow(
- ParameterNode p, Node node, ContentOption contentIn, ContentOption contentOut
- ) {
- parameterValueFlow0(p, node, contentIn, contentOut) and
+ predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) {
+ parameterValueFlow0(p, node, read) and
if node instanceof CastingNode
then
// normal flow through
- contentIn = TContentNone() and
- contentOut = TContentNone() and
+ read = TReadStepTypesNone() and
compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node))
or
// getter
- exists(Content fIn |
- contentIn.getContent() = fIn and
- contentOut = TContentNone() and
- compatibleTypes(fIn.getType(), getErasedNodeTypeBound(node))
- )
- or
- // (getter+)setter
- exists(Content fOut |
- contentOut.getContent() = fOut and
- compatibleTypes(fOut.getContainerType(), getErasedNodeTypeBound(node))
- )
+ compatibleTypes(read.getContentType(), getErasedNodeTypeBound(node))
else any()
}
pragma[nomagic]
- private predicate parameterValueFlow0(
- ParameterNode p, Node node, ContentOption contentIn, ContentOption contentOut
- ) {
+ private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) {
p = node and
Cand::cand(p, _) and
- contentIn = TContentNone() and
- contentOut = TContentNone()
+ read = TReadStepTypesNone()
or
// local flow
exists(Node mid |
- parameterValueFlow(p, mid, contentIn, contentOut) and
+ parameterValueFlow(p, mid, read) and
LocalFlowBigStep::localFlowBigStep(mid, node)
)
or
// read
- exists(Node mid, Content f, ContentOption contentInMid, ContentOption contentOutMid |
- parameterValueFlow(p, mid, contentInMid, contentOutMid) and
- readStep(mid, f, node)
- |
- // value neither read nor stored prior to read
- contentInMid = TContentNone() and
- contentOutMid = TContentNone() and
- contentIn.getContent() = f and
- contentOut = TContentNone() and
- Cand::parameterValueFlowReturnCand(p, _, true, _) and
- compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType())
- or
- // value (possibly read and then) stored prior to read (same content)
- contentIn = contentInMid and
- contentOutMid.getContent() = f and
- contentOut = TContentNone()
+ exists(Node mid |
+ parameterValueFlow(p, mid, TReadStepTypesNone()) and
+ readStepWithTypes(mid, read.getContainerType(), read.getContent(), node,
+ read.getContentType()) and
+ Cand::parameterValueFlowReturnCand(p, _, true) and
+ compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType())
)
or
- // store
- exists(Node mid, Content f |
- parameterValueFlow(p, mid, contentIn, TContentNone()) and
- storeStep(mid, f, node) and
- contentOut.getContent() = f
- |
- contentIn = TContentNone() and
- compatibleTypes(getErasedNodeTypeBound(p), f.getType())
- or
- compatibleTypes(contentIn.getContent().getType(), f.getType())
- )
- or
- // flow through: no prior read or store
+ // flow through: no prior read
exists(ArgumentNode arg |
- parameterValueFlowArg(p, arg, TContentNone(), TContentNone()) and
- argumentValueFlowsThrough(_, arg, contentIn, contentOut, node)
+ parameterValueFlowArg(p, arg, TReadStepTypesNone()) and
+ argumentValueFlowsThrough(arg, read, node)
)
or
- // flow through: no read or store inside method
+ // flow through: no read inside method
exists(ArgumentNode arg |
- parameterValueFlowArg(p, arg, contentIn, contentOut) and
- argumentValueFlowsThrough(_, arg, TContentNone(), TContentNone(), node)
- )
- or
- // flow through: possible prior read and prior store with compatible
- // flow-through method
- exists(ArgumentNode arg, ContentOption contentMid |
- parameterValueFlowArg(p, arg, contentIn, contentMid) and
- argumentValueFlowsThrough(_, arg, contentMid, contentOut, node)
+ parameterValueFlowArg(p, arg, read) and
+ argumentValueFlowsThrough(arg, TReadStepTypesNone(), node)
)
}
pragma[nomagic]
private predicate parameterValueFlowArg(
- ParameterNode p, ArgumentNode arg, ContentOption contentIn, ContentOption contentOut
+ ParameterNode p, ArgumentNode arg, ReadStepTypesOption read
) {
- parameterValueFlow(p, arg, contentIn, contentOut) and
- Cand::argumentValueFlowsThroughCand(arg, _, _, _)
+ parameterValueFlow(p, arg, read) and
+ Cand::argumentValueFlowsThroughCand(arg, _, _)
}
pragma[nomagic]
private predicate argumentValueFlowsThrough0(
- DataFlowCall call, ArgumentNode arg, ReturnKindExt kind, ContentOption contentIn,
- ContentOption contentOut
+ DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read
) {
exists(ParameterNode param | viableParamArg(call, param, arg) |
- parameterValueFlowReturn(param, _, kind, contentIn, contentOut)
+ parameterValueFlowReturn(param, kind, read)
)
}
/**
- * Holds if `arg` flows to `out` through `call` using only value-preserving steps,
- * not taking call contexts into account.
+ * Holds if `arg` flows to `out` through a call using only
+ * value-preserving steps and possibly a single read step, not taking
+ * call contexts into account.
*
- * `contentIn` describes the content of `arg` that can flow to `out` (if any), and
- * `contentOut` describes the content of `out` that it flows to (if any).
+ * If a read step was taken, then `read` captures the `Content`, the
+ * container type, and the content type.
*/
- cached
- predicate argumentValueFlowsThrough(
- DataFlowCall call, ArgumentNode arg, ContentOption contentIn, ContentOption contentOut,
- Node out
- ) {
- exists(ReturnKindExt kind |
- argumentValueFlowsThrough0(call, arg, kind, contentIn, contentOut) and
- out = kind.getAnOutNode(call)
+ pragma[nomagic]
+ predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) {
+ exists(DataFlowCall call, ReturnKind kind |
+ argumentValueFlowsThrough0(call, arg, kind, read) and
+ out = getAnOutNode(call, kind)
|
// normal flow through
- contentIn = TContentNone() and
- contentOut = TContentNone() and
+ read = TReadStepTypesNone() and
compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out))
or
// getter
- exists(Content fIn |
- contentIn.getContent() = fIn and
- contentOut = TContentNone() and
- compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and
- compatibleTypes(fIn.getType(), getErasedNodeTypeBound(out))
- )
- or
- // setter
- exists(Content fOut |
- contentIn = TContentNone() and
- contentOut.getContent() = fOut and
- compatibleTypes(getErasedNodeTypeBound(arg), fOut.getType()) and
- compatibleTypes(fOut.getContainerType(), getErasedNodeTypeBound(out))
- )
- or
- // getter+setter
- exists(Content fIn, Content fOut |
- contentIn.getContent() = fIn and
- contentOut.getContent() = fOut and
- compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and
- compatibleTypes(fOut.getContainerType(), getErasedNodeTypeBound(out))
- )
+ compatibleTypes(getErasedNodeTypeBound(arg), read.getContainerType()) and
+ compatibleTypes(read.getContentType(), getErasedNodeTypeBound(out))
)
}
/**
- * Holds if `p` can flow to the pre-update node associated with post-update
- * node `n`, in the same callable, using only value-preserving steps.
+ * Holds if `arg` flows to `out` through a call using only
+ * value-preserving steps and a single read step, not taking call
+ * contexts into account, thus representing a getter-step.
*/
- cached
- predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) {
- parameterValueFlow(p, n.getPreUpdateNode(), TContentNone(), TContentNone())
- }
-
- pragma[nomagic]
- private predicate parameterValueFlowsToPostUpdate(
- ParameterNode p, PostUpdateNode n, ContentOption contentIn, ContentOption contentOut
- ) {
- parameterValueFlow(p, n, contentIn, contentOut) and
- contentOut.hasContent()
+ predicate getterStep(ArgumentNode arg, Content c, Node out) {
+ argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out)
}
/**
* Holds if `p` can flow to a return node of kind `kind` in the same
- * callable using only value-preserving steps.
+ * callable using only value-preserving steps and possibly a single read
+ * step.
*
- * `contentIn` describes the content of `p` that can flow to the return
- * node (if any), and `contentOut` describes the content of the return
- * node that it flows to (if any).
+ * If a read step was taken, then `read` captures the `Content`, the
+ * container type, and the content type.
*/
- cached
- predicate parameterValueFlowReturn(
- ParameterNode p, Node ret, ReturnKindExt kind, ContentOption contentIn,
- ContentOption contentOut
+ private predicate parameterValueFlowReturn(
+ ParameterNode p, ReturnKind kind, ReadStepTypesOption read
) {
- ret =
- any(ReturnNode n |
- parameterValueFlow(p, n, contentIn, contentOut) and
- kind = TValueReturn(n.getKind())
- )
- or
- ret =
- any(PostUpdateNode n |
- exists(ParameterNode p2, int pos2 |
- parameterValueFlowsToPostUpdate(p, n, contentIn, contentOut) and
- parameterValueFlowsToPreUpdate(p2, n) and
- p2.isParameterOf(_, pos2) and
- kind = TParamUpdate(pos2) and
- p != p2
- )
- )
+ exists(ReturnNode ret |
+ parameterValueFlow(p, ret, read) and
+ kind = ret.getKind()
+ )
}
}
import Final
}
+ /**
+ * Holds if `p` can flow to the pre-update node associated with post-update
+ * node `n`, in the same callable, using only value-preserving steps.
+ */
+ cached
+ predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) {
+ parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone())
+ }
+
+ private predicate store(
+ Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
+ ) {
+ storeStep(node1, c, node2) and
+ readStep(_, c, _) and
+ contentType = getErasedNodeTypeBound(node1) and
+ containerType = getErasedNodeTypeBound(node2)
+ or
+ exists(Node n1, Node n2 |
+ n1 = node1.(PostUpdateNode).getPreUpdateNode() and
+ n2 = node2.(PostUpdateNode).getPreUpdateNode()
+ |
+ argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1)
+ or
+ readStep(n2, c, n1) and
+ contentType = getErasedNodeTypeBound(n1) and
+ containerType = getErasedNodeTypeBound(n2)
+ )
+ }
+
/**
* Holds if data can flow from `node1` to `node2` via a direct assignment to
* `f`.
@@ -469,17 +367,8 @@ private module Cached {
* been stored into, in order to handle cases like `x.f1.f2 = y`.
*/
cached
- predicate storeDirect(Node node1, Content f, Node node2) {
- storeStep(node1, f, node2) and readStep(_, f, _)
- or
- exists(Node n1, Node n2 |
- n1 = node1.(PostUpdateNode).getPreUpdateNode() and
- n2 = node2.(PostUpdateNode).getPreUpdateNode()
- |
- argumentValueFlowsThrough(_, n2, TContentSome(f), TContentNone(), n1)
- or
- readStep(n2, f, n1)
- )
+ predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) {
+ store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
}
import FlowThrough
@@ -520,6 +409,24 @@ private module Cached {
newtype TReturnKindExt =
TValueReturn(ReturnKind kind) or
TParamUpdate(int pos) { exists(ParameterNode p | p.isParameterOf(_, pos)) }
+
+ cached
+ newtype TBooleanOption =
+ TBooleanNone() or
+ TBooleanSome(boolean b) { b = true or b = false }
+
+ cached
+ newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
+
+ cached
+ newtype TAccessPathFront =
+ TFrontNil(DataFlowType t) or
+ TFrontHead(TypedContent tc)
+
+ cached
+ newtype TAccessPathFrontOption =
+ TAccessPathFrontNone() or
+ TAccessPathFrontSome(AccessPathFront apf)
}
/**
@@ -529,26 +436,38 @@ class CastingNode extends Node {
CastingNode() {
this instanceof ParameterNode or
this instanceof CastNode or
- this instanceof OutNode or
- this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode
+ this instanceof OutNodeExt or
+ // For reads, `x.f`, we want to check that the tracked type after the read (which
+ // is obtained by popping the head of the access path stack) is compatible with
+ // the type of `x.f`.
+ readStep(_, _, this)
}
}
-newtype TContentOption =
- TContentNone() or
- TContentSome(Content f)
+private predicate readStepWithTypes(
+ Node n1, DataFlowType container, Content c, Node n2, DataFlowType content
+) {
+ readStep(n1, c, n2) and
+ container = getErasedNodeTypeBound(n1) and
+ content = getErasedNodeTypeBound(n2)
+}
-class ContentOption extends TContentOption {
- Content getContent() { this = TContentSome(result) }
-
- predicate hasContent() { exists(this.getContent()) }
-
- string toString() {
- result = this.getContent().toString()
- or
- not this.hasContent() and
- result = ""
+private newtype TReadStepTypesOption =
+ TReadStepTypesNone() or
+ TReadStepTypesSome(DataFlowType container, Content c, DataFlowType content) {
+ readStepWithTypes(_, container, c, _, content)
}
+
+private class ReadStepTypesOption extends TReadStepTypesOption {
+ predicate isSome() { this instanceof TReadStepTypesSome }
+
+ DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) }
+
+ Content getContent() { this = TReadStepTypesSome(_, result, _) }
+
+ DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) }
+
+ string toString() { if this.isSome() then result = "Some(..)" else result = "None()" }
}
/**
@@ -678,6 +597,18 @@ class ReturnNodeExt extends Node {
}
}
+/**
+ * A node to which data can flow from a call. Either an ordinary out node
+ * or a post-update node associated with a call argument.
+ */
+class OutNodeExt extends Node {
+ OutNodeExt() {
+ this instanceof OutNode
+ or
+ this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode
+ }
+}
+
/**
* 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
@@ -688,7 +619,7 @@ abstract class ReturnKindExt extends TReturnKindExt {
abstract string toString();
/** Gets a node corresponding to data flow out of `call`. */
- abstract Node getAnOutNode(DataFlowCall call);
+ abstract OutNodeExt getAnOutNode(DataFlowCall call);
}
class ValueReturnKind extends ReturnKindExt, TValueReturn {
@@ -700,7 +631,9 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn {
override string toString() { result = kind.toString() }
- override Node getAnOutNode(DataFlowCall call) { result = getAnOutNode(call, this.getKind()) }
+ override OutNodeExt getAnOutNode(DataFlowCall call) {
+ result = getAnOutNode(call, this.getKind())
+ }
}
class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
@@ -712,9 +645,9 @@ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
override string toString() { result = "param update " + pos }
- override PostUpdateNode getAnOutNode(DataFlowCall call) {
+ override OutNodeExt getAnOutNode(DataFlowCall call) {
exists(ArgumentNode arg |
- result.getPreUpdateNode() = arg and
+ result.(PostUpdateNode).getPreUpdateNode() = arg and
arg.argumentOf(call, this.getPosition())
)
}
@@ -779,77 +712,79 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
result = viableCallable(call) and cc instanceof CallContextReturn
}
-newtype TSummary =
- TSummaryVal() or
- TSummaryTaint() or
- TSummaryReadVal(Content f) or
- TSummaryReadTaint(Content f) or
- TSummaryTaintStore(Content f)
-
-/**
- * A summary of flow through a callable. This can either be value-preserving
- * if no additional steps are used, taint-flow if at least one additional step
- * is used, or any one of those combined with a store or a read. Summaries
- * recorded at a return node are restricted to include at least one additional
- * step, as the value-based summaries are calculated independent of the
- * configuration.
- */
-class Summary extends TSummary {
- string toString() {
- result = "Val" and this = TSummaryVal()
- or
- result = "Taint" and this = TSummaryTaint()
- or
- exists(Content f |
- result = "ReadVal " + f.toString() and this = TSummaryReadVal(f)
- or
- result = "ReadTaint " + f.toString() and this = TSummaryReadTaint(f)
- or
- result = "TaintStore " + f.toString() and this = TSummaryTaintStore(f)
- )
- }
-
- /** Gets the summary that results from extending this with an additional step. */
- Summary additionalStep() {
- this = TSummaryVal() and result = TSummaryTaint()
- or
- this = TSummaryTaint() and result = TSummaryTaint()
- or
- exists(Content f | this = TSummaryReadVal(f) and result = TSummaryReadTaint(f))
- or
- exists(Content f | this = TSummaryReadTaint(f) and result = TSummaryReadTaint(f))
- }
-
- /** Gets the summary that results from extending this with a read. */
- Summary readStep(Content f) { this = TSummaryVal() and result = TSummaryReadVal(f) }
-
- /** Gets the summary that results from extending this with a store. */
- Summary storeStep(Content f) { this = TSummaryTaint() and result = TSummaryTaintStore(f) }
-
- /** Gets the summary that results from extending this with `step`. */
- bindingset[this, step]
- Summary compose(Summary step) {
- this = TSummaryVal() and result = step
- or
- this = TSummaryTaint() and
- (step = TSummaryTaint() or step = TSummaryTaintStore(_)) and
- result = step
- or
- exists(Content f |
- this = TSummaryReadVal(f) and step = TSummaryTaint() and result = TSummaryReadTaint(f)
- )
- or
- this = TSummaryReadTaint(_) and step = TSummaryTaint() and result = this
- }
-
- /** Holds if this summary does not include any taint steps. */
- predicate isPartial() {
- this = TSummaryVal() or
- this = TSummaryReadVal(_)
- }
-}
-
pragma[noinline]
DataFlowType getErasedNodeTypeBound(Node n) { result = getErasedRepr(n.getTypeBound()) }
-predicate readDirect = readStep/3;
+predicate read = readStep/3;
+
+/** An optional Boolean value. */
+class BooleanOption extends TBooleanOption {
+ string toString() {
+ this = TBooleanNone() and result = ""
+ or
+ this = TBooleanSome(any(boolean b | result = b.toString()))
+ }
+}
+
+/** Content tagged with the type of a containing object. */
+class TypedContent extends MkTypedContent {
+ private Content c;
+ private DataFlowType t;
+
+ TypedContent() { this = MkTypedContent(c, t) }
+
+ /** Gets the content. */
+ Content getContent() { result = c }
+
+ /** Gets the container type. */
+ DataFlowType getContainerType() { result = t }
+
+ /** Gets a textual representation of this content. */
+ string toString() { result = c.toString() }
+}
+
+/**
+ * The front of an access path. This is either a head or a nil.
+ */
+abstract class AccessPathFront extends TAccessPathFront {
+ abstract string toString();
+
+ abstract DataFlowType getType();
+
+ abstract boolean toBoolNonEmpty();
+
+ predicate headUsesContent(TypedContent tc) { this = TFrontHead(tc) }
+}
+
+class AccessPathFrontNil extends AccessPathFront, TFrontNil {
+ private DataFlowType t;
+
+ AccessPathFrontNil() { this = TFrontNil(t) }
+
+ override string toString() { result = ppReprType(t) }
+
+ override DataFlowType getType() { result = t }
+
+ override boolean toBoolNonEmpty() { result = false }
+}
+
+class AccessPathFrontHead extends AccessPathFront, TFrontHead {
+ private TypedContent tc;
+
+ AccessPathFrontHead() { this = TFrontHead(tc) }
+
+ override string toString() { result = tc.toString() }
+
+ override DataFlowType getType() { result = tc.getContainerType() }
+
+ override boolean toBoolNonEmpty() { result = true }
+}
+
+/** An optional access path front. */
+class AccessPathFrontOption extends TAccessPathFrontOption {
+ string toString() {
+ this = TAccessPathFrontNone() and result = ""
+ or
+ this = TAccessPathFrontSome(any(AccessPathFront apf | result = apf.toString()))
+ }
+}
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 a1daeb66411..1aeedf717f7 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
*/
predicate isBarrier(Node node) { none() }
- /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */
- deprecated predicate isBarrierEdge(Node node1, Node node2) { none() }
-
/** Holds if data flow into `node` is prohibited. */
predicate isBarrierIn(Node node) { none() }
@@ -251,15 +248,11 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
-pragma[noinline]
-private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
- viableCallable(call) = result.getCallable() and
- kind = result.getKind()
-}
-
/**
- * Holds if `node` is reachable from a source in the given configuration
- * taking simple call contexts into consideration.
+ * Holds if `node` is reachable from a source in the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call.
*/
private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) {
not fullBarrier(node, config) and
@@ -293,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
exists(Node mid |
useFieldFlow(config) and
nodeCandFwd1(mid, fromArg, config) and
- storeDirect(mid, _, node) and
+ store(mid, _, node, _) and
not outBarrier(mid, config)
)
or
// read
- exists(Content f |
- nodeCandFwd1Read(f, node, fromArg, config) and
- storeCandFwd1(f, config) and
+ exists(Content c |
+ nodeCandFwd1Read(c, node, fromArg, config) and
+ nodeCandFwd1IsStored(c, config) and
not inBarrier(node, config)
)
or
@@ -317,13 +310,35 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
fromArg = false
or
nodeCandFwd1OutFromArg(call, node, config) and
- flowOutCandFwd1(call, fromArg, config)
+ nodeCandFwd1IsEntered(call, fromArg, config)
)
)
}
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
+pragma[nomagic]
+private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
+ exists(Node mid |
+ nodeCandFwd1(mid, fromArg, config) and
+ read(mid, c, node)
+ )
+}
+
+/**
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
+ exists(Node mid, Node node, TypedContent tc |
+ not fullBarrier(node, config) and
+ useFieldFlow(config) and
+ nodeCandFwd1(mid, config) and
+ store(mid, tc, node, _) and
+ c = tc.getContent()
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1ReturnPosition(
ReturnPosition pos, boolean fromArg, Configuration config
@@ -335,43 +350,10 @@ private predicate nodeCandFwd1ReturnPosition(
}
pragma[nomagic]
-private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
- exists(Node mid |
- nodeCandFwd1(mid, fromArg, config) and
- readDirect(mid, f, node)
- )
-}
-
-/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
- */
-pragma[nomagic]
-private predicate storeCandFwd1(Content f, Configuration config) {
- exists(Node mid, Node node |
- not fullBarrier(node, config) and
- useFieldFlow(config) and
- nodeCandFwd1(mid, config) and
- storeDirect(mid, f, node)
- )
-}
-
-pragma[nomagic]
-private predicate nodeCandFwd1ReturnKind(
- DataFlowCall call, ReturnKindExt kind, boolean fromArg, Configuration config
-) {
+private predicate nodeCandFwd1Out(DataFlowCall call, Node out, boolean fromArg, Configuration config) {
exists(ReturnPosition pos |
nodeCandFwd1ReturnPosition(pos, fromArg, config) and
- pos = viableReturnPos(call, kind)
- )
-}
-
-pragma[nomagic]
-private predicate nodeCandFwd1Out(
- DataFlowCall call, Node node, boolean fromArg, Configuration config
-) {
- exists(ReturnKindExt kind |
- nodeCandFwd1ReturnKind(call, kind, fromArg, config) and
- node = kind.getAnOutNode(call)
+ viableReturnPosOut(call, pos, out)
)
}
@@ -384,7 +366,7 @@ private predicate nodeCandFwd1OutFromArg(DataFlowCall call, Node node, Configura
* Holds if an argument to `call` is reached in the flow covered by `nodeCandFwd1`.
*/
pragma[nomagic]
-private predicate flowOutCandFwd1(DataFlowCall call, boolean fromArg, Configuration config) {
+private predicate nodeCandFwd1IsEntered(DataFlowCall call, boolean fromArg, Configuration config) {
exists(ArgumentNode arg |
nodeCandFwd1(arg, fromArg, config) and
viableParamArg(call, _, arg)
@@ -395,8 +377,11 @@ bindingset[result, b]
private boolean unbindBool(boolean b) { result != b.booleanNot() }
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is part of a path from a source to a sink in the
+ * configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink.
*/
pragma[nomagic]
private predicate nodeCand1(Node node, boolean toReturn, Configuration config) {
@@ -433,81 +418,86 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
)
or
// store
- exists(Content f |
- nodeCand1Store(f, node, toReturn, config) and
- readCand1(f, config)
+ exists(Content c |
+ nodeCand1Store(c, node, toReturn, config) and
+ nodeCand1IsRead(c, config)
)
or
// read
- exists(Node mid, Content f |
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ exists(Node mid, Content c |
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, toReturn, config)
)
or
// flow into a callable
exists(DataFlowCall call |
- nodeCand1Arg(call, node, false, config) and
+ nodeCand1In(call, node, false, config) and
toReturn = false
or
- nodeCand1ArgToReturn(call, node, config) and
- flowInCand1(call, toReturn, config)
+ nodeCand1InToReturn(call, node, config) and
+ nodeCand1IsReturned(call, toReturn, config)
)
or
// flow out of a callable
exists(ReturnPosition pos |
- nodeCand1ReturnPosition(pos, config) and
+ nodeCand1Out(pos, config) and
getReturnPosition(node) = pos and
toReturn = true
)
}
-pragma[nomagic]
-private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _, config) }
-
-pragma[nomagic]
-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`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate readCand1(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content c, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd1(node, unbind(config)) and
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, _, config)
)
}
pragma[nomagic]
-private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
- exists(Node mid |
+private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
+ exists(Node mid, TypedContent tc |
nodeCand1(mid, toReturn, config) and
- storeCandFwd1(f, unbind(config)) and
- storeDirect(node, f, mid)
+ nodeCandFwd1IsStored(c, unbind(config)) and
+ store(node, tc, mid, _) and
+ c = tc.getContent()
)
}
/**
- * Holds if `f` is the target of both a read and a store in the flow covered
+ * Holds if `c` is the target of both a read and a store in the flow covered
* by `nodeCand1`.
*/
-private predicate readStoreCand1(Content f, Configuration conf) {
- readCand1(f, conf) and
- nodeCand1Store(f, _, _, conf)
+private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
+ nodeCand1IsRead(c, conf) and
+ nodeCand1Store(c, _, _, conf)
}
pragma[nomagic]
-private predicate viableParamArgCandFwd1(
+private predicate viableReturnPosOutNodeCandFwd1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+) {
+ nodeCandFwd1ReturnPosition(pos, _, config) and
+ viableReturnPosOut(call, pos, out)
+}
+
+pragma[nomagic]
+private predicate nodeCand1Out(ReturnPosition pos, Configuration config) {
+ exists(DataFlowCall call, Node out |
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, config)
+ )
+}
+
+pragma[nomagic]
+private predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
) {
viableParamArg(call, p, arg) and
@@ -515,32 +505,35 @@ private predicate viableParamArgCandFwd1(
}
pragma[nomagic]
-private predicate nodeCand1Arg(
+private predicate nodeCand1In(
DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config
) {
exists(ParameterNode p |
nodeCand1(p, toReturn, config) and
- viableParamArgCandFwd1(call, p, arg, config)
+ viableParamArgNodeCandFwd1(call, p, arg, config)
)
}
pragma[nomagic]
-private predicate nodeCand1ArgToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
- nodeCand1Arg(call, arg, true, config)
+private predicate nodeCand1InToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
+ nodeCand1In(call, arg, true, config)
}
/**
* Holds if an output from `call` is reached in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate flowInCand1(DataFlowCall call, boolean toReturn, Configuration config) {
+private predicate nodeCand1IsReturned(DataFlowCall call, boolean toReturn, Configuration config) {
exists(Node out |
nodeCand1(out, toReturn, config) and
nodeCandFwd1OutFromArg(call, out, config)
)
}
-private predicate throughFlowNodeCand(Node node, Configuration config) {
+pragma[nomagic]
+private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _, config) }
+
+private predicate throughFlowNodeCand1(Node node, Configuration config) {
nodeCand1(node, true, config) and
not fullBarrier(node, config) and
not inBarrier(node, config) and
@@ -549,11 +542,11 @@ private predicate throughFlowNodeCand(Node node, Configuration config) {
/** Holds if flow may return from `callable`. */
pragma[nomagic]
-private predicate returnFlowCallableCand(
+private predicate returnFlowCallableNodeCand1(
DataFlowCallable callable, ReturnKindExt kind, Configuration config
) {
exists(ReturnNodeExt ret |
- throughFlowNodeCand(ret, config) and
+ throughFlowNodeCand1(ret, config) and
callable = ret.getEnclosingCallable() and
kind = ret.getKind()
)
@@ -563,10 +556,10 @@ private predicate returnFlowCallableCand(
* Holds if flow may enter through `p` and reach a return node making `p` a
* candidate for the origin of a summary.
*/
-private predicate parameterThroughFlowCand(ParameterNode p, Configuration config) {
+private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration config) {
exists(ReturnKindExt kind |
- throughFlowNodeCand(p, config) and
- returnFlowCallableCand(p.getEnclosingCallable(), kind, config) and
+ throughFlowNodeCand1(p, config) and
+ returnFlowCallableNodeCand1(p.getEnclosingCallable(), kind, config) and
// we don't expect a parameter to return stored in itself
not exists(int pos |
kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos)
@@ -575,420 +568,77 @@ private predicate parameterThroughFlowCand(ParameterNode p, Configuration config
}
pragma[nomagic]
-private predicate store(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
- nodeCand1(n2, unbind(config)) and
- (
- storeDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentSome(f), n2)
- )
-}
-
-pragma[nomagic]
-private predicate read(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
- nodeCand1(n2, unbind(config)) and
- (
- readDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentSome(f), TContentNone(), n2)
- )
-}
-
-/**
- * Holds if `p` can flow to `node` in the same callable with `summary`
- * representing the flow path. The type of the tracked object is `t2`, and if
- * the summary includes a store step, `t1` is the tracked type just prior to the
- * store, that is, the type of the stored object, otherwise `t1` is equal to `t2`.
- */
-pragma[nomagic]
-private predicate parameterFlow(
- ParameterNode p, Node node, DataFlowType t1, DataFlowType t2, Summary summary,
- Configuration config
-) {
- parameterThroughFlowCand(p, config) and
- p = node and
- t1 = getErasedNodeTypeBound(node) and
- t1 = t2 and
- summary = TSummaryVal()
- or
- throughFlowNodeCand(node, unbind(config)) and
- (
- exists(Node mid |
- parameterFlow(p, mid, t1, t2, summary, config) and
- localFlowStep(mid, node, config) and
- compatibleTypes(t2, getErasedNodeTypeBound(node))
- )
- or
- exists(Node mid, Summary midsum |
- parameterFlow(p, mid, _, _, midsum, config) and
- additionalLocalFlowStep(mid, node, config) and
- t1 = getErasedNodeTypeBound(node) and
- t1 = t2 and
- summary = midsum.additionalStep()
- )
- or
- // read step
- exists(Node mid, Content f, Summary midsum |
- parameterFlow(p, mid, _, _, midsum, config) and
- read(mid, f, node, config) and
- summary = midsum.readStep(f) and
- t1 = f.getType() and
- t1 = t2
- )
- or
- // store step
- exists(Node mid, Content f, Summary midsum |
- parameterFlow(p, mid, t1, /* t1 */ _, midsum, config) and
- store(mid, f, node, config) and
- summary = midsum.storeStep(f) and
- compatibleTypes(t1, f.getType()) and
- t2 = f.getContainerType()
- )
- or
- // value flow through a callable
- exists(Node arg |
- parameterFlow(p, arg, t1, t2, summary, config) and
- argumentValueFlowsThrough(_, arg, TContentNone(), TContentNone(), node) and
- compatibleTypes(t2, getErasedNodeTypeBound(node))
- )
- or
- // flow through a callable
- exists(Node arg, Summary s1, Summary s2 |
- parameterFlow(p, arg, _, _, s1, config) and
- argumentFlowsThrough(arg, node, t1, t2, s2, config) and
- summary = s1.compose(s2)
- )
- )
-}
-
-private predicate viableParamArgCand(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
-) {
- viableParamArg(call, p, arg) and
- nodeCand1(arg, unbind(config)) and
- nodeCand1(p, config) and
- not outBarrier(arg, config) and
- not inBarrier(p, config)
-}
-
-pragma[nomagic]
-private predicate parameterFlowReturn(
- ParameterNode p, ReturnNodeExt ret, ReturnKindExt kind, DataFlowType t1, DataFlowType t2,
- Summary summary, Configuration config
-) {
- parameterFlow(p, ret, t1, t2, summary, config) and
- kind = ret.getKind() and
- not summary.isPartial() and
- not exists(int pos | kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos))
-}
-
-pragma[nomagic]
-private predicate argumentFlowsThrough0(
- DataFlowCall call, ArgumentNode arg, ReturnKindExt kind, DataFlowType t1, DataFlowType t2,
- Summary summary, Configuration config
-) {
- exists(ParameterNode p |
- viableParamArgCand(call, p, arg, config) and
- parameterFlowReturn(p, _, kind, t1, t2, summary, config)
- )
-}
-
-/**
- * Holds if data can flow from `arg` to `out` through a call with `summary`
- * representing the flow path. The type of the tracked object is `t2`, and if
- * the summary includes a store step, `t1` is the tracked type just prior to the
- * store, that is, the type of the stored object, otherwise `t1` is equal to `t2`.
- */
-private predicate argumentFlowsThrough(
- ArgumentNode arg, Node out, DataFlowType t1, DataFlowType t2, Summary summary,
- Configuration config
-) {
- nodeCand1(out, unbind(config)) and
- not inBarrier(out, config) and
- compatibleTypes(t2, getErasedNodeTypeBound(out)) and
- exists(DataFlowCall call, ReturnKindExt kind |
- argumentFlowsThrough0(call, arg, kind, t1, t2, summary, config) and
- out = kind.getAnOutNode(call)
- )
-}
-
-pragma[noinline]
-private predicate readStoreNode(
- DataFlowCall call, ArgumentNode arg, Content f1, Configuration config
-) {
- exists(Content f2, Node out |
- argumentValueFlowsThrough(call, arg, TContentSome(f1), TContentSome(f2), out) and
- nodeCand1(out, config) and
- readStoreCand1(f2, unbind(config))
- )
-}
-
-private newtype TNodeExt =
- TNormalNode(Node node) { nodeCand1(node, _) } or
- TReadStoreNode(DataFlowCall call, ArgumentNode arg, Content f1, Configuration config) {
- nodeCand1(arg, config) and
- readStoreNode(call, arg, f1, config) and
- readStoreCand1(f1, unbind(config))
- } or
- TReadTaintNode(ArgumentNode arg, Content f, Configuration config) {
- argumentFlowsThrough(arg, _, _, _, TSummaryReadTaint(f), config)
- } or
- TTaintStoreNode(ArgumentNode arg, DataFlowType t, Configuration config) {
- argumentFlowsThrough(arg, _, t, _, TSummaryTaintStore(_), config)
- }
-
-/**
- * An extended data flow node. Either a normal node, or an intermediate node
- * used to split up a summarized flow steps.
- *
- * This is purely an internal implementation detail.
- */
-abstract private class NodeExt extends TNodeExt {
- /** Gets the underlying (normal) node, if any. */
- abstract Node getNode();
-
- abstract DataFlowType getErasedNodeTypeBound();
-
- abstract DataFlowCallable getEnclosingCallable();
-
- abstract predicate isCand1(Configuration config);
-
- abstract string toString();
-
- abstract predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- );
-}
-
-/** A `Node` at which a cast can occur such that the type should be checked. */
-abstract private class CastingNodeExt extends NodeExt { }
-
-private class NormalNodeExt extends NodeExt, TNormalNode {
- override Node getNode() { this = TNormalNode(result) }
-
- override DataFlowType getErasedNodeTypeBound() {
- result = getErasedRepr(this.getNode().getTypeBound())
- }
-
- override DataFlowCallable getEnclosingCallable() {
- result = this.getNode().getEnclosingCallable()
- }
-
- override predicate isCand1(Configuration config) { nodeCand1(this.getNode(), config) }
-
- override string toString() { result = this.getNode().toString() }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class NormalCastingNodeExt extends CastingNodeExt, NormalNodeExt {
- NormalCastingNodeExt() { this.getNode() instanceof CastingNode }
-}
-
-private class ReadStoreNodeExt extends CastingNodeExt, TReadStoreNode {
- private DataFlowCall call;
- private ArgumentNode arg;
- private Content f1;
- private Configuration config0;
-
- ReadStoreNodeExt() { this = TReadStoreNode(call, arg, f1, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = f1.getType() }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = "(inside) " + call.toString() + " [read " + f1 + "]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class ReadTaintNode extends NodeExt, TReadTaintNode {
- private ArgumentNode arg;
- private Content f;
- private Configuration config0;
-
- ReadTaintNode() { this = TReadTaintNode(arg, f, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = f.getType() }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = arg.toString() + " [read taint " + f + "]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class TaintStoreNode extends NodeExt, TTaintStoreNode {
- private ArgumentNode arg;
- private DataFlowType t;
- private Configuration config0;
-
- TaintStoreNode() { this = TTaintStoreNode(arg, t, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = t }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = arg.toString() + " [taint store]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private predicate additionalLocalFlowStepExt(
- NodeExt node1, NodeExt node2, DataFlowType t, Configuration config
-) {
- exists(ArgumentNode arg, Content f |
- node1 = TReadTaintNode(arg, f, config) and
- argumentFlowsThrough(arg, node2.getNode(), _, t, TSummaryReadTaint(f), config)
- )
- or
- node2 = TTaintStoreNode(node1.getNode(), t, config)
-}
-
-pragma[nomagic]
-private predicate readExt(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- read(node1.getNode(), f, node2.getNode(), config)
- or
- node2 = TReadStoreNode(_, node1.getNode(), f, config)
- or
- node2 = TReadTaintNode(node1.getNode(), f, config)
-}
-
-pragma[nomagic]
-private predicate storeExt(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- store(node1.getNode(), f, node2.getNode(), config)
- or
- exists(DataFlowCall call, ArgumentNode arg, Content f1, Node n2 |
- node1 = TReadStoreNode(call, arg, f1, config) and
- n2 = node2.getNode() and
- argumentValueFlowsThrough(call, arg, TContentSome(f1), TContentSome(f), n2) and
+private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
+ exists(TypedContent tc |
+ nodeCand1IsReadAndStored(c, config) and
nodeCand1(n2, unbind(config)) and
- readStoreCand1(f, unbind(config))
- )
- or
- exists(ArgumentNode arg, DataFlowType t |
- node1 = TTaintStoreNode(arg, t, config) and
- argumentFlowsThrough(arg, node2.getNode(), t, _, TSummaryTaintStore(f), config)
+ store(n1, tc, n2, _) and
+ c = tc.getContent()
)
}
-private predicate jumpStepExt(NodeExt node1, NodeExt node2, Configuration config) {
- jumpStep(node1.getNode(), node2.getNode(), config)
-}
-
-private predicate additionalJumpStepExt(NodeExt node1, NodeExt node2, Configuration config) {
- additionalJumpStep(node1.getNode(), node2.getNode(), config)
-}
-
-private predicate argumentValueFlowsThrough(NodeExt node1, NodeExt node2) {
- argumentValueFlowsThrough(_, node1.getNode(), TContentNone(), TContentNone(), node2.getNode())
-}
-
-private predicate argumentFlowsThrough(
- NodeExt arg, NodeExt out, DataFlowType t, Configuration config
-) {
- argumentFlowsThrough(arg.getNode(), out.getNode(), _, t, TSummaryTaint(), config)
-}
-
-/**
- * Holds if data can flow from `node1` to `node2` in one local step or a step
- * through a callable.
- */
-pragma[noinline]
-private predicate localFlowStepOrFlowThroughCallable(
- NodeExt node1, NodeExt node2, Configuration config
-) {
- exists(Node n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode()
- |
- nodeCand1(n1, config) and
- localFlowStep(n1, n2, config)
- or
- nodeCand1(n1, config) and
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentNone(), n2)
- )
-}
-
-/**
- * Holds if data can flow from `node1` to `node2` in one local step or a step
- * through a callable, in both cases using an additional flow step from the
- * configuration.
- */
-pragma[noinline]
-private predicate additionalLocalFlowStepOrFlowThroughCallable(
- NodeExt node1, NodeExt node2, Configuration config
-) {
- exists(Node n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode()
- |
- nodeCand1(n1, config) and
- additionalLocalFlowStep(n1, n2, config)
- or
- argumentFlowsThrough(n1, n2, _, _, TSummaryTaint(), config)
- )
- or
- additionalLocalFlowStepExt(node1, node2, _, config)
+pragma[nomagic]
+private predicate read(Node n1, Content c, Node n2, Configuration config) {
+ nodeCand1IsReadAndStored(c, config) and
+ nodeCand1(n2, unbind(config)) and
+ read(n1, c, n2)
}
pragma[noinline]
-private ReturnPosition getReturnPosition1(ReturnNodeExt node, Configuration config) {
- result = getReturnPosition(node) and
- nodeCand1(node, config)
+private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ localFlowStep(node1, node2, config)
+}
+
+pragma[noinline]
+private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ additionalLocalFlowStep(node1, node2, config)
+}
+
+pragma[nomagic]
+private predicate viableReturnPosOutNodeCand1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+) {
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, config)
}
/**
- * Holds if data can flow out of a callable from `node1` to `node2`, either
+ * Holds if data can flow out of `call` from `ret` to `out`, 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 flowOutOfCallableNodeCand1(ReturnNodeExt node1, Node node2, Configuration config) {
- nodeCand1(node2, config) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- exists(DataFlowCall call, ReturnKindExt kind |
- getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
- node2 = kind.getAnOutNode(call)
- )
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand1(
+ DataFlowCall call, ReturnNodeExt ret, Node out, Configuration config
+) {
+ viableReturnPosOutNodeCand1(call, getReturnPosition(ret), out, config) and
+ nodeCand1(ret, config) and
+ not outBarrier(ret, config) and
+ not inBarrier(out, config)
+}
+
+pragma[nomagic]
+private predicate viableParamArgNodeCand1(
+ DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+) {
+ viableParamArgNodeCandFwd1(call, p, arg, config) and
+ nodeCand1(arg, config)
}
/**
- * Holds if data can flow into a callable and that this step is part of a
+ * Holds if data can flow into `call` and that this step is part of a
* path from a source to a sink.
*/
-private predicate flowIntoCallableNodeCand1(
- ArgumentNode node1, ParameterNode node2, Configuration config
+pragma[nomagic]
+private predicate flowIntoCallNodeCand1(
+ DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config
) {
- viableParamArgCand(_, node2, node1, config)
+ viableParamArgNodeCand1(call, p, arg, config) and
+ nodeCand1(p, config) and
+ not outBarrier(arg, config) and
+ not inBarrier(p, config)
}
/**
@@ -999,7 +649,7 @@ private predicate flowIntoCallableNodeCand1(
private int branch(Node n1, Configuration conf) {
result =
strictcount(Node n |
- flowOutOfCallableNodeCand1(n1, n, conf) or flowIntoCallableNodeCand1(n1, n, conf)
+ flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1011,269 +661,391 @@ private int branch(Node n1, Configuration conf) {
private int join(Node n2, Configuration conf) {
result =
strictcount(Node n |
- flowOutOfCallableNodeCand1(n, n2, conf) or flowIntoCallableNodeCand1(n, n2, conf)
+ flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
/**
- * Holds if data can flow out of a callable from `node1` to `node2`, either
+ * Holds if data can flow out of `call` from `ret` to `out`, 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. The
* `allowsFieldFlow` flag indicates whether the branching is within the limit
* specified by the configuration.
*/
-private predicate flowOutOfCallableNodeCand1(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand1(
+ DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config
) {
- exists(ReturnNodeExt n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode() and
- flowOutOfCallableNodeCand1(n1, n2, config) and
- exists(int b, int j |
- b = branch(n1, config) and
- j = join(n2, config) and
- if b.minimum(j) <= config.fieldFlowBranchLimit()
- then allowsFieldFlow = true
- else allowsFieldFlow = false
- )
+ flowOutOfCallNodeCand1(call, ret, out, config) and
+ exists(int b, int j |
+ b = branch(ret, config) and
+ j = join(out, config) and
+ if b.minimum(j) <= config.fieldFlowBranchLimit()
+ then allowsFieldFlow = true
+ else allowsFieldFlow = false
)
}
/**
- * Holds if data can flow into a callable and that this step is part of a
+ * Holds if data can flow into `call` and that this step is part of a
* path from a source to a sink. The `allowsFieldFlow` flag indicates whether
* the branching is within the limit specified by the configuration.
*/
-private predicate flowIntoCallableNodeCand1(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+pragma[nomagic]
+private predicate flowIntoCallNodeCand1(
+ DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow,
+ Configuration config
) {
- exists(ArgumentNode n1, ParameterNode n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode() and
- flowIntoCallableNodeCand1(n1, n2, config) and
- exists(int b, int j |
- b = branch(n1, config) and
- j = join(n2, config) and
- if b.minimum(j) <= config.fieldFlowBranchLimit()
- then allowsFieldFlow = true
- else allowsFieldFlow = false
- )
+ flowIntoCallNodeCand1(call, arg, p, config) and
+ exists(int b, int j |
+ b = branch(arg, config) and
+ j = join(p, config) and
+ if b.minimum(j) <= config.fieldFlowBranchLimit()
+ then allowsFieldFlow = true
+ else allowsFieldFlow = false
)
}
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is reachable from a source in the configuration `config`.
+ * The Boolean `stored` records whether the tracked value is stored into a
+ * field of `node`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argStored` records whether the tracked
+ * value was stored into a field of the argument.
*/
-private predicate nodeCandFwd2(NodeExt node, boolean fromArg, boolean stored, Configuration config) {
- nodeCand1(node.getNode(), config) and
- config.isSource(node.getNode()) and
+private predicate nodeCandFwd2(
+ Node node, boolean fromArg, BooleanOption argStored, boolean stored, Configuration config
+) {
+ nodeCand1(node, config) and
+ config.isSource(node) and
fromArg = false and
+ argStored = TBooleanNone() and
stored = false
or
- node.isCand1(unbind(config)) and
+ nodeCand1(node, unbind(config)) and
(
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, stored, config) and
- localFlowStepOrFlowThroughCallable(mid, node, config)
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, stored, config) and
+ localFlowStepNodeCand1(mid, node, config)
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, stored, config) and
- additionalLocalFlowStepOrFlowThroughCallable(mid, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, stored, config) and
+ additionalLocalFlowStepNodeCand1(mid, node, config) and
stored = false
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, _, stored, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
+ exists(Node mid |
+ nodeCandFwd2(mid, _, _, stored, config) and
+ jumpStep(mid, node, config) and
+ fromArg = false and
+ argStored = TBooleanNone()
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, _, stored, config) and
- additionalJumpStepExt(mid, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, _, _, stored, config) and
+ additionalJumpStep(mid, node, config) and
fromArg = false and
+ argStored = TBooleanNone() and
stored = false
)
or
// store
- exists(NodeExt mid, Content f |
- nodeCandFwd2(mid, fromArg, _, config) and
- storeExt(mid, f, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, _, config) and
+ storeCand1(mid, _, node, config) and
stored = true
)
or
// read
- exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, config) and
- storeCandFwd2(f, stored, config)
+ exists(Content c |
+ nodeCandFwd2Read(c, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(c, stored, config)
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- nodeCandFwd2(mid, _, stored, config) and
- flowIntoCallableNodeCand1(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (stored = false or allowsFieldFlow = true)
- )
+ // flow into a callable
+ nodeCandFwd2In(_, node, _, _, stored, config) and
+ fromArg = true and
+ if parameterThroughFlowNodeCand1(node, config)
+ then argStored = TBooleanSome(stored)
+ else argStored = TBooleanNone()
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- nodeCandFwd2(mid, false, stored, config) and
- flowOutOfCallableNodeCand1(mid, node, allowsFieldFlow, config) and
- fromArg = false and
- (stored = false or allowsFieldFlow = true)
+ // flow out of a callable
+ exists(DataFlowCall call |
+ nodeCandFwd2Out(call, node, fromArg, argStored, stored, config) and
+ fromArg = false
+ or
+ exists(boolean argStored0 |
+ nodeCandFwd2OutFromArg(call, node, argStored0, stored, config) and
+ nodeCandFwd2IsEntered(call, fromArg, argStored, argStored0, config)
+ )
)
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
*/
pragma[noinline]
-private predicate storeCandFwd2(Content f, boolean stored, Configuration config) {
- exists(NodeExt mid, NodeExt node |
+private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
+ exists(Node mid, Node node |
useFieldFlow(config) and
- node.isCand1(unbind(config)) and
- nodeCandFwd2(mid, _, stored, config) and
- storeExt(mid, f, node, config)
+ nodeCand1(node, unbind(config)) and
+ nodeCandFwd2(mid, _, _, stored, config) and
+ storeCand1(mid, c, node, config)
)
}
pragma[nomagic]
-private predicate nodeCandFwd2Read(Content f, NodeExt node, boolean fromArg, Configuration config) {
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, true, config) and
- readExt(mid, f, node, config)
+private predicate nodeCandFwd2Read(
+ Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+) {
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, true, config) and
+ read(mid, c, node, config)
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2In(
+ DataFlowCall call, ParameterNode p, boolean fromArg, BooleanOption argStored, boolean stored,
+ Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ nodeCandFwd2(arg, fromArg, argStored, stored, config) and
+ flowIntoCallNodeCand1(call, arg, p, allowsFieldFlow, config)
+ |
+ stored = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2Out(
+ DataFlowCall call, Node out, boolean fromArg, BooleanOption argStored, boolean stored,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ nodeCandFwd2(ret, fromArg, argStored, stored, config) and
+ flowOutOfCallNodeCand1(call, ret, out, allowsFieldFlow, config)
+ |
+ stored = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2OutFromArg(
+ DataFlowCall call, Node out, boolean argStored, boolean stored, Configuration config
+) {
+ nodeCandFwd2Out(call, out, true, TBooleanSome(argStored), stored, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `nodeCandFwd2`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd2IsEntered(
+ DataFlowCall call, boolean fromArg, BooleanOption argStored, boolean stored, Configuration config
+) {
+ exists(ParameterNode p |
+ nodeCandFwd2In(call, p, fromArg, argStored, stored, config) and
+ parameterThroughFlowNodeCand1(p, config)
)
}
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is part of a path from a source to a sink in the
+ * configuration `config`. The Boolean `read` records whether the tracked
+ * value must be read from a field of `node` in order to reach a sink.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnRead`
+ * records whether a field must be read from the returned value.
*/
-private predicate nodeCand2(NodeExt node, boolean toReturn, boolean read, Configuration config) {
- nodeCandFwd2(node, _, false, config) and
- config.isSink(node.getNode()) and
+private predicate nodeCand2(
+ Node node, boolean toReturn, BooleanOption returnRead, boolean read, Configuration config
+) {
+ nodeCandFwd2(node, _, _, false, config) and
+ config.isSink(node) and
toReturn = false and
+ returnRead = TBooleanNone() and
read = false
or
- nodeCandFwd2(node, _, unbindBool(read), unbind(config)) and
+ nodeCandFwd2(node, _, _, unbindBool(read), unbind(config)) and
(
- exists(NodeExt mid |
- localFlowStepOrFlowThroughCallable(node, mid, config) and
- nodeCand2(mid, toReturn, read, config)
+ exists(Node mid |
+ localFlowStepNodeCand1(node, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, read, config)
)
or
- exists(NodeExt mid |
- additionalLocalFlowStepOrFlowThroughCallable(node, mid, config) and
- nodeCand2(mid, toReturn, read, config) and
+ exists(Node mid |
+ additionalLocalFlowStepNodeCand1(node, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, read, config) and
read = false
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- nodeCand2(mid, _, read, config) and
- toReturn = false
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ nodeCand2(mid, _, _, read, config) and
+ toReturn = false and
+ returnRead = TBooleanNone()
)
or
- exists(NodeExt mid |
- additionalJumpStepExt(node, mid, config) and
- nodeCand2(mid, _, read, config) and
+ exists(Node mid |
+ additionalJumpStep(node, mid, config) and
+ nodeCand2(mid, _, _, read, config) and
toReturn = false and
+ returnRead = TBooleanNone() and
read = false
)
or
// store
- exists(Content f |
- nodeCand2Store(f, node, toReturn, read, config) and
- readCand2(f, read, config)
+ exists(Content c |
+ nodeCand2Store(c, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(c, read, config)
)
or
// read
- exists(NodeExt mid, Content f, boolean read0 |
- readExt(node, f, mid, config) and
- storeCandFwd2(f, unbindBool(read0), unbind(config)) and
- nodeCand2(mid, toReturn, read0, config) and
+ exists(Node mid, Content c, boolean read0 |
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
+ nodeCand2(mid, toReturn, returnRead, read0, config) and
read = true
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand1(node, mid, allowsFieldFlow, config) and
- nodeCand2(mid, false, read, config) and
- toReturn = false and
- (read = false or allowsFieldFlow = true)
+ // flow into a callable
+ exists(DataFlowCall call |
+ nodeCand2In(call, node, toReturn, returnRead, read, config) and
+ toReturn = false
+ or
+ exists(boolean returnRead0 |
+ nodeCand2InToReturn(call, node, returnRead0, read, config) and
+ nodeCand2IsReturned(call, toReturn, returnRead, returnRead0, config)
+ )
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand1(node, mid, allowsFieldFlow, config) and
- nodeCand2(mid, _, read, config) and
- toReturn = true and
- (read = false or allowsFieldFlow = true)
- )
+ // flow out of a callable
+ nodeCand2Out(_, node, _, _, read, config) and
+ toReturn = true and
+ if nodeCandFwd2(node, true, TBooleanSome(_), unbindBool(read), config)
+ then returnRead = TBooleanSome(read)
+ else returnRead = TBooleanNone()
)
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate readCand2(Content f, boolean read, Configuration config) {
- exists(NodeExt mid, NodeExt node |
+private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
+ exists(Node mid, Node node |
useFieldFlow(config) and
- nodeCandFwd2(node, _, true, unbind(config)) and
- readExt(node, f, mid, config) and
- storeCandFwd2(f, unbindBool(read), unbind(config)) and
- nodeCand2(mid, _, read, config)
+ nodeCandFwd2(node, _, _, true, unbind(config)) and
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
+ nodeCand2(mid, _, _, read, config)
)
}
pragma[nomagic]
private predicate nodeCand2Store(
- Content f, NodeExt node, boolean toReturn, boolean stored, Configuration config
+ Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
+ Configuration config
) {
- exists(NodeExt mid |
- storeExt(node, f, mid, config) and
- nodeCand2(mid, toReturn, true, config) and
- nodeCandFwd2(node, _, stored, unbind(config))
- )
-}
-
-pragma[nomagic]
-private predicate storeCand2(Content f, boolean stored, Configuration conf) {
- exists(NodeExt node |
- nodeCand2Store(f, node, _, stored, conf) and
- nodeCand2(node, _, stored, conf)
+ exists(Node mid |
+ storeCand1(node, c, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, true, config) and
+ nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
/**
- * Holds if `f` is the target of both a store and a read in the path graph
- * covered by `nodeCand2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
*/
-pragma[noinline]
-private predicate readStoreCand(Content f, Configuration conf) {
- exists(boolean apNonEmpty |
- storeCand2(f, apNonEmpty, conf) and
- readCand2(f, apNonEmpty, conf)
+pragma[nomagic]
+private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
+ exists(Node node |
+ nodeCand2Store(c, node, _, _, stored, conf) and
+ nodeCand2(node, _, _, stored, conf)
)
}
-private predicate nodeCand2(NodeExt node, Configuration config) { nodeCand2(node, _, _, config) }
+/**
+ * Holds if `c` is the target of both a store and a read in the path graph
+ * covered by `nodeCand2`.
+ */
+pragma[noinline]
+private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
+ exists(boolean apNonEmpty |
+ nodeCand2IsStored(c, apNonEmpty, conf) and
+ nodeCand2IsRead(c, apNonEmpty, conf)
+ )
+}
pragma[nomagic]
-private predicate flowOutOfCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate nodeCand2Out(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
) {
- flowOutOfCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ exists(Node out, boolean allowsFieldFlow |
+ nodeCand2(out, toReturn, returnRead, read, config) and
+ flowOutOfCallNodeCand1(call, ret, out, allowsFieldFlow, config)
+ |
+ read = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCand2In(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ nodeCand2(p, toReturn, returnRead, read, config) and
+ flowIntoCallNodeCand1(call, arg, p, allowsFieldFlow, config)
+ |
+ read = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCand2InToReturn(
+ DataFlowCall call, ArgumentNode arg, boolean returnRead, boolean read, Configuration config
+) {
+ nodeCand2In(call, arg, true, TBooleanSome(returnRead), read, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `nodeCand2`.
+ */
+pragma[nomagic]
+private predicate nodeCand2IsReturned(
+ DataFlowCall call, boolean toReturn, BooleanOption returnRead, boolean read, Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ nodeCand2Out(call, ret, toReturn, returnRead, read, config) and
+ nodeCandFwd2(ret, true, TBooleanSome(_), read, config)
+ )
+}
+
+private predicate nodeCand2(Node node, Configuration config) { nodeCand2(node, _, _, _, config) }
+
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand2(
+ DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
+) {
+ flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
nodeCand2(node2, config) and
nodeCand2(node1, unbind(config))
}
pragma[nomagic]
-private predicate flowIntoCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate flowIntoCallNodeCand2(
+ DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ Configuration config
) {
- flowIntoCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
nodeCand2(node2, config) and
nodeCand2(node1, unbind(config))
}
@@ -1284,15 +1056,15 @@ private module LocalFlowBigStep {
* flow steps in a dataflow path.
*/
private predicate localFlowEntry(Node node, Configuration config) {
- nodeCand2(TNormalNode(node), config) and
+ nodeCand2(node, config) and
(
config.isSource(node) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
node instanceof ParameterNode or
- node instanceof OutNode or
- node instanceof PostUpdateNode or
- readDirect(_, _, node) or
+ node instanceof OutNodeExt or
+ store(_, _, node, _) or
+ read(_, _, node) or
node instanceof CastNode
)
}
@@ -1302,15 +1074,13 @@ private module LocalFlowBigStep {
* flow steps in a dataflow path.
*/
private predicate localFlowExit(Node node, Configuration config) {
- exists(Node next | nodeCand2(TNormalNode(next), config) |
+ exists(Node next | nodeCand2(next, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
- flowIntoCallableNodeCand1(node, next, config) or
- flowOutOfCallableNodeCand1(node, next, config) or
- argumentFlowsThrough(node, next, _, _, _, config) or
- argumentValueFlowsThrough(_, node, TContentNone(), TContentNone(), next) or
- storeDirect(node, _, next) or
- readDirect(node, _, next)
+ flowIntoCallNodeCand1(_, node, next, config) or
+ flowOutOfCallNodeCand1(_, node, next, config) or
+ store(node, _, next, _) or
+ read(node, _, next)
)
or
node instanceof CastNode
@@ -1318,6 +1088,13 @@ private module LocalFlowBigStep {
config.isSink(node)
}
+ pragma[noinline]
+ private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
+ additionalLocalFlowStepNodeCand1(node1, node2, config) and
+ nodeCand2(node1, _, _, false, config) and
+ nodeCand2(node2, _, _, false, unbind(config))
+ }
+
/**
* Holds if the local path from `node1` to `node2` is a prefix of a maximal
* subsequence of local flow steps in a dataflow path.
@@ -1334,33 +1111,33 @@ private module LocalFlowBigStep {
(
localFlowEntry(node1, config) and
(
- localFlowStep(node1, node2, config) and
+ localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
t = getErasedNodeTypeBound(node1)
or
- additionalLocalFlowStep(node1, node2, config) and
+ additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
t = getErasedNodeTypeBound(node2)
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
or
exists(Node mid |
localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
- localFlowStep(mid, node2, config) and
+ localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof CastNode and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
)
or
exists(Node mid |
localFlowStepPlus(node1, mid, _, _, config, cc) and
- additionalLocalFlowStep(mid, node2, config) and
+ additionalLocalFlowStepNodeCand2(mid, node2, config) and
not mid instanceof CastNode and
preservesValue = false and
t = getErasedNodeTypeBound(node2) and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
)
)
}
@@ -1371,311 +1148,380 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config,
+ Node node1, Node node2, boolean preservesValue, AccessPathFrontNil apf, Configuration config,
LocalCallContext callContext
) {
- localFlowStepPlus(node1, node2, preservesValue, t, config, callContext) and
+ localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, config)
}
-
- pragma[nomagic]
- predicate localFlowBigStepExt(
- NodeExt node1, NodeExt node2, boolean preservesValue, AccessPathFrontNil apf,
- Configuration config
- ) {
- localFlowBigStep(node1.getNode(), node2.getNode(), preservesValue, apf.getType(), config, _)
- or
- additionalLocalFlowStepExt(node1, node2, apf.getType(), config) and
- nodeCand2(node1, config) and
- nodeCand2(node2, unbind(config)) and
- preservesValue = false
- }
}
private import LocalFlowBigStep
pragma[nomagic]
-private predicate readExtCand2(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- readExt(node1, f, node2, config) and
- nodeCand2(node1, _, true, unbind(config)) and
+private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
+ read(node1, c, node2, config) and
+ nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- readStoreCand(f, unbind(config))
+ nodeCand2IsReadAndStored(c, unbind(config))
}
pragma[nomagic]
-private predicate storeExtCand2(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- storeExt(node1, f, node2, config) and
+private predicate storeCand2(
+ Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+) {
+ store(node1, tc, node2, contentType) and
nodeCand2(node1, config) and
- nodeCand2(node2, _, true, unbind(config)) and
- readStoreCand(f, unbind(config))
-}
-
-private newtype TAccessPathFront =
- TFrontNil(DataFlowType t) or
- TFrontHead(Content f)
-
-/**
- * The front of an `AccessPath`. This is either a head or a nil.
- */
-abstract private class AccessPathFront extends TAccessPathFront {
- abstract string toString();
-
- abstract DataFlowType getType();
-
- abstract boolean toBoolNonEmpty();
-
- predicate headUsesContent(Content f) { this = TFrontHead(f) }
-}
-
-private class AccessPathFrontNil extends AccessPathFront, TFrontNil {
- override string toString() {
- exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
- }
-
- override DataFlowType getType() { this = TFrontNil(result) }
-
- override boolean toBoolNonEmpty() { result = false }
-}
-
-private class AccessPathFrontHead extends AccessPathFront, TFrontHead {
- override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) }
-
- override DataFlowType getType() {
- exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
- }
-
- override boolean toBoolNonEmpty() { result = true }
+ nodeCand2(node2, _, _, true, unbind(config)) and
+ nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
}
/**
- * Holds if data can flow from a source to `node` with the given `apf`.
+ * Holds if `node` is reachable with access path front `apf` from a
+ * source in the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argApf` records the front of the
+ * access path of that argument.
*/
pragma[nomagic]
private predicate flowCandFwd(
- NodeExt node, boolean fromArg, AccessPathFront apf, Configuration config
+ Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
) {
- flowCandFwd0(node, fromArg, apf, config) and
- if node instanceof CastingNodeExt
- then compatibleTypes(node.getErasedNodeTypeBound(), apf.getType())
+ flowCandFwd0(node, fromArg, argApf, apf, config) and
+ if node instanceof CastingNode
+ then compatibleTypes(getErasedNodeTypeBound(node), apf.getType())
else any()
}
pragma[nomagic]
private predicate flowCandFwd0(
- NodeExt node, boolean fromArg, AccessPathFront apf, Configuration config
+ Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
) {
- nodeCand2(node, _, false, config) and
- config.isSource(node.getNode()) and
+ nodeCand2(node, _, _, false, config) and
+ config.isSource(node) and
fromArg = false and
- apf = TFrontNil(node.getErasedNodeTypeBound())
+ argApf = TAccessPathFrontNone() and
+ apf = TFrontNil(getErasedNodeTypeBound(node))
or
- exists(NodeExt mid |
- flowCandFwd(mid, fromArg, apf, config) and
- localFlowBigStepExt(mid, node, true, _, config)
+ exists(Node mid |
+ flowCandFwd(mid, fromArg, argApf, apf, config) and
+ localFlowBigStep(mid, node, true, _, config, _)
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(mid, fromArg, nil, config) and
- localFlowBigStepExt(mid, node, false, apf, config)
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(mid, fromArg, argApf, nil, config) and
+ localFlowBigStep(mid, node, false, apf, config, _)
)
or
- nodeCand2(node, unbind(config)) and
- (
- exists(NodeExt mid |
- flowCandFwd(mid, _, apf, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(mid, _, nil, config) and
- additionalJumpStepExt(mid, node, config) and
- fromArg = false and
- apf = TFrontNil(node.getErasedNodeTypeBound())
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowCandFwd(mid, _, apf, config) and
- flowIntoCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowCandFwd(mid, false, apf, config) and
- flowOutOfCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = false and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- flowCandFwd(mid, fromArg, apf, config) and
- argumentValueFlowsThrough(mid, node)
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil, DataFlowType t |
- flowCandFwd(mid, fromArg, nil, config) and
- argumentFlowsThrough(mid, node, t, config) and
- apf = TFrontNil(t)
- )
+ exists(Node mid |
+ flowCandFwd(mid, _, _, apf, config) and
+ nodeCand2(node, unbind(config)) and
+ jumpStep(mid, node, config) and
+ fromArg = false and
+ argApf = TAccessPathFrontNone()
)
or
- exists(NodeExt mid, Content f |
- flowCandFwd(mid, fromArg, _, config) and
- storeExtCand2(mid, f, node, config) and
- nodeCand2(node, _, true, unbind(config)) and
- apf.headUsesContent(f)
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(mid, _, _, nil, config) and
+ nodeCand2(node, unbind(config)) and
+ additionalJumpStep(mid, node, config) and
+ fromArg = false and
+ argApf = TAccessPathFrontNone() and
+ apf = TFrontNil(getErasedNodeTypeBound(node))
)
or
- exists(Content f |
- flowCandFwdRead(f, node, fromArg, config) and
- consCandFwd(f, apf, config) and
- nodeCand2(node, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ // store
+ exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ storeCand2(mid, tc, node, contentType, config) and
+ nodeCand2(node, _, _, true, unbind(config)) and
+ apf.headUsesContent(tc) and
+ compatibleTypes(apf0.getType(), contentType)
+ )
+ or
+ // read
+ exists(TypedContent tc |
+ flowCandFwdRead(tc, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(tc, apf, config) and
+ nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ )
+ or
+ // flow into a callable
+ flowCandFwdIn(_, node, _, _, apf, config) and
+ fromArg = true and
+ if nodeCand2(node, true, _, unbindBool(apf.toBoolNonEmpty()), config)
+ then argApf = TAccessPathFrontSome(apf)
+ else argApf = TAccessPathFrontNone()
+ or
+ // flow out of a callable
+ exists(DataFlowCall call |
+ flowCandFwdOut(call, node, fromArg, argApf, apf, config) and
+ fromArg = false
+ or
+ exists(AccessPathFront argApf0 |
+ flowCandFwdOutFromArg(call, node, argApf0, apf, config) and
+ flowCandFwdIsEntered(call, fromArg, argApf, argApf0, config)
+ )
)
}
pragma[nomagic]
-private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) {
- exists(NodeExt mid, NodeExt n |
- flowCandFwd(mid, _, apf, config) and
- storeExtCand2(mid, f, n, config) and
- nodeCand2(n, _, true, unbind(config)) and
- compatibleTypes(apf.getType(), f.getType())
+private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n, DataFlowType contentType |
+ flowCandFwd(mid, _, _, apf, config) and
+ storeCand2(mid, tc, n, contentType, config) and
+ nodeCand2(n, _, _, true, unbind(config)) and
+ compatibleTypes(apf.getType(), contentType)
)
}
pragma[nomagic]
-private predicate flowCandFwdRead(Content f, NodeExt node, boolean fromArg, Configuration config) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowCandFwd(mid, fromArg, apf0, config) and
- readExtCand2(mid, f, node, config) and
- apf0.headUsesContent(f)
+private predicate flowCandFwdRead0(
+ Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFrontHead apf, Configuration config
+) {
+ flowCandFwd(node1, fromArg, argApf, apf, config) and
+ readCand2(node1, c, node2, config) and
+ apf.headUsesContent(tc)
+}
+
+pragma[nomagic]
+private predicate flowCandFwdRead(
+ TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+) {
+ flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
+}
+
+pragma[nomagic]
+private predicate flowCandFwdIn(
+ DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ flowCandFwd(arg, fromArg, argApf, apf, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ flowCandFwd(ret, fromArg, argApf, apf, config) and
+ flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandFwdOutFromArg(
+ DataFlowCall call, Node node, AccessPathFront argApf, AccessPathFront apf, Configuration config
+) {
+ flowCandFwdOut(call, node, true, TAccessPathFrontSome(argApf), apf, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `flowCandFwd`.
+ */
+pragma[nomagic]
+private predicate flowCandFwdIsEntered(
+ DataFlowCall call, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ParameterNode p |
+ flowCandFwdIn(call, p, fromArg, argApf, apf, config) and
+ nodeCand2(p, true, TBooleanSome(_), unbindBool(apf.toBoolNonEmpty()), config)
)
}
/**
- * Holds if data can flow from a source to `node` with the given `apf` and
- * from there flow to a sink.
+ * Holds if `node` with access path front `apf` is part of a path from a
+ * source to a sink in the configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnApf`
+ * records the front of the access path of the returned value.
*/
pragma[nomagic]
-private predicate flowCand(NodeExt node, boolean toReturn, AccessPathFront apf, Configuration config) {
- flowCand0(node, toReturn, apf, config) and
- flowCandFwd(node, _, apf, config)
+private predicate flowCand(
+ Node node, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ flowCand0(node, toReturn, returnApf, apf, config) and
+ flowCandFwd(node, _, _, apf, config)
}
pragma[nomagic]
private predicate flowCand0(
- NodeExt node, boolean toReturn, AccessPathFront apf, Configuration config
+ Node node, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
) {
- flowCandFwd(node, _, apf, config) and
- config.isSink(node.getNode()) and
+ flowCandFwd(node, _, _, apf, config) and
+ config.isSink(node) and
toReturn = false and
+ returnApf = TAccessPathFrontNone() and
apf instanceof AccessPathFrontNil
or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flowCand(mid, toReturn, apf, config)
+ exists(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flowCand(mid, toReturn, returnApf, apf, config)
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- localFlowBigStepExt(node, mid, false, _, config) and
- flowCand(mid, toReturn, nil, config) and
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(node, _, _, apf, config) and
+ localFlowBigStep(node, mid, false, _, config, _) and
+ flowCand(mid, toReturn, returnApf, nil, config) and
apf instanceof AccessPathFrontNil
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flowCand(mid, _, apf, config) and
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flowCand(mid, _, _, apf, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone()
+ )
+ or
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(node, _, _, apf, config) and
+ additionalJumpStep(node, mid, config) and
+ flowCand(mid, _, _, nil, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone() and
+ apf instanceof AccessPathFrontNil
+ )
+ or
+ // store
+ exists(TypedContent tc |
+ flowCandStore(node, tc, apf, toReturn, returnApf, config) and
+ flowCandConsCand(tc, apf, config)
+ )
+ or
+ // read
+ exists(TypedContent tc, AccessPathFront apf0 |
+ flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(tc, apf0, config)
+ )
+ or
+ // flow into a callable
+ exists(DataFlowCall call |
+ flowCandIn(call, node, toReturn, returnApf, apf, config) and
toReturn = false
+ or
+ exists(AccessPathFront returnApf0 |
+ flowCandInToReturn(call, node, returnApf0, apf, config) and
+ flowCandIsReturned(call, toReturn, returnApf, returnApf0, config)
+ )
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- additionalJumpStepExt(node, mid, config) and
- flowCand(mid, _, nil, config) and
- toReturn = false and
- apf instanceof AccessPathFrontNil
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flowCand(mid, false, apf, config) and
- toReturn = false and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flowCand(mid, _, apf, config) and
- toReturn = true and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- argumentValueFlowsThrough(node, mid) and
- flowCand(mid, toReturn, apf, config)
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil |
- argumentFlowsThrough(node, mid, _, config) and
- flowCand(mid, toReturn, nil, config) and
- apf instanceof AccessPathFrontNil and
- flowCandFwd(node, _, apf, config)
- )
- or
- exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, apf0, config) and
- apf0.headUsesContent(f) and
- consCand(f, apf, config)
- )
- or
- exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, config) and
- apf.headUsesContent(f)
- )
+ // flow out of a callable
+ flowCandOut(_, node, _, _, apf, config) and
+ toReturn = true and
+ if flowCandFwd(node, true, _, apf, config)
+ then returnApf = TAccessPathFrontSome(apf)
+ else returnApf = TAccessPathFrontNone()
+}
+
+pragma[nomagic]
+private predicate readCandFwd(
+ Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
+) {
+ flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
}
pragma[nomagic]
private predicate flowCandRead(
- NodeExt node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
) {
- exists(NodeExt mid |
- readExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ readCandFwd(node, tc, apf, mid, config) and
+ flowCand(mid, toReturn, returnApf, apf0, config)
)
}
pragma[nomagic]
private predicate flowCandStore(
- NodeExt node, Content f, boolean toReturn, AccessPathFrontHead apf0, Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, Configuration config
) {
- exists(NodeExt mid |
- storeExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ flowCandFwd(node, _, _, apf, config) and
+ storeCand2(node, tc, mid, _, unbind(config)) and
+ flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
)
}
pragma[nomagic]
-private predicate consCand(Content f, AccessPathFront apf, Configuration config) {
- consCandFwd(f, apf, config) and
- exists(NodeExt n, AccessPathFrontHead apf0 |
- flowCandFwd(n, _, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandRead(n, f, _, apf, config)
+private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(tc, apf, config) and
+ flowCandRead(_, tc, _, _, _, apf, config)
+}
+
+pragma[nomagic]
+private predicate flowCandOut(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathFrontOption returnApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(Node out, boolean allowsFieldFlow |
+ flowCand(out, toReturn, returnApf, apf, config) and
+ flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandIn(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathFrontOption returnApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ flowCand(p, toReturn, returnApf, apf, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandInToReturn(
+ DataFlowCall call, ArgumentNode arg, AccessPathFront returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ flowCandIn(call, arg, true, TAccessPathFrontSome(returnApf), apf, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `flowCand`.
+ */
+pragma[nomagic]
+private predicate flowCandIsReturned(
+ DataFlowCall call, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ flowCandOut(call, ret, toReturn, returnApf, apf, config) and
+ flowCandFwd(ret, true, TAccessPathFrontSome(_), apf, config)
)
}
private newtype TAccessPath =
TNil(DataFlowType t) or
- TConsNil(Content f, DataFlowType t) { consCand(f, TFrontNil(t), _) } or
- TConsCons(Content f1, Content f2, int len) {
- consCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
+ TConsCons(TypedContent tc1, TypedContent tc2, int len) {
+ flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
}
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first two
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
* elements of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -1684,7 +1530,7 @@ private newtype TAccessPath =
abstract private class AccessPath extends TAccessPath {
abstract string toString();
- abstract Content getHead();
+ abstract TypedContent getHead();
abstract int len();
@@ -1695,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
/**
* Holds if this access path has `head` at the front and may be followed by `tail`.
*/
- abstract predicate pop(Content head, AccessPath tail);
+ abstract predicate pop(TypedContent head, AccessPath tail);
}
private class AccessPathNil extends AccessPath, TNil {
@@ -1705,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
override string toString() { result = concat(": " + ppReprType(t)) }
- override Content getHead() { none() }
+ override TypedContent getHead() { none() }
override int len() { result = 0 }
@@ -1713,357 +1559,469 @@ private class AccessPathNil extends AccessPath, TNil {
override AccessPathFront getFront() { result = TFrontNil(t) }
- override predicate pop(Content head, AccessPath tail) { none() }
+ override predicate pop(TypedContent head, AccessPath tail) { none() }
}
abstract private class AccessPathCons extends AccessPath { }
private class AccessPathConsNil extends AccessPathCons, TConsNil {
- private Content f;
+ private TypedContent tc;
private DataFlowType t;
- AccessPathConsNil() { this = TConsNil(f, t) }
+ AccessPathConsNil() { this = TConsNil(tc, t) }
override string toString() {
// The `concat` becomes "" if `ppReprType` has no result.
- result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
+ result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
}
- override Content getHead() { result = f }
+ override TypedContent getHead() { result = tc }
override int len() { result = 1 }
- override DataFlowType getType() { result = f.getContainerType() }
+ override DataFlowType getType() { result = tc.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f) }
+ override AccessPathFront getFront() { result = TFrontHead(tc) }
- override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
+ override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
}
private class AccessPathConsCons extends AccessPathCons, TConsCons {
- private Content f1;
- private Content f2;
+ private TypedContent tc1;
+ private TypedContent tc2;
private int len;
- AccessPathConsCons() { this = TConsCons(f1, f2, len) }
+ AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
override string toString() {
if len = 2
- then result = "[" + f1.toString() + ", " + f2.toString() + "]"
- else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
+ else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
}
- override Content getHead() { result = f1 }
+ override TypedContent getHead() { result = tc1 }
override int len() { result = len }
- override DataFlowType getType() { result = f1.getContainerType() }
+ override DataFlowType getType() { result = tc1.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f1) }
+ override AccessPathFront getFront() { result = TFrontHead(tc1) }
- override predicate pop(Content head, AccessPath tail) {
- head = f1 and
+ override predicate pop(TypedContent head, AccessPath tail) {
+ head = tc1 and
(
- tail = TConsCons(f2, _, len - 1)
+ tail = TConsCons(tc2, _, len - 1)
or
len = 2 and
- tail = TConsNil(f2, _)
+ tail = TConsNil(tc2, _)
)
}
}
-/** Gets the access path obtained by popping `f` from `ap`, if any. */
-private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
+/** Gets the access path obtained by popping `tc` from `ap`, if any. */
+private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
-/** Gets the access path obtained by pushing `f` onto `ap`. */
-private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
+/** Gets the access path obtained by pushing `tc` onto `ap`. */
+private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
+
+private newtype TAccessPathOption =
+ TAccessPathNone() or
+ TAccessPathSome(AccessPath ap)
+
+private class AccessPathOption extends TAccessPathOption {
+ string toString() {
+ this = TAccessPathNone() and result = ""
+ or
+ this = TAccessPathSome(any(AccessPath ap | result = ap.toString()))
+ }
+}
/**
- * Holds if data can flow from a source to `node` with the given `ap`.
+ * Holds if `node` is reachable with access path `ap` from a source in
+ * the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argAp` records the access path of that
+ * argument.
*/
private predicate flowFwd(
- NodeExt node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config
+ Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
) {
- flowFwd0(node, fromArg, apf, ap, config) and
- flowCand(node, _, apf, config)
+ flowFwd0(node, fromArg, argAp, apf, ap, config) and
+ flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
- NodeExt node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config
+ Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
) {
- flowCand(node, _, _, config) and
- config.isSource(node.getNode()) and
+ flowCand(node, _, _, _, config) and
+ config.isSource(node) and
fromArg = false and
- ap = TNil(node.getErasedNodeTypeBound()) and
+ argAp = TAccessPathNone() and
+ ap = TNil(getErasedNodeTypeBound(node)) and
apf = ap.(AccessPathNil).getFront()
or
- flowCand(node, _, _, unbind(config)) and
+ flowCand(node, _, _, _, unbind(config)) and
(
- exists(NodeExt mid |
- flowFwd(mid, fromArg, apf, ap, config) and
- localFlowBigStepExt(mid, node, true, _, config)
+ exists(Node mid |
+ flowFwd(mid, fromArg, argAp, apf, ap, config) and
+ localFlowBigStep(mid, node, true, _, config, _)
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(mid, fromArg, _, nil, config) and
- localFlowBigStepExt(mid, node, false, apf, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(mid, fromArg, argAp, _, nil, config) and
+ localFlowBigStep(mid, node, false, apf, config, _) and
apf = ap.(AccessPathNil).getFront()
)
or
- exists(NodeExt mid |
- flowFwd(mid, _, apf, ap, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
- )
- or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(mid, _, _, nil, config) and
- additionalJumpStepExt(mid, node, config) and
+ exists(Node mid |
+ flowFwd(mid, _, _, apf, ap, config) and
+ jumpStep(mid, node, config) and
fromArg = false and
- ap = TNil(node.getErasedNodeTypeBound()) and
- apf = ap.(AccessPathNil).getFront()
+ argAp = TAccessPathNone()
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowFwd(mid, _, apf, ap, config) and
- flowIntoCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowFwd(mid, false, apf, ap, config) and
- flowOutOfCallableNodeCand2(mid, node, allowsFieldFlow, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(mid, _, _, _, nil, config) and
+ additionalJumpStep(mid, node, config) and
fromArg = false and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- flowFwd(mid, fromArg, apf, ap, config) and
- argumentValueFlowsThrough(mid, node)
- )
- or
- exists(NodeExt mid, AccessPathNil nil, DataFlowType t |
- flowFwd(mid, fromArg, _, nil, config) and
- argumentFlowsThrough(mid, node, t, config) and
- ap = TNil(t) and
+ argAp = TAccessPathNone() and
+ ap = TNil(getErasedNodeTypeBound(node)) and
apf = ap.(AccessPathNil).getFront()
)
)
or
- exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, config) and
- ap = push(f, ap0)
+ // store
+ exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
+ or
+ // read
+ exists(TypedContent tc |
+ flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
+ flowFwdConsCand(tc, apf, ap, config)
)
or
- exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, config) and
- flowConsCandFwd(f, apf, ap, config)
+ // flow into a callable
+ flowFwdIn(_, node, _, _, apf, ap, config) and
+ fromArg = true and
+ if flowCand(node, true, _, apf, config)
+ then argAp = TAccessPathSome(ap)
+ else argAp = TAccessPathNone()
+ or
+ // flow out of a callable
+ exists(DataFlowCall call |
+ flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
+ fromArg = false
+ or
+ exists(AccessPath argAp0 |
+ flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and
+ flowFwdIsEntered(call, fromArg, argAp, argAp0, config)
+ )
)
}
pragma[nomagic]
private predicate flowFwdStore(
- NodeExt node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
- Configuration config
+ Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFront apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- flowFwdStore1(mid, f, node, apf0, apf, config)
+ exists(Node mid, AccessPathFront apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
+ flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
pragma[nomagic]
-private predicate flowFwdStore0(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, Configuration config
+private predicate storeCand(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
+ Configuration config
) {
- storeExtCand2(mid, f, node, config) and
- flowCand(mid, _, apf0, config)
+ storeCand2(mid, tc, node, _, config) and
+ flowCand(mid, _, _, apf0, config) and
+ apf.headUsesContent(tc)
}
pragma[noinline]
-private predicate flowFwdStore1(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, AccessPathFrontHead apf,
+private predicate flowFwdStore0(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
- flowFwdStore0(mid, f, node, apf0, config) and
- consCand(f, apf0, config) and
- apf.headUsesContent(f) and
- flowCand(node, _, apf, unbind(config))
+ storeCand(mid, tc, node, apf0, apf, config) and
+ flowCandConsCand(tc, apf0, config) and
+ flowCand(node, _, _, apf, unbind(config))
+}
+
+pragma[nomagic]
+private predicate flowFwdRead0(
+ Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
+ boolean fromArg, AccessPathOption argAp, Configuration config
+) {
+ flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
+ readCandFwd(node1, tc, apf0, node2, config)
}
pragma[nomagic]
private predicate flowFwdRead(
- NodeExt node, Content f, AccessPath ap0, boolean fromArg, Configuration config
+ Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- readExtCand2(mid, f, node, config) and
- apf0.headUsesContent(f) and
- flowCand(node, _, _, unbind(config))
+ exists(Node mid, TypedContent tc |
+ flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
+ flowCand(node, _, _, apf, unbind(config)) and
+ flowCandConsCand(tc, apf, unbind(config))
)
}
pragma[nomagic]
-private predicate flowConsCandFwd(
- Content f, AccessPathFront apf, AccessPath ap, Configuration config
+private predicate flowFwdConsCand(
+ TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
) {
- exists(NodeExt n |
- flowFwd(n, _, apf, ap, config) and
- flowFwdStore1(n, f, _, apf, _, config)
+ exists(Node n |
+ flowFwd(n, _, _, apf, ap, config) and
+ flowFwdStore0(n, tc, _, apf, _, config)
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdIn(
+ DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ flowFwd(arg, fromArg, argAp, apf, ap, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
+ flowCand(p, _, _, _, unbind(config))
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ flowFwd(ret, fromArg, argAp, apf, ap, config) and
+ flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
+ flowCand(node, _, _, _, unbind(config))
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdOutFromArg(
+ DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
+) {
+ flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `flowFwd`.
+ */
+pragma[nomagic]
+private predicate flowFwdIsEntered(
+ DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
+) {
+ exists(ParameterNode p, AccessPathFront apf |
+ flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and
+ flowCand(p, true, TAccessPathFrontSome(_), apf, config)
)
}
/**
- * Holds if data can flow from a source to `node` with the given `ap` and
- * from there flow to a sink.
+ * Holds if `node` with access path `ap` is part of a path from a source to
+ * a sink in the configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnAp`
+ * records the access path of the returned value.
*/
-private predicate flow(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flow0(node, toReturn, ap, config) and
- flowFwd(node, _, _, ap, config)
+private predicate flow(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flow0(node, toReturn, returnAp, ap, config) and
+ flowFwd(node, _, _, _, ap, config)
}
-private predicate flow0(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flowFwd(node, _, _, ap, config) and
- config.isSink(node.getNode()) and
+private predicate flow0(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flowFwd(node, _, _, _, ap, config) and
+ config.isSink(node) and
toReturn = false and
+ returnAp = TAccessPathNone() and
ap instanceof AccessPathNil
or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flow(mid, toReturn, ap, config)
+ exists(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flow(mid, toReturn, returnAp, ap, config)
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(node, _, _, ap, config) and
- localFlowBigStepExt(node, mid, false, _, config) and
- flow(mid, toReturn, nil, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(node, _, _, _, ap, config) and
+ localFlowBigStep(node, mid, false, _, config, _) and
+ flow(mid, toReturn, returnAp, nil, config) and
ap instanceof AccessPathNil
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flow(mid, _, ap, config) and
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flow(mid, _, _, ap, config) and
+ toReturn = false and
+ returnAp = TAccessPathNone()
+ )
+ or
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(node, _, _, _, ap, config) and
+ additionalJumpStep(node, mid, config) and
+ flow(mid, _, _, nil, config) and
+ toReturn = false and
+ returnAp = TAccessPathNone() and
+ ap instanceof AccessPathNil
+ )
+ or
+ // store
+ exists(TypedContent tc |
+ flowStore(tc, node, toReturn, returnAp, ap, config) and
+ flowConsCand(tc, ap, config)
+ )
+ or
+ // read
+ exists(Node mid, AccessPath ap0 |
+ readFlowFwd(node, _, mid, ap, ap0, config) and
+ flow(mid, toReturn, returnAp, ap0, config)
+ )
+ or
+ // flow into a callable
+ exists(DataFlowCall call |
+ flowIn(call, node, toReturn, returnAp, ap, config) and
toReturn = false
+ or
+ exists(AccessPath returnAp0 |
+ flowInToReturn(call, node, returnAp0, ap, config) and
+ flowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(node, _, _, ap, config) and
- additionalJumpStepExt(node, mid, config) and
- flow(mid, _, nil, config) and
- toReturn = false and
- ap instanceof AccessPathNil
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flow(mid, false, ap, config) and
- toReturn = false and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flow(mid, _, ap, config) and
- toReturn = true and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- argumentValueFlowsThrough(node, mid) and
- flow(mid, toReturn, ap, config)
- )
- or
- exists(NodeExt mid, AccessPathNil nil |
- argumentFlowsThrough(node, mid, _, config) and
- flow(mid, toReturn, nil, config) and
- ap instanceof AccessPathNil and
- flowFwd(node, _, _, ap, config)
- )
- or
- exists(Content f |
- flowStore(f, node, toReturn, ap, config) and
- flowConsCand(f, ap, config)
- )
- or
- exists(NodeExt mid, AccessPath ap0 |
- readFwd(node, _, mid, ap, ap0, config) and
- flow(mid, toReturn, ap0, config)
- )
+ // flow out of a callable
+ flowOut(_, node, _, _, ap, config) and
+ toReturn = true and
+ if flowFwd(node, true, TAccessPathSome(_), _, ap, config)
+ then returnAp = TAccessPathSome(ap)
+ else returnAp = TAccessPathNone()
}
pragma[nomagic]
-private predicate storeFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate storeFlowFwd(
+ Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- storeExtCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, config) and
- ap0 = push(f, ap)
+ storeCand2(node1, tc, node2, _, config) and
+ flowFwdStore(node2, tc, ap, _, _, _, config) and
+ ap0 = push(tc, ap)
}
pragma[nomagic]
private predicate flowStore(
- Content f, NodeExt node, boolean toReturn, AccessPath ap, Configuration config
+ TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
) {
- exists(NodeExt mid, AccessPath ap0 |
- storeFwd(node, f, mid, ap, ap0, config) and
- flow(mid, toReturn, ap0, config)
+ exists(Node mid, AccessPath ap0 |
+ storeFlowFwd(node, tc, mid, ap, ap0, config) and
+ flow(mid, toReturn, returnAp, ap0, config)
)
}
pragma[nomagic]
-private predicate readFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate readFlowFwd(
+ Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- readExtCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, config) and
- ap0 = pop(f, ap) and
- flowConsCandFwd(f, _, ap0, unbind(config))
+ exists(AccessPathFrontHead apf |
+ readCandFwd(node1, tc, apf, node2, config) and
+ flowFwdRead(node2, apf, ap, _, _, _, config) and
+ ap0 = pop(tc, ap) and
+ flowFwdConsCand(tc, _, ap0, unbind(config))
+ )
}
pragma[nomagic]
-private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
- exists(NodeExt n, NodeExt mid |
- flow(mid, _, ap, config) and
- readFwd(n, f, mid, _, ap, config)
+private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
+ exists(Node n, Node mid |
+ flow(mid, _, _, ap, config) and
+ readFlowFwd(n, tc, mid, _, ap, config)
+ )
+}
+
+pragma[nomagic]
+private predicate flowOut(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(Node out, boolean allowsFieldFlow |
+ flow(out, toReturn, returnAp, ap, config) and
+ flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config)
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowIn(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ flow(p, toReturn, returnAp, ap, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowInToReturn(
+ DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config
+) {
+ flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `flow`.
+ */
+pragma[nomagic]
+private predicate flowIsReturned(
+ DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ flowOut(call, ret, toReturn, returnAp, ap, config) and
+ flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
)
}
bindingset[conf, result]
private Configuration unbind(Configuration conf) { result >= conf and result <= conf }
-private predicate flow(Node n, Configuration config) { flow(TNormalNode(n), _, _, config) }
+private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) }
+
+pragma[noinline]
+private predicate parameterFlow(
+ ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config
+) {
+ flow(p, true, _, ap, config) and
+ c = p.getEnclosingCallable()
+}
private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParameterNode p, AccessPath ap) {
- exists(ReturnNodeExt ret, Configuration config | flow(TNormalNode(p), true, ap, config) |
- exists(Summary summary |
- parameterFlowReturn(p, ret, _, _, _, summary, config) and
- flow(ret, unbind(config))
- |
- // taint through
- summary = TSummaryTaint() and
- ap instanceof AccessPathNil
- or
- // taint setter
- summary = TSummaryTaintStore(_) and
- ap instanceof AccessPathNil
- or
- // taint getter
- summary = TSummaryReadTaint(ap.(AccessPathConsNil).getHead())
- )
- or
- exists(ContentOption contentIn |
- parameterValueFlowReturn(p, ret, _, contentIn, _) and
- flow(ret, unbind(config))
- |
- // value through/setter
- contentIn = TContentNone()
- or
- // value getter (+ setter)
- contentIn = TContentSome(ap.getHead())
- )
+ exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 |
+ parameterFlow(p, ap, ret.getEnclosingCallable(), config) and
+ flow(ret, true, TAccessPathSome(_), ap0, config) and
+ flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config)
)
}
@@ -2113,7 +2071,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, cc, sc, ap) and
config = mid.getConfiguration() and
- flow(TNormalNode(node), _, ap, unbind(config))
+ flow(node, _, _, ap, unbind(config))
)
} or
TPathNodeSink(Node node, Configuration config) {
@@ -2164,14 +2122,31 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
+ private predicate isHidden() {
+ nodeIsHidden(this.getNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ }
+
+ private PathNode getASuccessorIfHidden() {
+ this.isHidden() and
+ result = this.(PathNodeImpl).getASuccessorImpl()
+ }
+
/** Gets a successor of this node, if any. */
- PathNode getASuccessor() { none() }
+ final PathNode getASuccessor() {
+ result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
+ not this.isHidden() and
+ not result.isHidden()
+ }
/** Holds if this node is a source. */
predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
+ abstract PathNode getASuccessorImpl();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2246,7 +2221,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
result.getConfiguration() = unbind(this.getConfiguration())
}
- override PathNodeImpl getASuccessor() {
+ override PathNodeImpl getASuccessorImpl() {
// an intermediate step to another intermediate node
result = getSuccMid()
or
@@ -2283,7 +2258,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNode getASuccessor() { none() }
+ override PathNode getASuccessorImpl() { none() }
override predicate isSource() { config.isSource(node) }
}
@@ -2293,17 +2268,18 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
* a callable is recorded by `cc`.
*/
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
- exists(
- AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
- LocalCallContext localCC
- |
- pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
- localCC = getLocalCallContext(cc, enclosing)
+ exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
+ midnode = mid.getNode() and
+ conf = mid.getConfiguration() and
+ cc = mid.getCallContext() and
+ sc = mid.getSummaryCtx() and
+ localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
ap = ap0
or
- localFlowBigStep(midnode, node, false, ap.(AccessPathNil).getType(), conf, localCC) and
+ localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -2318,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
mid.getAp() instanceof AccessPathNil and
ap = TNil(getErasedNodeTypeBound(node))
or
- exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
+ exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2332,44 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
}
pragma[nomagic]
-private predicate pathIntoLocalStep(
- PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
- AccessPath ap0, Configuration conf
-) {
- midnode = mid.getNode() and
- cc = mid.getCallContext() and
- conf = mid.getConfiguration() and
- localFlowBigStep(midnode, _, _, _, conf, _) and
- enclosing = midnode.getEnclosingCallable() and
- sc = mid.getSummaryCtx() and
- ap0 = mid.getAp()
-}
-
-pragma[nomagic]
-private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
- readDirect(node1, f, node2) and
+private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ readCandFwd(node1, tc, _, node2, config) and
flow(node2, config)
}
pragma[nomagic]
-private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
+private predicate pathReadStep(
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+) {
ap0 = mid.getAp() and
- readCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
-private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- storeDirect(node1, f, node2) and
+private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ storeCand2(node1, tc, node2, _, config) and
flow(node2, config)
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
@@ -2399,11 +2363,11 @@ private predicate pathOutOfCallable1(
}
pragma[noinline]
-private Node getAnOutNodeCand(
+private Node getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config
) {
result = kind.getAnOutNode(call) and
- flow(TNormalNode(result), _, ap, config)
+ flow(result, _, _, ap, config)
}
/**
@@ -2415,7 +2379,7 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config |
pathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = getAnOutNodeCand(kind, call, ap, config)
+ out = getAnOutNodeFlow(kind, call, ap, config)
)
}
@@ -2439,7 +2403,7 @@ private predicate parameterCand(
DataFlowCallable callable, int i, AccessPath ap, Configuration config
) {
exists(ParameterNode p |
- flow(TNormalNode(p), _, ap, config) and
+ flow(p, _, _, ap, config) and
p.isParameterOf(callable, i)
)
}
@@ -2514,7 +2478,7 @@ pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
exists(DataFlowCall call, ReturnKindExt kind |
pathThroughCallable0(call, mid, kind, cc, ap) and
- out = getAnOutNodeCand(kind, call, ap, mid.getConfiguration())
+ out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration())
)
}
@@ -2555,10 +2519,7 @@ private module FlowExploration {
viableParamArg(_, node2, node1)
or
// flow out of a callable
- exists(DataFlowCall call, ReturnKindExt kind |
- getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = kind.getAnOutNode(call)
- )
+ viableReturnPosOut(_, getReturnPosition(node1), node2)
|
c1 = node1.getEnclosingCallable() and
c2 = node2.getEnclosingCallable() and
@@ -2603,10 +2564,10 @@ private module FlowExploration {
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
- TPartialCons(Content f, int len) { len in [1 .. 5] }
+ TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
* element of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -2615,7 +2576,7 @@ private module FlowExploration {
private class PartialAccessPath extends TPartialAccessPath {
abstract string toString();
- Content getHead() { this = TPartialCons(result, _) }
+ TypedContent getHead() { this = TPartialCons(result, _) }
int len() {
this = TPartialNil(_) and result = 0
@@ -2626,7 +2587,7 @@ private module FlowExploration {
DataFlowType getType() {
this = TPartialNil(result)
or
- exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
+ exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
@@ -2644,15 +2605,15 @@ private module FlowExploration {
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
override string toString() {
- exists(Content f, int len | this = TPartialCons(f, len) |
+ exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
if len = 1
- then result = "[" + f.toString() + "]"
- else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc.toString() + "]"
+ else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
override AccessPathFront getFront() {
- exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
+ exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
}
}
@@ -2828,11 +2789,12 @@ private module FlowExploration {
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
- exists(PartialAccessPath ap0, Content f |
- partialPathReadStep(mid, ap0, f, node, cc, config) and
+ exists(PartialAccessPath ap0, TypedContent tc |
+ partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, f, ap0, config)
+ apConsFwd(ap, tc, ap0, config) and
+ compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -2851,35 +2813,42 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
+ PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
+ PartialAccessPath ap2
) {
- ap1 = mid.getAp() and
- storeDirect(mid.getNode(), f, node) and
- ap2.getHead() = f and
- ap2.len() = unbindInt(ap1.len() + 1) and
- compatibleTypes(ap1.getType(), f.getType())
+ exists(Node midNode, DataFlowType contentType |
+ midNode = mid.getNode() and
+ ap1 = mid.getAp() and
+ store(midNode, tc, node, contentType) and
+ ap2.getHead() = tc and
+ ap2.len() = unbindInt(ap1.len() + 1) and
+ compatibleTypes(ap1.getType(), contentType)
+ )
}
pragma[nomagic]
private predicate apConsFwd(
- PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
+ PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
- partialPathStoreStep(mid, ap1, f, _, ap2) and
+ partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
+ PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
- ap = mid.getAp() and
- readStep(mid.getNode(), f, node) and
- ap.getHead() = f and
- config = mid.getConfiguration() and
- cc = mid.getCallContext()
+ exists(Node midNode |
+ midNode = mid.getNode() and
+ ap = mid.getAp() and
+ read(midNode, tc.getContent(), node) and
+ ap.getHead() = tc and
+ config = mid.getConfiguration() and
+ cc = mid.getCallContext()
+ )
}
private predicate partialPathOutOfCallable0(
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
index 1c6ab9a8c46..617559e195c 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
@@ -148,12 +148,6 @@ class Content extends TContent {
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
}
-
- /** Gets the type of the object containing this content. */
- abstract Type getContainerType();
-
- /** Gets the type of this content. */
- abstract Type getType();
}
private class FieldContent extends Content, TFieldContent {
@@ -168,26 +162,14 @@ private class FieldContent extends Content, TFieldContent {
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
f.getLocation().hasLocationInfo(path, sl, sc, el, ec)
}
-
- override Type getContainerType() { result = f.getDeclaringType() }
-
- override Type getType() { result = f.getType() }
}
private class CollectionContent extends Content, TCollectionContent {
override string toString() { result = "collection" }
-
- override Type getContainerType() { none() }
-
- override Type getType() { none() }
}
private class ArrayContent extends Content, TArrayContent {
override string toString() { result = "array" }
-
- override Type getContainerType() { none() }
-
- override Type getType() { none() }
}
/**
@@ -311,9 +293,9 @@ predicate isImmutableOrUnobservable(Node n) {
or
dt.getBaseType() instanceof RoutineType
)
- or
- // Isn't something we can track
- n.asExpr() instanceof Call
// The above list of cases isn't exhaustive, but it narrows down the
// consistency alerts enough that most of them are interesting.
}
+
+/** Holds if `n` should be hidden from path explanations. */
+predicate nodeIsHidden(Node n) { none() }
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
index 700087871cc..47da6ca6e54 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
@@ -20,10 +20,12 @@ private newtype TNode =
TInstanceParameterNode(MemberFunction f) { exists(f.getBlock()) and not f.isStatic() } or
TPreConstructorInitThis(ConstructorFieldInit cfi) or
TPostConstructorInitThis(ConstructorFieldInit cfi) or
- TThisArgumentPostUpdate(ThisExpr ta) {
- exists(Call c, int i |
- ta = c.getArgument(i) and
- not c.getTarget().getParameter(i).getUnderlyingType().(PointerType).getBaseType().isConst()
+ TInnerPartialDefinitionNode(Expr e) {
+ exists(PartialDefinition def, Expr outer |
+ def.definesExpressions(e, outer) and
+ // This condition ensures that we don't get two post-update nodes sharing
+ // the same pre-update node.
+ e != outer
)
} or
TUninitializedNode(LocalVariable v) { not v.hasInitializer() } or
@@ -43,7 +45,7 @@ class Node extends TNode {
/**
* INTERNAL: Do not use. Alternative name for `getFunction`.
*/
- Function getEnclosingCallable() { result = this.getFunction() }
+ final Function getEnclosingCallable() { result = unique(Function f | f = this.getFunction() | f) }
/** Gets the type of this node. */
Type getType() { none() } // overridden in subclasses
@@ -66,7 +68,7 @@ class Node extends TNode {
* a partial definition of `&x`).
*/
Expr asPartialDefinition() {
- result = this.(PartialDefinitionNode).getPartialDefinition().getDefinedExpr()
+ this.(PartialDefinitionNode).getPartialDefinition().definesExpressions(_, result)
}
/**
@@ -114,33 +116,12 @@ class ExprNode extends Node, TExprNode {
override string toString() { result = expr.toString() }
- override Location getLocation() {
- result = getExprLocationOverride(expr)
- or
- not exists(getExprLocationOverride(expr)) and
- result = expr.getLocation()
- }
+ override Location getLocation() { result = expr.getLocation() }
/** Gets the expression corresponding to this node. */
Expr getExpr() { result = expr }
}
-/**
- * Gets a location for `e` that's more accurate than `e.getLocation()`, if any.
- */
-private Location getExprLocationOverride(Expr e) {
- // Base case: the parent has a better location than `e`.
- e.getLocation() instanceof UnknownExprLocation and
- result = e.getParent().getLocation() and
- not result instanceof UnknownLocation
- or
- // Recursive case: the parent has a location override that's better than what
- // `e` has.
- e.getLocation() instanceof UnknownExprLocation and
- result = getExprLocationOverride(e.getParent()) and
- not result instanceof UnknownLocation
-}
-
abstract class ParameterNode extends Node, TNode {
/**
* Holds if this node is the parameter of `c` at the specified (zero-based)
@@ -198,25 +179,23 @@ class ImplicitParameterNode extends ParameterNode, TInstanceParameterNode {
* returned. This node will have its `getArgument()` equal to `&x`.
*/
class DefinitionByReferenceNode extends PartialDefinitionNode {
- VariableAccess va;
+ Expr inner;
Expr argument;
DefinitionByReferenceNode() {
- exists(DefinitionByReference def |
- def = this.getPartialDefinition() and
- argument = def.getDefinedExpr() and
- va = def.getVariableAccess()
- )
+ this.getPartialDefinition().(DefinitionByReference).definesExpressions(inner, argument)
}
- override Function getFunction() { result = va.getEnclosingFunction() }
+ override Function getFunction() { result = inner.getEnclosingFunction() }
- override Type getType() { result = va.getType() }
+ override Type getType() { result = inner.getType() }
override string toString() { result = "ref arg " + argument.toString() }
override Location getLocation() { result = argument.getLocation() }
+ override ExprNode getPreUpdateNode() { result.getExpr() = argument }
+
/** Gets the argument corresponding to this node. */
Expr getArgument() { result = argument }
@@ -297,23 +276,32 @@ private class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNo
PartialDefinitionNode() { this = TPartialDefinitionNode(pd) }
- override Node getPreUpdateNode() { result.asExpr() = pd.getDefinedExpr() }
+ override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) }
- override Location getLocation() { result = pd.getLocation() }
+ override Location getLocation() { result = pd.getActualLocation() }
PartialDefinition getPartialDefinition() { result = pd }
override string toString() { result = getPreUpdateNode().toString() + " [post update]" }
}
-private class ThisArgumentPostUpdateNode extends PostUpdateNode, TThisArgumentPostUpdate {
- ThisExpr thisExpr;
+/**
+ * A post-update node on the `e->f` in `f(&e->f)` (and other forms).
+ */
+private class InnerPartialDefinitionNode extends TInnerPartialDefinitionNode, PostUpdateNode {
+ Expr e;
- ThisArgumentPostUpdateNode() { this = TThisArgumentPostUpdate(thisExpr) }
+ InnerPartialDefinitionNode() { this = TInnerPartialDefinitionNode(e) }
- override Node getPreUpdateNode() { result.asExpr() = thisExpr }
+ override ExprNode getPreUpdateNode() { result.getExpr() = e }
- override string toString() { result = "ref arg this" }
+ override Function getFunction() { result = e.getEnclosingFunction() }
+
+ override Type getType() { result = e.getType() }
+
+ override string toString() { result = e.toString() + " [inner post update]" }
+
+ override Location getLocation() { result = e.getLocation() }
}
/**
@@ -496,8 +484,6 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
// Expr -> Expr
exprToExprStep_nocfg(nodeFrom.asExpr(), nodeTo.asExpr())
or
- exprToExprStep_nocfg(nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr(), nodeTo.asExpr())
- or
// Node -> FlowVar -> VariableAccess
exists(FlowVar var |
(
@@ -520,6 +506,14 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
or
// post-update-`this` -> following-`this`-ref
ThisFlow::adjacentThisRefs(nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo)
+ or
+ // In `f(&x->a)`, this step provides the flow from post-`&` to post-`x->a`,
+ // from which there is field flow to `x` via reverse read.
+ exists(PartialDefinition def, Expr inner, Expr outer |
+ def.definesExpressions(inner, outer) and
+ inner = nodeTo.(InnerPartialDefinitionNode).getPreUpdateNode().asExpr() and
+ outer = nodeFrom.(PartialDefinitionNode).getPreUpdateNode().asExpr()
+ )
}
/**
@@ -657,7 +651,7 @@ private module FieldFlow {
exists(FieldConfiguration cfg | cfg.hasFlow(node1, node2)) and
// This configuration should not be able to cross function boundaries, but
// we double-check here just to be sure.
- node1.getFunction() = node2.getFunction()
+ node1.getEnclosingCallable() = node2.getEnclosingCallable()
}
}
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 2a2755ac1d0..72033b0f72d 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
@@ -5,6 +5,7 @@
import cpp
private import semmle.code.cpp.controlflow.SSA
private import semmle.code.cpp.dataflow.internal.SubBasicBlocks
+private import semmle.code.cpp.dataflow.internal.AddressFlow
/**
* A conceptual variable that is assigned only once, like an SSA variable. This
@@ -56,15 +57,9 @@ class FlowVar extends TFlowVar {
abstract predicate definedByExpr(Expr e, ControlFlowNode node);
/**
- * Holds if this `FlowVar` corresponds to the data written by a call that
- * passes a variable as argument `arg`.
- */
- cached
- abstract predicate definedByReference(Expr arg);
-
- /**
- * Holds if this `FlowVar` is a `PartialDefinition` whose defined expression
- * is `e`.
+ * Holds if this `FlowVar` is a `PartialDefinition` whose outer defined
+ * expression is `e`. For example, in `f(&x)`, the outer defined expression
+ * is `&x`.
*/
cached
abstract predicate definedPartiallyAt(Expr e);
@@ -113,44 +108,21 @@ class FlowVar extends TFlowVar {
* ```
*/
private module PartialDefinitions {
- private newtype TPartialDefinition =
- TExplicitFieldStoreQualifier(Expr qualifier, ControlFlowNode node) {
- exists(FieldAccess fa | qualifier = fa.getQualifier() |
- isInstanceFieldWrite(fa, node)
- or
- exists(PartialDefinition pd |
- node = pd.getSubBasicBlockStart() and
- fa = pd.getDefinedExpr()
- )
- )
- } or
- TExplicitCallQualifier(Expr qualifier) {
- exists(Call call |
- qualifier = call.getQualifier() and
- not call.getTarget().hasSpecifier("const")
- )
- } or
- TReferenceArgument(Expr arg, VariableAccess va) { referenceArgument(va, arg) }
-
- private predicate isInstanceFieldWrite(FieldAccess fa, ControlFlowNode node) {
- assignmentLikeOperation(node, _, fa, _)
- }
-
- class PartialDefinition extends TPartialDefinition {
- Expr definedExpr;
+ class PartialDefinition extends Expr {
+ Expr innerDefinedExpr;
ControlFlowNode node;
PartialDefinition() {
- this = TExplicitFieldStoreQualifier(definedExpr, node)
- or
- this = TExplicitCallQualifier(definedExpr) and node = definedExpr
- or
- this = TReferenceArgument(definedExpr, node)
+ exists(Expr convertedInner |
+ valueToUpdate(convertedInner, this.getFullyConverted(), node) and
+ innerDefinedExpr = convertedInner.getUnconverted() and
+ not this instanceof Conversion
+ )
}
- predicate partiallyDefines(Variable v) { definedExpr = v.getAnAccess() }
+ predicate partiallyDefines(Variable v) { innerDefinedExpr = v.getAnAccess() }
- predicate partiallyDefinesThis(ThisExpr e) { definedExpr = e }
+ predicate partiallyDefinesThis(ThisExpr e) { innerDefinedExpr = e }
/**
* Gets the subBasicBlock where this `PartialDefinition` is defined.
@@ -158,69 +130,37 @@ private module PartialDefinitions {
ControlFlowNode getSubBasicBlockStart() { result = node }
/**
- * Gets the expression that is being partially defined. For example in the
- * following code:
- * ```
- * x.y = 1;
- * ```
- * The expression `x` is being partially defined.
+ * Holds if this partial definition may modify `inner` (or what it points
+ * to) through `outer`. These expressions will never be `Conversion`s.
+ *
+ * For example, in `f(& (*a).x)`, there are two results:
+ * - `inner` = `... .x`, `outer` = `&...`
+ * - `inner` = `a`, `outer` = `*`
*/
- Expr getDefinedExpr() { result = definedExpr }
-
- Location getLocation() {
- not exists(definedExpr.getLocation()) and result = definedExpr.getParent().getLocation()
- or
- definedExpr.getLocation() instanceof UnknownLocation and
- result = definedExpr.getParent().getLocation()
- or
- result = definedExpr.getLocation() and not result instanceof UnknownLocation
+ predicate definesExpressions(Expr inner, Expr outer) {
+ inner = innerDefinedExpr and
+ outer = this
}
- string toString() { result = "partial def of " + definedExpr }
+ /**
+ * Gets the location of this element, adjusted to avoid unknown locations
+ * on compiler-generated `ThisExpr`s.
+ */
+ Location getActualLocation() {
+ not exists(this.getLocation()) and result = this.getParent().getLocation()
+ or
+ this.getLocation() instanceof UnknownLocation and
+ result = this.getParent().getLocation()
+ or
+ result = this.getLocation() and not result instanceof UnknownLocation
+ }
}
/**
* A partial definition that's a definition by reference.
*/
- class DefinitionByReference extends PartialDefinition, TReferenceArgument {
- VariableAccess va;
-
- DefinitionByReference() {
- // `this` is not restricted in this charpred. That's because the full
- // extent of this class includes the charpred of the superclass, which
- // relates `this` to `definedExpr`, and `va` is functionally determined
- // by `definedExpr`.
- referenceArgument(va, definedExpr)
- }
-
- VariableAccess getVariableAccess() { result = va }
-
- override predicate partiallyDefines(Variable v) { va = v.getAnAccess() }
- }
-
- private predicate referenceArgument(VariableAccess va, Expr argument) {
- argument = any(Call c).getAnArgument() and
- exists(Type argumentType |
- argumentType = argument.getFullyConverted().getType().stripTopLevelSpecifiers()
- |
- argumentType instanceof ReferenceType and
- not argumentType.(ReferenceType).getBaseType().isConst() and
- va = argument
- or
- argumentType instanceof PointerType and
- not argumentType.(PointerType).getBaseType().isConst() and
- (
- // f(variable)
- va = argument
- or
- // f(&variable)
- va = argument.(AddressOfExpr).getOperand()
- or
- // f(&array[0])
- va.getType().getUnspecifiedType() instanceof ArrayType and
- va = argument.(AddressOfExpr).getOperand().(ArrayExpr).getArrayBase()
- )
- )
+ class DefinitionByReference extends PartialDefinition {
+ DefinitionByReference() { exists(Call c | this = c.getAnArgument() or this = c.getQualifier()) }
}
}
@@ -337,10 +277,6 @@ module FlowVar_internal {
)
}
- override predicate definedByReference(Expr arg) {
- none() // Not supported for SSA. See `fullySupportedSsaVariable`.
- }
-
override predicate definedPartiallyAt(Expr e) { none() }
override predicate definedByInitialValue(StackVariable param) {
@@ -425,19 +361,11 @@ module FlowVar_internal {
node = sbb.getANode()
}
- override predicate definedByReference(Expr arg) {
- exists(DefinitionByReference def |
- def.partiallyDefines(v) and
- sbb = def.getSubBasicBlockStart() and
- arg = def.getDefinedExpr()
- )
- }
-
override predicate definedPartiallyAt(Expr e) {
exists(PartialDefinition p |
p.partiallyDefines(v) and
sbb = p.getSubBasicBlockStart() and
- e = p.getDefinedExpr()
+ p.definesExpressions(_, e)
)
}
@@ -450,11 +378,6 @@ module FlowVar_internal {
this.definedByInitialValue(_) and
result = "initial value of " + v
or
- exists(Expr arg |
- this.definedByReference(arg) and
- result = "definition by reference of " + v
- )
- or
exists(Expr partialDef |
this.definedPartiallyAt(partialDef) and
result = "partial definition at " + partialDef
@@ -463,7 +386,6 @@ module FlowVar_internal {
// impossible case
not this.definedByExpr(_, _) and
not this.definedByInitialValue(_) and
- not this.definedByReference(_) and
not this.definedPartiallyAt(_) and
result = "undefined " + v
}
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll
index dafcd1fdd97..fa9d2e94081 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll
@@ -168,6 +168,7 @@ class SubBasicBlock extends ControlFlowNodeBase {
/** Gets the first control-flow node in this `SubBasicBlock`. */
ControlFlowNode getStart() { result = this }
+ /** Gets the function that contains this `SubBasicBlock`. */
pragma[noinline]
Function getEnclosingFunction() { result = this.getStart().getControlFlowScope() }
}
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
index e8b828f5b3e..0f0607662e9 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
@@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration {
defaultTaintBarrier(node)
}
- /** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */
- deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() }
-
- deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
- isSanitizerEdge(node1, node2)
- }
-
/** Holds if data flow into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
index e8b828f5b3e..0f0607662e9 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
@@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration {
defaultTaintBarrier(node)
}
- /** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */
- deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() }
-
- deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
- isSanitizerEdge(node1, node2)
- }
-
/** Holds if data flow into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Access.qll b/cpp/ql/src/semmle/code/cpp/exprs/Access.qll
index f68eaef5d8b..fe467be6fe4 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/Access.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/Access.qll
@@ -6,9 +6,13 @@ private import semmle.code.cpp.dataflow.EscapesTree
/**
* A C/C++ access expression. This refers to a function, variable, or enum constant.
*/
-abstract class Access extends Expr, NameQualifiableElement {
+class Access extends Expr, NameQualifiableElement, @access {
+ // As `@access` is a union type containing `@routineexpr` (which describes function accesses
+ // that are called), we need to exclude function calls.
+ Access() { this instanceof @routineexpr implies not iscall(underlyingElement(this), _) }
+
/** Gets the accessed function, variable, or enum constant. */
- abstract Declaration getTarget();
+ Declaration getTarget() { none() } // overridden in subclasses
override predicate mayBeImpure() { none() }
@@ -338,7 +342,7 @@ class PointerToFieldLiteral extends ImplicitThisFieldAccess {
* int myFunctionTarget(int);
*
* void myFunction() {
- * int (*myFunctionPointer)(int) = &myTarget;
+ * int (*myFunctionPointer)(int) = &myFunctionTarget;
* }
* ```
*/
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll b/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll
index 446bdbdee51..5729a49086b 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll
@@ -4,7 +4,7 @@ import semmle.code.cpp.exprs.Expr
* A C/C++ builtin operation. This is the root QL class encompassing
* built-in functionality.
*/
-abstract class BuiltInOperation extends Expr {
+class BuiltInOperation extends Expr, @builtin_op {
override string getCanonicalQLClass() { result = "BuiltInOperation" }
}
@@ -12,17 +12,7 @@ abstract class BuiltInOperation extends Expr {
* A C/C++ built-in operation that is used to support functions with variable numbers of arguments.
* This includes `va_start`, `va_end`, `va_copy`, and `va_arg`.
*/
-class VarArgsExpr extends BuiltInOperation {
- VarArgsExpr() {
- this instanceof BuiltInVarArgsStart
- or
- this instanceof BuiltInVarArgsEnd
- or
- this instanceof BuiltInVarArg
- or
- this instanceof BuiltInVarArgCopy
- }
-}
+class VarArgsExpr extends BuiltInOperation, @var_args_expr { }
/**
* A C/C++ `__builtin_va_start` built-in operation (used by some
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll
index 6eb4ed52319..abb26002091 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll
@@ -9,9 +9,8 @@ private import semmle.code.cpp.dataflow.EscapesTree
*/
abstract class Call extends Expr, NameQualifiableElement {
/**
- * Gets the number of actual parameters in this call; use
- * `getArgument(i)` with `i` between `0` and `result - 1` to
- * retrieve actuals.
+ * Gets the number of arguments (actual parameters) of this call. The count
+ * does _not_ include the qualifier of the call, if any.
*/
int getNumberOfArguments() { result = count(this.getAnArgument()) }
@@ -32,21 +31,24 @@ abstract class Call extends Expr, NameQualifiableElement {
Expr getQualifier() { result = this.getChild(-1) }
/**
- * Gets an argument for this call.
+ * Gets an argument for this call. To get the qualifier of this call, if
+ * any, use `getQualifier()`.
*/
Expr getAnArgument() { exists(int i | result = this.getChild(i) and i >= 0) }
/**
* Gets the nth argument for this call.
*
- * The range of `n` is from `0` to `getNumberOfArguments() - 1`.
+ * The range of `n` is from `0` to `getNumberOfArguments() - 1`. To get the
+ * qualifier of this call, if any, use `getQualifier()`.
*/
Expr getArgument(int n) { result = this.getChild(n) and n >= 0 }
/**
- * Gets a sub expression of the argument at position `index`. If the
+ * Gets a subexpression of the argument at position `index`. If the
* argument itself contains calls, such calls will be considered
- * leafs in the expression tree.
+ * leaves in the expression tree. The qualifier of the call, if any, is not
+ * considered to be an argument.
*
* Example: the call `f(2, 3 + 4, g(4 + 5))` has sub expression(s)
* `2` at index 0; `3`, `4`, and `3 + 4` at index 1; and `g(4 + 5)`
@@ -80,7 +82,8 @@ abstract class Call extends Expr, NameQualifiableElement {
/**
* Holds if this call passes the variable accessed by `va` by
- * reference as the `i`th argument.
+ * reference as the `i`th argument. The qualifier of a call to a member
+ * function is `i = -1`.
*
* A variable is passed by reference if the `i`th parameter of the function
* receives an address that points within the object denoted by `va`. For a
@@ -99,11 +102,15 @@ abstract class Call extends Expr, NameQualifiableElement {
*/
predicate passesByReference(int i, VariableAccess va) {
variableAddressEscapesTree(va, this.getArgument(i).getFullyConverted())
+ or
+ variableAddressEscapesTree(va, this.getQualifier().getFullyConverted()) and
+ i = -1
}
/**
* Holds if this call passes the variable accessed by `va` by
- * reference to non-const data as the `i`th argument.
+ * reference to non-const data as the `i`th argument. The qualifier of a
+ * call to a member function is `i = -1`.
*
* A variable is passed by reference if the `i`th parameter of the function
* receives an address that points within the object denoted by `va`. For a
@@ -122,6 +129,9 @@ abstract class Call extends Expr, NameQualifiableElement {
*/
predicate passesByReferenceNonConst(int i, VariableAccess va) {
variableAddressEscapesTreeNonConst(va, this.getArgument(i).getFullyConverted())
+ or
+ variableAddressEscapesTreeNonConst(va, this.getQualifier().getFullyConverted()) and
+ i = -1
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll b/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll
index 3a45d8597d5..a00014b9af7 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll
@@ -7,7 +7,7 @@ private import semmle.code.cpp.internal.ResolveClass
* Instances of this class are not present in the main AST which is navigated by parent/child links. Instead,
* instances of this class are attached to nodes in the main AST via special conversion links.
*/
-abstract class Conversion extends Expr {
+class Conversion extends Expr, @conversion {
/** Gets the expression being converted. */
Expr getExpr() { result.getConversion() = this }
@@ -34,10 +34,10 @@ abstract class Conversion extends Expr {
* 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
+ * This is a 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 {
+class Cast extends Conversion, @cast {
/**
* Gets a string describing the semantic conversion operation being performed by
* this cast.
@@ -48,10 +48,13 @@ abstract class Cast extends Conversion, @cast {
/**
* INTERNAL: Do not use.
* Query predicates used to check invariants that should hold for all `Cast`
- * nodes. To run all sanity queries for the ASTs, including the ones below,
- * run "semmle/code/cpp/ASTSanity.ql".
+ * nodes. To run all consistency queries for the ASTs, including the ones below,
+ * run "semmle/code/cpp/ASTConsistency.ql".
*/
-module CastSanity {
+module CastConsistency {
+ /**
+ * Holds if the cast has more than one result for `Cast.getSemanticConversionString()`.
+ */
query predicate multipleSemanticConversionStrings(Cast cast, Type fromType, string kind) {
// Every cast should have exactly one semantic conversion kind
count(cast.getSemanticConversionString()) > 1 and
@@ -59,12 +62,19 @@ module CastSanity {
fromType = cast.getExpr().getUnspecifiedType()
}
+ /**
+ * Holds if the cast has no result for `Cast.getSemanticConversionString()`.
+ */
query predicate missingSemanticConversionString(Cast cast, Type fromType) {
// Every cast should have exactly one semantic conversion kind
not exists(cast.getSemanticConversionString()) and
fromType = cast.getExpr().getUnspecifiedType()
}
+ /**
+ * Holds if the cast has a result for `Cast.getSemanticConversionString()` that indicates that the
+ * kind of its semantic conversion is not known.
+ */
query predicate unknownSemanticConversionString(Cast cast, Type fromType) {
// Every cast should have a known semantic conversion kind
cast.getSemanticConversionString() = "unknown conversion" and
@@ -699,7 +709,7 @@ class SizeofPackOperator extends Expr, @sizeof_pack {
/**
* A C/C++ sizeof expression.
*/
-abstract class SizeofOperator extends Expr, @runtime_sizeof {
+class SizeofOperator extends Expr, @runtime_sizeof {
override int getPrecedence() { result = 16 }
}
@@ -762,7 +772,7 @@ class SizeofTypeOperator extends SizeofOperator {
/**
* A C++11 `alignof` expression.
*/
-abstract class AlignofOperator extends Expr, @runtime_alignof {
+class AlignofOperator extends Expr, @runtime_alignof {
override int getPrecedence() { result = 16 }
}
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll
index 97371919b02..fd15a22fbd2 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll
@@ -53,7 +53,32 @@ class Expr extends StmtParent, @expr {
Element getParent() { exprparents(underlyingElement(this), _, unresolveElement(result)) }
/** Gets the location of this expression. */
- override Location getLocation() { exprs(underlyingElement(this), _, result) }
+ override Location getLocation() {
+ result = this.getExprLocationOverride()
+ or
+ not exists(this.getExprLocationOverride()) and
+ result = this.getDbLocation()
+ }
+
+ /**
+ * Gets a location for this expression that's more accurate than
+ * `getDbLocation()`, if any.
+ */
+ private Location getExprLocationOverride() {
+ // Base case: the parent has a better location than `this`.
+ this.getDbLocation() instanceof UnknownExprLocation and
+ result = this.getParent().(Expr).getDbLocation() and
+ not result instanceof UnknownLocation
+ or
+ // Recursive case: the parent has a location override that's better than
+ // what `this` has.
+ this.getDbLocation() instanceof UnknownExprLocation and
+ result = this.getParent().(Expr).getExprLocationOverride() and
+ not result instanceof UnknownLocation
+ }
+
+ /** Gets the location of this expressions, raw from the database. */
+ private Location getDbLocation() { exprs(underlyingElement(this), _, result) }
/** Holds if this is an auxiliary expression generated by the compiler. */
predicate isCompilerGenerated() {
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll b/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll
index f634d9239b3..4a5b1b21c8d 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll
@@ -86,7 +86,7 @@ class Closure extends Class {
result.getName() = "operator()"
}
- override string toString() { result = "decltype([...](...){...})" }
+ override string getDescription() { result = "decltype([...](...){...})" }
}
/**
@@ -99,7 +99,7 @@ class Closure extends Class {
* ```
*/
class LambdaCapture extends Locatable, @lambdacapture {
- override string toString() { result = getField().toString() }
+ override string toString() { result = getField().getName() }
override string getCanonicalQLClass() { result = "LambdaCapture" }
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll b/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll
index 211b65c809d..3360be330c2 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll
@@ -145,8 +145,6 @@ class HexLiteral extends Literal {
/**
* A C/C++ aggregate literal.
- *
- * For example:
*/
class AggregateLiteral extends Expr, @aggregateliteral {
override string getCanonicalQLClass() { result = "AggregateLiteral" }
diff --git a/cpp/ql/src/semmle/code/cpp/internal/AddressConstantExpression.qll b/cpp/ql/src/semmle/code/cpp/internal/AddressConstantExpression.qll
index 5152e3005f3..436be8384e8 100644
--- a/cpp/ql/src/semmle/code/cpp/internal/AddressConstantExpression.qll
+++ b/cpp/ql/src/semmle/code/cpp/internal/AddressConstantExpression.qll
@@ -1,3 +1,11 @@
+/*
+ * Maintainer note: this file is one of several files that are similar but not
+ * identical. Many changes to this file will also apply to the others:
+ * - AddressConstantExpression.qll
+ * - AddressFlow.qll
+ * - EscapesTree.qll
+ */
+
private import cpp
predicate addressConstantExpression(Expr e) {
diff --git a/cpp/ql/src/semmle/code/cpp/ir/IRConsistency.ql b/cpp/ql/src/semmle/code/cpp/ir/IRConsistency.ql
new file mode 100644
index 00000000000..1a1c2e369cc
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/IRConsistency.ql
@@ -0,0 +1,8 @@
+/**
+ * @name IR Consistency Check
+ * @description Performs consistency checks on the Intermediate Representation. This query should have no results.
+ * @kind table
+ * @id cpp/ir-consistency-check
+ */
+
+import implementation.aliased_ssa.IRConsistency
diff --git a/cpp/ql/src/semmle/code/cpp/ir/IRSanity.ql b/cpp/ql/src/semmle/code/cpp/ir/IRSanity.ql
deleted file mode 100644
index 2c2c6e348a1..00000000000
--- a/cpp/ql/src/semmle/code/cpp/ir/IRSanity.ql
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * @name IR Sanity Check
- * @description Performs sanity checks on the Intermediate Representation. This query should have no results.
- * @kind table
- * @id cpp/ir-sanity-check
- */
-
-import implementation.aliased_ssa.IRSanity
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 dbeefae4880..ddf43651727 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
@@ -1,10 +1,12 @@
import cpp
import semmle.code.cpp.security.Security
private import semmle.code.cpp.ir.dataflow.DataFlow
+private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import semmle.code.cpp.ir.dataflow.DataFlow2
private import semmle.code.cpp.ir.dataflow.DataFlow3
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.internal.DataFlowDispatch as Dispatch
+private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.models.interfaces.Taint
private import semmle.code.cpp.models.interfaces.DataFlow
@@ -31,8 +33,7 @@ private predicate predictableInstruction(Instruction instr) {
* Note that the list itself is not very principled; it consists of all the
* functions listed in the old security library's [default] `isPureFunction`
* that have more than one argument, but are not in the old taint tracking
- * library's `returnArgument` predicate. In addition, `strlen` is included
- * because it's also a special case in flow to return values.
+ * library's `returnArgument` predicate.
*/
predicate predictableOnlyFlow(string name) {
name = "strcasestr" or
@@ -41,7 +42,6 @@ predicate predictableOnlyFlow(string name) {
name = "strchrnul" or
name = "strcmp" or
name = "strcspn" or
- name = "strlen" or // special case
name = "strncmp" or
name = "strndup" or
name = "strnlen" or
@@ -67,6 +67,9 @@ private DataFlow::Node getNodeForSource(Expr source) {
// to `gets`. It's impossible here to tell which is which, but the "access
// to argv" source is definitely not intended to match an output argument,
// and it causes false positives if we let it.
+ //
+ // This case goes together with the similar (but not identical) rule in
+ // `nodeIsBarrierIn`.
result = DataFlow::definitionByReferenceNode(source) and
not argv(source.(VariableAccess).getTarget())
)
@@ -170,16 +173,45 @@ private predicate hasUpperBoundsCheck(Variable var) {
)
}
+private predicate nodeIsBarrierEqualityCandidate(
+ DataFlow::Node node, Operand access, Variable checkedVar
+) {
+ readsVariable(node.asInstruction(), checkedVar) and
+ any(IRGuardCondition guard).ensuresEq(access, _, _, node.asInstruction().getBlock(), true)
+}
+
private predicate nodeIsBarrier(DataFlow::Node node) {
exists(Variable checkedVar |
readsVariable(node.asInstruction(), checkedVar) and
hasUpperBoundsCheck(checkedVar)
)
+ or
+ exists(Variable checkedVar, Operand access |
+ /*
+ * This node is guarded by a condition that forces the accessed variable
+ * to equal something else. For example:
+ * ```
+ * x = taintsource()
+ * if (x == 10) {
+ * taintsink(x); // not considered tainted
+ * }
+ * ```
+ */
+
+ nodeIsBarrierEqualityCandidate(node, access, checkedVar) and
+ readsVariable(access.getDef(), checkedVar)
+ )
}
private predicate nodeIsBarrierIn(DataFlow::Node node) {
// don't use dataflow into taint sources, as this leads to duplicate results.
- node = getNodeForSource(any(Expr e))
+ exists(Expr source | isUserInput(source, _) |
+ node = DataFlow::exprNode(source)
+ or
+ // This case goes together with the similar (but not identical) rule in
+ // `getNodeForSource`.
+ node = DataFlow::definitionByReferenceNode(source)
+ )
}
cached
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 1dd1d9ac4cc..d8904625e0e 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
@@ -77,51 +77,48 @@ private module VirtualDispatch {
// Local flow
DataFlow::localFlowStep(src, other) and
allowFromArg = allowOtherFromArg
- )
- or
- // Flow through global variable
- exists(StoreInstruction store |
- store = src.asInstruction() and
- (
- exists(Variable var |
- var = store.getDestinationAddress().(VariableAddressInstruction).getASTVariable() and
- this.flowsFromGlobal(var)
- )
- or
- exists(Variable var, FieldAccess a |
- var =
- store
- .getDestinationAddress()
- .(FieldAddressInstruction)
- .getObjectAddress()
- .(VariableAddressInstruction)
- .getASTVariable() and
- this.flowsFromGlobalUnionField(var, a)
- )
- ) and
- allowFromArg = true
+ or
+ // Flow from global variable to load.
+ exists(LoadInstruction load, GlobalOrNamespaceVariable var |
+ var = src.asVariable() and
+ other.asInstruction() = load and
+ addressOfGlobal(load.getSourceAddress(), var) and
+ // The `allowFromArg` concept doesn't play a role when `src` is a
+ // global variable, so we just set it to a single arbitrary value for
+ // performance.
+ allowFromArg = true
+ )
+ or
+ // Flow from store to global variable.
+ exists(StoreInstruction store, GlobalOrNamespaceVariable var |
+ var = other.asVariable() and
+ store = src.asInstruction() and
+ storeIntoGlobal(store, var) and
+ // Setting `allowFromArg` to `true` like in the base case means we
+ // treat a store to a global variable like the dispatch itself: flow
+ // may come from anywhere.
+ allowFromArg = true
+ )
)
}
+ }
- private predicate flowsFromGlobal(GlobalOrNamespaceVariable var) {
- exists(LoadInstruction load |
- this.flowsFrom(DataFlow::instructionNode(load), _) and
- load.getSourceAddress().(VariableAddressInstruction).getASTVariable() = var
- )
- }
+ pragma[noinline]
+ private predicate storeIntoGlobal(StoreInstruction store, GlobalOrNamespaceVariable var) {
+ addressOfGlobal(store.getDestinationAddress(), var)
+ }
- private predicate flowsFromGlobalUnionField(Variable var, FieldAccess a) {
- a.getTarget().getDeclaringType() instanceof Union and
- exists(LoadInstruction load |
- this.flowsFrom(DataFlow::instructionNode(load), _) and
- load
- .getSourceAddress()
- .(FieldAddressInstruction)
- .getObjectAddress()
- .(VariableAddressInstruction)
- .getASTVariable() = var
- )
- }
+ /** Holds if `addressInstr` is an instruction that produces the address of `var`. */
+ private predicate addressOfGlobal(Instruction addressInstr, GlobalOrNamespaceVariable var) {
+ // Access directly to the global variable
+ addressInstr.(VariableAddressInstruction).getASTVariable() = var
+ or
+ // Access to a field on a global union
+ exists(FieldAddressInstruction fa |
+ fa = addressInstr and
+ fa.getObjectAddress().(VariableAddressInstruction).getASTVariable() = var and
+ fa.getField().getDeclaringType() instanceof Union
+ )
}
/**
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 a1daeb66411..1aeedf717f7 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
*/
predicate isBarrier(Node node) { none() }
- /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */
- deprecated predicate isBarrierEdge(Node node1, Node node2) { none() }
-
/** Holds if data flow into `node` is prohibited. */
predicate isBarrierIn(Node node) { none() }
@@ -251,15 +248,11 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
-pragma[noinline]
-private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
- viableCallable(call) = result.getCallable() and
- kind = result.getKind()
-}
-
/**
- * Holds if `node` is reachable from a source in the given configuration
- * taking simple call contexts into consideration.
+ * Holds if `node` is reachable from a source in the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call.
*/
private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) {
not fullBarrier(node, config) and
@@ -293,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
exists(Node mid |
useFieldFlow(config) and
nodeCandFwd1(mid, fromArg, config) and
- storeDirect(mid, _, node) and
+ store(mid, _, node, _) and
not outBarrier(mid, config)
)
or
// read
- exists(Content f |
- nodeCandFwd1Read(f, node, fromArg, config) and
- storeCandFwd1(f, config) and
+ exists(Content c |
+ nodeCandFwd1Read(c, node, fromArg, config) and
+ nodeCandFwd1IsStored(c, config) and
not inBarrier(node, config)
)
or
@@ -317,13 +310,35 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
fromArg = false
or
nodeCandFwd1OutFromArg(call, node, config) and
- flowOutCandFwd1(call, fromArg, config)
+ nodeCandFwd1IsEntered(call, fromArg, config)
)
)
}
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
+pragma[nomagic]
+private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
+ exists(Node mid |
+ nodeCandFwd1(mid, fromArg, config) and
+ read(mid, c, node)
+ )
+}
+
+/**
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
+ exists(Node mid, Node node, TypedContent tc |
+ not fullBarrier(node, config) and
+ useFieldFlow(config) and
+ nodeCandFwd1(mid, config) and
+ store(mid, tc, node, _) and
+ c = tc.getContent()
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1ReturnPosition(
ReturnPosition pos, boolean fromArg, Configuration config
@@ -335,43 +350,10 @@ private predicate nodeCandFwd1ReturnPosition(
}
pragma[nomagic]
-private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
- exists(Node mid |
- nodeCandFwd1(mid, fromArg, config) and
- readDirect(mid, f, node)
- )
-}
-
-/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
- */
-pragma[nomagic]
-private predicate storeCandFwd1(Content f, Configuration config) {
- exists(Node mid, Node node |
- not fullBarrier(node, config) and
- useFieldFlow(config) and
- nodeCandFwd1(mid, config) and
- storeDirect(mid, f, node)
- )
-}
-
-pragma[nomagic]
-private predicate nodeCandFwd1ReturnKind(
- DataFlowCall call, ReturnKindExt kind, boolean fromArg, Configuration config
-) {
+private predicate nodeCandFwd1Out(DataFlowCall call, Node out, boolean fromArg, Configuration config) {
exists(ReturnPosition pos |
nodeCandFwd1ReturnPosition(pos, fromArg, config) and
- pos = viableReturnPos(call, kind)
- )
-}
-
-pragma[nomagic]
-private predicate nodeCandFwd1Out(
- DataFlowCall call, Node node, boolean fromArg, Configuration config
-) {
- exists(ReturnKindExt kind |
- nodeCandFwd1ReturnKind(call, kind, fromArg, config) and
- node = kind.getAnOutNode(call)
+ viableReturnPosOut(call, pos, out)
)
}
@@ -384,7 +366,7 @@ private predicate nodeCandFwd1OutFromArg(DataFlowCall call, Node node, Configura
* Holds if an argument to `call` is reached in the flow covered by `nodeCandFwd1`.
*/
pragma[nomagic]
-private predicate flowOutCandFwd1(DataFlowCall call, boolean fromArg, Configuration config) {
+private predicate nodeCandFwd1IsEntered(DataFlowCall call, boolean fromArg, Configuration config) {
exists(ArgumentNode arg |
nodeCandFwd1(arg, fromArg, config) and
viableParamArg(call, _, arg)
@@ -395,8 +377,11 @@ bindingset[result, b]
private boolean unbindBool(boolean b) { result != b.booleanNot() }
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is part of a path from a source to a sink in the
+ * configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink.
*/
pragma[nomagic]
private predicate nodeCand1(Node node, boolean toReturn, Configuration config) {
@@ -433,81 +418,86 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
)
or
// store
- exists(Content f |
- nodeCand1Store(f, node, toReturn, config) and
- readCand1(f, config)
+ exists(Content c |
+ nodeCand1Store(c, node, toReturn, config) and
+ nodeCand1IsRead(c, config)
)
or
// read
- exists(Node mid, Content f |
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ exists(Node mid, Content c |
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, toReturn, config)
)
or
// flow into a callable
exists(DataFlowCall call |
- nodeCand1Arg(call, node, false, config) and
+ nodeCand1In(call, node, false, config) and
toReturn = false
or
- nodeCand1ArgToReturn(call, node, config) and
- flowInCand1(call, toReturn, config)
+ nodeCand1InToReturn(call, node, config) and
+ nodeCand1IsReturned(call, toReturn, config)
)
or
// flow out of a callable
exists(ReturnPosition pos |
- nodeCand1ReturnPosition(pos, config) and
+ nodeCand1Out(pos, config) and
getReturnPosition(node) = pos and
toReturn = true
)
}
-pragma[nomagic]
-private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _, config) }
-
-pragma[nomagic]
-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`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate readCand1(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content c, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd1(node, unbind(config)) and
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, _, config)
)
}
pragma[nomagic]
-private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
- exists(Node mid |
+private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
+ exists(Node mid, TypedContent tc |
nodeCand1(mid, toReturn, config) and
- storeCandFwd1(f, unbind(config)) and
- storeDirect(node, f, mid)
+ nodeCandFwd1IsStored(c, unbind(config)) and
+ store(node, tc, mid, _) and
+ c = tc.getContent()
)
}
/**
- * Holds if `f` is the target of both a read and a store in the flow covered
+ * Holds if `c` is the target of both a read and a store in the flow covered
* by `nodeCand1`.
*/
-private predicate readStoreCand1(Content f, Configuration conf) {
- readCand1(f, conf) and
- nodeCand1Store(f, _, _, conf)
+private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
+ nodeCand1IsRead(c, conf) and
+ nodeCand1Store(c, _, _, conf)
}
pragma[nomagic]
-private predicate viableParamArgCandFwd1(
+private predicate viableReturnPosOutNodeCandFwd1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+) {
+ nodeCandFwd1ReturnPosition(pos, _, config) and
+ viableReturnPosOut(call, pos, out)
+}
+
+pragma[nomagic]
+private predicate nodeCand1Out(ReturnPosition pos, Configuration config) {
+ exists(DataFlowCall call, Node out |
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, config)
+ )
+}
+
+pragma[nomagic]
+private predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
) {
viableParamArg(call, p, arg) and
@@ -515,32 +505,35 @@ private predicate viableParamArgCandFwd1(
}
pragma[nomagic]
-private predicate nodeCand1Arg(
+private predicate nodeCand1In(
DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config
) {
exists(ParameterNode p |
nodeCand1(p, toReturn, config) and
- viableParamArgCandFwd1(call, p, arg, config)
+ viableParamArgNodeCandFwd1(call, p, arg, config)
)
}
pragma[nomagic]
-private predicate nodeCand1ArgToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
- nodeCand1Arg(call, arg, true, config)
+private predicate nodeCand1InToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
+ nodeCand1In(call, arg, true, config)
}
/**
* Holds if an output from `call` is reached in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate flowInCand1(DataFlowCall call, boolean toReturn, Configuration config) {
+private predicate nodeCand1IsReturned(DataFlowCall call, boolean toReturn, Configuration config) {
exists(Node out |
nodeCand1(out, toReturn, config) and
nodeCandFwd1OutFromArg(call, out, config)
)
}
-private predicate throughFlowNodeCand(Node node, Configuration config) {
+pragma[nomagic]
+private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _, config) }
+
+private predicate throughFlowNodeCand1(Node node, Configuration config) {
nodeCand1(node, true, config) and
not fullBarrier(node, config) and
not inBarrier(node, config) and
@@ -549,11 +542,11 @@ private predicate throughFlowNodeCand(Node node, Configuration config) {
/** Holds if flow may return from `callable`. */
pragma[nomagic]
-private predicate returnFlowCallableCand(
+private predicate returnFlowCallableNodeCand1(
DataFlowCallable callable, ReturnKindExt kind, Configuration config
) {
exists(ReturnNodeExt ret |
- throughFlowNodeCand(ret, config) and
+ throughFlowNodeCand1(ret, config) and
callable = ret.getEnclosingCallable() and
kind = ret.getKind()
)
@@ -563,10 +556,10 @@ private predicate returnFlowCallableCand(
* Holds if flow may enter through `p` and reach a return node making `p` a
* candidate for the origin of a summary.
*/
-private predicate parameterThroughFlowCand(ParameterNode p, Configuration config) {
+private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration config) {
exists(ReturnKindExt kind |
- throughFlowNodeCand(p, config) and
- returnFlowCallableCand(p.getEnclosingCallable(), kind, config) and
+ throughFlowNodeCand1(p, config) and
+ returnFlowCallableNodeCand1(p.getEnclosingCallable(), kind, config) and
// we don't expect a parameter to return stored in itself
not exists(int pos |
kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos)
@@ -575,420 +568,77 @@ private predicate parameterThroughFlowCand(ParameterNode p, Configuration config
}
pragma[nomagic]
-private predicate store(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
- nodeCand1(n2, unbind(config)) and
- (
- storeDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentSome(f), n2)
- )
-}
-
-pragma[nomagic]
-private predicate read(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
- nodeCand1(n2, unbind(config)) and
- (
- readDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentSome(f), TContentNone(), n2)
- )
-}
-
-/**
- * Holds if `p` can flow to `node` in the same callable with `summary`
- * representing the flow path. The type of the tracked object is `t2`, and if
- * the summary includes a store step, `t1` is the tracked type just prior to the
- * store, that is, the type of the stored object, otherwise `t1` is equal to `t2`.
- */
-pragma[nomagic]
-private predicate parameterFlow(
- ParameterNode p, Node node, DataFlowType t1, DataFlowType t2, Summary summary,
- Configuration config
-) {
- parameterThroughFlowCand(p, config) and
- p = node and
- t1 = getErasedNodeTypeBound(node) and
- t1 = t2 and
- summary = TSummaryVal()
- or
- throughFlowNodeCand(node, unbind(config)) and
- (
- exists(Node mid |
- parameterFlow(p, mid, t1, t2, summary, config) and
- localFlowStep(mid, node, config) and
- compatibleTypes(t2, getErasedNodeTypeBound(node))
- )
- or
- exists(Node mid, Summary midsum |
- parameterFlow(p, mid, _, _, midsum, config) and
- additionalLocalFlowStep(mid, node, config) and
- t1 = getErasedNodeTypeBound(node) and
- t1 = t2 and
- summary = midsum.additionalStep()
- )
- or
- // read step
- exists(Node mid, Content f, Summary midsum |
- parameterFlow(p, mid, _, _, midsum, config) and
- read(mid, f, node, config) and
- summary = midsum.readStep(f) and
- t1 = f.getType() and
- t1 = t2
- )
- or
- // store step
- exists(Node mid, Content f, Summary midsum |
- parameterFlow(p, mid, t1, /* t1 */ _, midsum, config) and
- store(mid, f, node, config) and
- summary = midsum.storeStep(f) and
- compatibleTypes(t1, f.getType()) and
- t2 = f.getContainerType()
- )
- or
- // value flow through a callable
- exists(Node arg |
- parameterFlow(p, arg, t1, t2, summary, config) and
- argumentValueFlowsThrough(_, arg, TContentNone(), TContentNone(), node) and
- compatibleTypes(t2, getErasedNodeTypeBound(node))
- )
- or
- // flow through a callable
- exists(Node arg, Summary s1, Summary s2 |
- parameterFlow(p, arg, _, _, s1, config) and
- argumentFlowsThrough(arg, node, t1, t2, s2, config) and
- summary = s1.compose(s2)
- )
- )
-}
-
-private predicate viableParamArgCand(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
-) {
- viableParamArg(call, p, arg) and
- nodeCand1(arg, unbind(config)) and
- nodeCand1(p, config) and
- not outBarrier(arg, config) and
- not inBarrier(p, config)
-}
-
-pragma[nomagic]
-private predicate parameterFlowReturn(
- ParameterNode p, ReturnNodeExt ret, ReturnKindExt kind, DataFlowType t1, DataFlowType t2,
- Summary summary, Configuration config
-) {
- parameterFlow(p, ret, t1, t2, summary, config) and
- kind = ret.getKind() and
- not summary.isPartial() and
- not exists(int pos | kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos))
-}
-
-pragma[nomagic]
-private predicate argumentFlowsThrough0(
- DataFlowCall call, ArgumentNode arg, ReturnKindExt kind, DataFlowType t1, DataFlowType t2,
- Summary summary, Configuration config
-) {
- exists(ParameterNode p |
- viableParamArgCand(call, p, arg, config) and
- parameterFlowReturn(p, _, kind, t1, t2, summary, config)
- )
-}
-
-/**
- * Holds if data can flow from `arg` to `out` through a call with `summary`
- * representing the flow path. The type of the tracked object is `t2`, and if
- * the summary includes a store step, `t1` is the tracked type just prior to the
- * store, that is, the type of the stored object, otherwise `t1` is equal to `t2`.
- */
-private predicate argumentFlowsThrough(
- ArgumentNode arg, Node out, DataFlowType t1, DataFlowType t2, Summary summary,
- Configuration config
-) {
- nodeCand1(out, unbind(config)) and
- not inBarrier(out, config) and
- compatibleTypes(t2, getErasedNodeTypeBound(out)) and
- exists(DataFlowCall call, ReturnKindExt kind |
- argumentFlowsThrough0(call, arg, kind, t1, t2, summary, config) and
- out = kind.getAnOutNode(call)
- )
-}
-
-pragma[noinline]
-private predicate readStoreNode(
- DataFlowCall call, ArgumentNode arg, Content f1, Configuration config
-) {
- exists(Content f2, Node out |
- argumentValueFlowsThrough(call, arg, TContentSome(f1), TContentSome(f2), out) and
- nodeCand1(out, config) and
- readStoreCand1(f2, unbind(config))
- )
-}
-
-private newtype TNodeExt =
- TNormalNode(Node node) { nodeCand1(node, _) } or
- TReadStoreNode(DataFlowCall call, ArgumentNode arg, Content f1, Configuration config) {
- nodeCand1(arg, config) and
- readStoreNode(call, arg, f1, config) and
- readStoreCand1(f1, unbind(config))
- } or
- TReadTaintNode(ArgumentNode arg, Content f, Configuration config) {
- argumentFlowsThrough(arg, _, _, _, TSummaryReadTaint(f), config)
- } or
- TTaintStoreNode(ArgumentNode arg, DataFlowType t, Configuration config) {
- argumentFlowsThrough(arg, _, t, _, TSummaryTaintStore(_), config)
- }
-
-/**
- * An extended data flow node. Either a normal node, or an intermediate node
- * used to split up a summarized flow steps.
- *
- * This is purely an internal implementation detail.
- */
-abstract private class NodeExt extends TNodeExt {
- /** Gets the underlying (normal) node, if any. */
- abstract Node getNode();
-
- abstract DataFlowType getErasedNodeTypeBound();
-
- abstract DataFlowCallable getEnclosingCallable();
-
- abstract predicate isCand1(Configuration config);
-
- abstract string toString();
-
- abstract predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- );
-}
-
-/** A `Node` at which a cast can occur such that the type should be checked. */
-abstract private class CastingNodeExt extends NodeExt { }
-
-private class NormalNodeExt extends NodeExt, TNormalNode {
- override Node getNode() { this = TNormalNode(result) }
-
- override DataFlowType getErasedNodeTypeBound() {
- result = getErasedRepr(this.getNode().getTypeBound())
- }
-
- override DataFlowCallable getEnclosingCallable() {
- result = this.getNode().getEnclosingCallable()
- }
-
- override predicate isCand1(Configuration config) { nodeCand1(this.getNode(), config) }
-
- override string toString() { result = this.getNode().toString() }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class NormalCastingNodeExt extends CastingNodeExt, NormalNodeExt {
- NormalCastingNodeExt() { this.getNode() instanceof CastingNode }
-}
-
-private class ReadStoreNodeExt extends CastingNodeExt, TReadStoreNode {
- private DataFlowCall call;
- private ArgumentNode arg;
- private Content f1;
- private Configuration config0;
-
- ReadStoreNodeExt() { this = TReadStoreNode(call, arg, f1, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = f1.getType() }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = "(inside) " + call.toString() + " [read " + f1 + "]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class ReadTaintNode extends NodeExt, TReadTaintNode {
- private ArgumentNode arg;
- private Content f;
- private Configuration config0;
-
- ReadTaintNode() { this = TReadTaintNode(arg, f, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = f.getType() }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = arg.toString() + " [read taint " + f + "]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class TaintStoreNode extends NodeExt, TTaintStoreNode {
- private ArgumentNode arg;
- private DataFlowType t;
- private Configuration config0;
-
- TaintStoreNode() { this = TTaintStoreNode(arg, t, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = t }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = arg.toString() + " [taint store]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private predicate additionalLocalFlowStepExt(
- NodeExt node1, NodeExt node2, DataFlowType t, Configuration config
-) {
- exists(ArgumentNode arg, Content f |
- node1 = TReadTaintNode(arg, f, config) and
- argumentFlowsThrough(arg, node2.getNode(), _, t, TSummaryReadTaint(f), config)
- )
- or
- node2 = TTaintStoreNode(node1.getNode(), t, config)
-}
-
-pragma[nomagic]
-private predicate readExt(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- read(node1.getNode(), f, node2.getNode(), config)
- or
- node2 = TReadStoreNode(_, node1.getNode(), f, config)
- or
- node2 = TReadTaintNode(node1.getNode(), f, config)
-}
-
-pragma[nomagic]
-private predicate storeExt(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- store(node1.getNode(), f, node2.getNode(), config)
- or
- exists(DataFlowCall call, ArgumentNode arg, Content f1, Node n2 |
- node1 = TReadStoreNode(call, arg, f1, config) and
- n2 = node2.getNode() and
- argumentValueFlowsThrough(call, arg, TContentSome(f1), TContentSome(f), n2) and
+private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
+ exists(TypedContent tc |
+ nodeCand1IsReadAndStored(c, config) and
nodeCand1(n2, unbind(config)) and
- readStoreCand1(f, unbind(config))
- )
- or
- exists(ArgumentNode arg, DataFlowType t |
- node1 = TTaintStoreNode(arg, t, config) and
- argumentFlowsThrough(arg, node2.getNode(), t, _, TSummaryTaintStore(f), config)
+ store(n1, tc, n2, _) and
+ c = tc.getContent()
)
}
-private predicate jumpStepExt(NodeExt node1, NodeExt node2, Configuration config) {
- jumpStep(node1.getNode(), node2.getNode(), config)
-}
-
-private predicate additionalJumpStepExt(NodeExt node1, NodeExt node2, Configuration config) {
- additionalJumpStep(node1.getNode(), node2.getNode(), config)
-}
-
-private predicate argumentValueFlowsThrough(NodeExt node1, NodeExt node2) {
- argumentValueFlowsThrough(_, node1.getNode(), TContentNone(), TContentNone(), node2.getNode())
-}
-
-private predicate argumentFlowsThrough(
- NodeExt arg, NodeExt out, DataFlowType t, Configuration config
-) {
- argumentFlowsThrough(arg.getNode(), out.getNode(), _, t, TSummaryTaint(), config)
-}
-
-/**
- * Holds if data can flow from `node1` to `node2` in one local step or a step
- * through a callable.
- */
-pragma[noinline]
-private predicate localFlowStepOrFlowThroughCallable(
- NodeExt node1, NodeExt node2, Configuration config
-) {
- exists(Node n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode()
- |
- nodeCand1(n1, config) and
- localFlowStep(n1, n2, config)
- or
- nodeCand1(n1, config) and
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentNone(), n2)
- )
-}
-
-/**
- * Holds if data can flow from `node1` to `node2` in one local step or a step
- * through a callable, in both cases using an additional flow step from the
- * configuration.
- */
-pragma[noinline]
-private predicate additionalLocalFlowStepOrFlowThroughCallable(
- NodeExt node1, NodeExt node2, Configuration config
-) {
- exists(Node n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode()
- |
- nodeCand1(n1, config) and
- additionalLocalFlowStep(n1, n2, config)
- or
- argumentFlowsThrough(n1, n2, _, _, TSummaryTaint(), config)
- )
- or
- additionalLocalFlowStepExt(node1, node2, _, config)
+pragma[nomagic]
+private predicate read(Node n1, Content c, Node n2, Configuration config) {
+ nodeCand1IsReadAndStored(c, config) and
+ nodeCand1(n2, unbind(config)) and
+ read(n1, c, n2)
}
pragma[noinline]
-private ReturnPosition getReturnPosition1(ReturnNodeExt node, Configuration config) {
- result = getReturnPosition(node) and
- nodeCand1(node, config)
+private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ localFlowStep(node1, node2, config)
+}
+
+pragma[noinline]
+private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ additionalLocalFlowStep(node1, node2, config)
+}
+
+pragma[nomagic]
+private predicate viableReturnPosOutNodeCand1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+) {
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, config)
}
/**
- * Holds if data can flow out of a callable from `node1` to `node2`, either
+ * Holds if data can flow out of `call` from `ret` to `out`, 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 flowOutOfCallableNodeCand1(ReturnNodeExt node1, Node node2, Configuration config) {
- nodeCand1(node2, config) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- exists(DataFlowCall call, ReturnKindExt kind |
- getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
- node2 = kind.getAnOutNode(call)
- )
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand1(
+ DataFlowCall call, ReturnNodeExt ret, Node out, Configuration config
+) {
+ viableReturnPosOutNodeCand1(call, getReturnPosition(ret), out, config) and
+ nodeCand1(ret, config) and
+ not outBarrier(ret, config) and
+ not inBarrier(out, config)
+}
+
+pragma[nomagic]
+private predicate viableParamArgNodeCand1(
+ DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+) {
+ viableParamArgNodeCandFwd1(call, p, arg, config) and
+ nodeCand1(arg, config)
}
/**
- * Holds if data can flow into a callable and that this step is part of a
+ * Holds if data can flow into `call` and that this step is part of a
* path from a source to a sink.
*/
-private predicate flowIntoCallableNodeCand1(
- ArgumentNode node1, ParameterNode node2, Configuration config
+pragma[nomagic]
+private predicate flowIntoCallNodeCand1(
+ DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config
) {
- viableParamArgCand(_, node2, node1, config)
+ viableParamArgNodeCand1(call, p, arg, config) and
+ nodeCand1(p, config) and
+ not outBarrier(arg, config) and
+ not inBarrier(p, config)
}
/**
@@ -999,7 +649,7 @@ private predicate flowIntoCallableNodeCand1(
private int branch(Node n1, Configuration conf) {
result =
strictcount(Node n |
- flowOutOfCallableNodeCand1(n1, n, conf) or flowIntoCallableNodeCand1(n1, n, conf)
+ flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1011,269 +661,391 @@ private int branch(Node n1, Configuration conf) {
private int join(Node n2, Configuration conf) {
result =
strictcount(Node n |
- flowOutOfCallableNodeCand1(n, n2, conf) or flowIntoCallableNodeCand1(n, n2, conf)
+ flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
/**
- * Holds if data can flow out of a callable from `node1` to `node2`, either
+ * Holds if data can flow out of `call` from `ret` to `out`, 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. The
* `allowsFieldFlow` flag indicates whether the branching is within the limit
* specified by the configuration.
*/
-private predicate flowOutOfCallableNodeCand1(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand1(
+ DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config
) {
- exists(ReturnNodeExt n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode() and
- flowOutOfCallableNodeCand1(n1, n2, config) and
- exists(int b, int j |
- b = branch(n1, config) and
- j = join(n2, config) and
- if b.minimum(j) <= config.fieldFlowBranchLimit()
- then allowsFieldFlow = true
- else allowsFieldFlow = false
- )
+ flowOutOfCallNodeCand1(call, ret, out, config) and
+ exists(int b, int j |
+ b = branch(ret, config) and
+ j = join(out, config) and
+ if b.minimum(j) <= config.fieldFlowBranchLimit()
+ then allowsFieldFlow = true
+ else allowsFieldFlow = false
)
}
/**
- * Holds if data can flow into a callable and that this step is part of a
+ * Holds if data can flow into `call` and that this step is part of a
* path from a source to a sink. The `allowsFieldFlow` flag indicates whether
* the branching is within the limit specified by the configuration.
*/
-private predicate flowIntoCallableNodeCand1(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+pragma[nomagic]
+private predicate flowIntoCallNodeCand1(
+ DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow,
+ Configuration config
) {
- exists(ArgumentNode n1, ParameterNode n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode() and
- flowIntoCallableNodeCand1(n1, n2, config) and
- exists(int b, int j |
- b = branch(n1, config) and
- j = join(n2, config) and
- if b.minimum(j) <= config.fieldFlowBranchLimit()
- then allowsFieldFlow = true
- else allowsFieldFlow = false
- )
+ flowIntoCallNodeCand1(call, arg, p, config) and
+ exists(int b, int j |
+ b = branch(arg, config) and
+ j = join(p, config) and
+ if b.minimum(j) <= config.fieldFlowBranchLimit()
+ then allowsFieldFlow = true
+ else allowsFieldFlow = false
)
}
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is reachable from a source in the configuration `config`.
+ * The Boolean `stored` records whether the tracked value is stored into a
+ * field of `node`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argStored` records whether the tracked
+ * value was stored into a field of the argument.
*/
-private predicate nodeCandFwd2(NodeExt node, boolean fromArg, boolean stored, Configuration config) {
- nodeCand1(node.getNode(), config) and
- config.isSource(node.getNode()) and
+private predicate nodeCandFwd2(
+ Node node, boolean fromArg, BooleanOption argStored, boolean stored, Configuration config
+) {
+ nodeCand1(node, config) and
+ config.isSource(node) and
fromArg = false and
+ argStored = TBooleanNone() and
stored = false
or
- node.isCand1(unbind(config)) and
+ nodeCand1(node, unbind(config)) and
(
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, stored, config) and
- localFlowStepOrFlowThroughCallable(mid, node, config)
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, stored, config) and
+ localFlowStepNodeCand1(mid, node, config)
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, stored, config) and
- additionalLocalFlowStepOrFlowThroughCallable(mid, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, stored, config) and
+ additionalLocalFlowStepNodeCand1(mid, node, config) and
stored = false
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, _, stored, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
+ exists(Node mid |
+ nodeCandFwd2(mid, _, _, stored, config) and
+ jumpStep(mid, node, config) and
+ fromArg = false and
+ argStored = TBooleanNone()
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, _, stored, config) and
- additionalJumpStepExt(mid, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, _, _, stored, config) and
+ additionalJumpStep(mid, node, config) and
fromArg = false and
+ argStored = TBooleanNone() and
stored = false
)
or
// store
- exists(NodeExt mid, Content f |
- nodeCandFwd2(mid, fromArg, _, config) and
- storeExt(mid, f, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, _, config) and
+ storeCand1(mid, _, node, config) and
stored = true
)
or
// read
- exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, config) and
- storeCandFwd2(f, stored, config)
+ exists(Content c |
+ nodeCandFwd2Read(c, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(c, stored, config)
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- nodeCandFwd2(mid, _, stored, config) and
- flowIntoCallableNodeCand1(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (stored = false or allowsFieldFlow = true)
- )
+ // flow into a callable
+ nodeCandFwd2In(_, node, _, _, stored, config) and
+ fromArg = true and
+ if parameterThroughFlowNodeCand1(node, config)
+ then argStored = TBooleanSome(stored)
+ else argStored = TBooleanNone()
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- nodeCandFwd2(mid, false, stored, config) and
- flowOutOfCallableNodeCand1(mid, node, allowsFieldFlow, config) and
- fromArg = false and
- (stored = false or allowsFieldFlow = true)
+ // flow out of a callable
+ exists(DataFlowCall call |
+ nodeCandFwd2Out(call, node, fromArg, argStored, stored, config) and
+ fromArg = false
+ or
+ exists(boolean argStored0 |
+ nodeCandFwd2OutFromArg(call, node, argStored0, stored, config) and
+ nodeCandFwd2IsEntered(call, fromArg, argStored, argStored0, config)
+ )
)
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
*/
pragma[noinline]
-private predicate storeCandFwd2(Content f, boolean stored, Configuration config) {
- exists(NodeExt mid, NodeExt node |
+private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
+ exists(Node mid, Node node |
useFieldFlow(config) and
- node.isCand1(unbind(config)) and
- nodeCandFwd2(mid, _, stored, config) and
- storeExt(mid, f, node, config)
+ nodeCand1(node, unbind(config)) and
+ nodeCandFwd2(mid, _, _, stored, config) and
+ storeCand1(mid, c, node, config)
)
}
pragma[nomagic]
-private predicate nodeCandFwd2Read(Content f, NodeExt node, boolean fromArg, Configuration config) {
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, true, config) and
- readExt(mid, f, node, config)
+private predicate nodeCandFwd2Read(
+ Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+) {
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, true, config) and
+ read(mid, c, node, config)
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2In(
+ DataFlowCall call, ParameterNode p, boolean fromArg, BooleanOption argStored, boolean stored,
+ Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ nodeCandFwd2(arg, fromArg, argStored, stored, config) and
+ flowIntoCallNodeCand1(call, arg, p, allowsFieldFlow, config)
+ |
+ stored = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2Out(
+ DataFlowCall call, Node out, boolean fromArg, BooleanOption argStored, boolean stored,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ nodeCandFwd2(ret, fromArg, argStored, stored, config) and
+ flowOutOfCallNodeCand1(call, ret, out, allowsFieldFlow, config)
+ |
+ stored = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2OutFromArg(
+ DataFlowCall call, Node out, boolean argStored, boolean stored, Configuration config
+) {
+ nodeCandFwd2Out(call, out, true, TBooleanSome(argStored), stored, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `nodeCandFwd2`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd2IsEntered(
+ DataFlowCall call, boolean fromArg, BooleanOption argStored, boolean stored, Configuration config
+) {
+ exists(ParameterNode p |
+ nodeCandFwd2In(call, p, fromArg, argStored, stored, config) and
+ parameterThroughFlowNodeCand1(p, config)
)
}
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is part of a path from a source to a sink in the
+ * configuration `config`. The Boolean `read` records whether the tracked
+ * value must be read from a field of `node` in order to reach a sink.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnRead`
+ * records whether a field must be read from the returned value.
*/
-private predicate nodeCand2(NodeExt node, boolean toReturn, boolean read, Configuration config) {
- nodeCandFwd2(node, _, false, config) and
- config.isSink(node.getNode()) and
+private predicate nodeCand2(
+ Node node, boolean toReturn, BooleanOption returnRead, boolean read, Configuration config
+) {
+ nodeCandFwd2(node, _, _, false, config) and
+ config.isSink(node) and
toReturn = false and
+ returnRead = TBooleanNone() and
read = false
or
- nodeCandFwd2(node, _, unbindBool(read), unbind(config)) and
+ nodeCandFwd2(node, _, _, unbindBool(read), unbind(config)) and
(
- exists(NodeExt mid |
- localFlowStepOrFlowThroughCallable(node, mid, config) and
- nodeCand2(mid, toReturn, read, config)
+ exists(Node mid |
+ localFlowStepNodeCand1(node, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, read, config)
)
or
- exists(NodeExt mid |
- additionalLocalFlowStepOrFlowThroughCallable(node, mid, config) and
- nodeCand2(mid, toReturn, read, config) and
+ exists(Node mid |
+ additionalLocalFlowStepNodeCand1(node, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, read, config) and
read = false
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- nodeCand2(mid, _, read, config) and
- toReturn = false
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ nodeCand2(mid, _, _, read, config) and
+ toReturn = false and
+ returnRead = TBooleanNone()
)
or
- exists(NodeExt mid |
- additionalJumpStepExt(node, mid, config) and
- nodeCand2(mid, _, read, config) and
+ exists(Node mid |
+ additionalJumpStep(node, mid, config) and
+ nodeCand2(mid, _, _, read, config) and
toReturn = false and
+ returnRead = TBooleanNone() and
read = false
)
or
// store
- exists(Content f |
- nodeCand2Store(f, node, toReturn, read, config) and
- readCand2(f, read, config)
+ exists(Content c |
+ nodeCand2Store(c, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(c, read, config)
)
or
// read
- exists(NodeExt mid, Content f, boolean read0 |
- readExt(node, f, mid, config) and
- storeCandFwd2(f, unbindBool(read0), unbind(config)) and
- nodeCand2(mid, toReturn, read0, config) and
+ exists(Node mid, Content c, boolean read0 |
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
+ nodeCand2(mid, toReturn, returnRead, read0, config) and
read = true
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand1(node, mid, allowsFieldFlow, config) and
- nodeCand2(mid, false, read, config) and
- toReturn = false and
- (read = false or allowsFieldFlow = true)
+ // flow into a callable
+ exists(DataFlowCall call |
+ nodeCand2In(call, node, toReturn, returnRead, read, config) and
+ toReturn = false
+ or
+ exists(boolean returnRead0 |
+ nodeCand2InToReturn(call, node, returnRead0, read, config) and
+ nodeCand2IsReturned(call, toReturn, returnRead, returnRead0, config)
+ )
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand1(node, mid, allowsFieldFlow, config) and
- nodeCand2(mid, _, read, config) and
- toReturn = true and
- (read = false or allowsFieldFlow = true)
- )
+ // flow out of a callable
+ nodeCand2Out(_, node, _, _, read, config) and
+ toReturn = true and
+ if nodeCandFwd2(node, true, TBooleanSome(_), unbindBool(read), config)
+ then returnRead = TBooleanSome(read)
+ else returnRead = TBooleanNone()
)
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate readCand2(Content f, boolean read, Configuration config) {
- exists(NodeExt mid, NodeExt node |
+private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
+ exists(Node mid, Node node |
useFieldFlow(config) and
- nodeCandFwd2(node, _, true, unbind(config)) and
- readExt(node, f, mid, config) and
- storeCandFwd2(f, unbindBool(read), unbind(config)) and
- nodeCand2(mid, _, read, config)
+ nodeCandFwd2(node, _, _, true, unbind(config)) and
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
+ nodeCand2(mid, _, _, read, config)
)
}
pragma[nomagic]
private predicate nodeCand2Store(
- Content f, NodeExt node, boolean toReturn, boolean stored, Configuration config
+ Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
+ Configuration config
) {
- exists(NodeExt mid |
- storeExt(node, f, mid, config) and
- nodeCand2(mid, toReturn, true, config) and
- nodeCandFwd2(node, _, stored, unbind(config))
- )
-}
-
-pragma[nomagic]
-private predicate storeCand2(Content f, boolean stored, Configuration conf) {
- exists(NodeExt node |
- nodeCand2Store(f, node, _, stored, conf) and
- nodeCand2(node, _, stored, conf)
+ exists(Node mid |
+ storeCand1(node, c, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, true, config) and
+ nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
/**
- * Holds if `f` is the target of both a store and a read in the path graph
- * covered by `nodeCand2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
*/
-pragma[noinline]
-private predicate readStoreCand(Content f, Configuration conf) {
- exists(boolean apNonEmpty |
- storeCand2(f, apNonEmpty, conf) and
- readCand2(f, apNonEmpty, conf)
+pragma[nomagic]
+private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
+ exists(Node node |
+ nodeCand2Store(c, node, _, _, stored, conf) and
+ nodeCand2(node, _, _, stored, conf)
)
}
-private predicate nodeCand2(NodeExt node, Configuration config) { nodeCand2(node, _, _, config) }
+/**
+ * Holds if `c` is the target of both a store and a read in the path graph
+ * covered by `nodeCand2`.
+ */
+pragma[noinline]
+private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
+ exists(boolean apNonEmpty |
+ nodeCand2IsStored(c, apNonEmpty, conf) and
+ nodeCand2IsRead(c, apNonEmpty, conf)
+ )
+}
pragma[nomagic]
-private predicate flowOutOfCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate nodeCand2Out(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
) {
- flowOutOfCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ exists(Node out, boolean allowsFieldFlow |
+ nodeCand2(out, toReturn, returnRead, read, config) and
+ flowOutOfCallNodeCand1(call, ret, out, allowsFieldFlow, config)
+ |
+ read = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCand2In(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ nodeCand2(p, toReturn, returnRead, read, config) and
+ flowIntoCallNodeCand1(call, arg, p, allowsFieldFlow, config)
+ |
+ read = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCand2InToReturn(
+ DataFlowCall call, ArgumentNode arg, boolean returnRead, boolean read, Configuration config
+) {
+ nodeCand2In(call, arg, true, TBooleanSome(returnRead), read, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `nodeCand2`.
+ */
+pragma[nomagic]
+private predicate nodeCand2IsReturned(
+ DataFlowCall call, boolean toReturn, BooleanOption returnRead, boolean read, Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ nodeCand2Out(call, ret, toReturn, returnRead, read, config) and
+ nodeCandFwd2(ret, true, TBooleanSome(_), read, config)
+ )
+}
+
+private predicate nodeCand2(Node node, Configuration config) { nodeCand2(node, _, _, _, config) }
+
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand2(
+ DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
+) {
+ flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
nodeCand2(node2, config) and
nodeCand2(node1, unbind(config))
}
pragma[nomagic]
-private predicate flowIntoCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate flowIntoCallNodeCand2(
+ DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ Configuration config
) {
- flowIntoCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
nodeCand2(node2, config) and
nodeCand2(node1, unbind(config))
}
@@ -1284,15 +1056,15 @@ private module LocalFlowBigStep {
* flow steps in a dataflow path.
*/
private predicate localFlowEntry(Node node, Configuration config) {
- nodeCand2(TNormalNode(node), config) and
+ nodeCand2(node, config) and
(
config.isSource(node) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
node instanceof ParameterNode or
- node instanceof OutNode or
- node instanceof PostUpdateNode or
- readDirect(_, _, node) or
+ node instanceof OutNodeExt or
+ store(_, _, node, _) or
+ read(_, _, node) or
node instanceof CastNode
)
}
@@ -1302,15 +1074,13 @@ private module LocalFlowBigStep {
* flow steps in a dataflow path.
*/
private predicate localFlowExit(Node node, Configuration config) {
- exists(Node next | nodeCand2(TNormalNode(next), config) |
+ exists(Node next | nodeCand2(next, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
- flowIntoCallableNodeCand1(node, next, config) or
- flowOutOfCallableNodeCand1(node, next, config) or
- argumentFlowsThrough(node, next, _, _, _, config) or
- argumentValueFlowsThrough(_, node, TContentNone(), TContentNone(), next) or
- storeDirect(node, _, next) or
- readDirect(node, _, next)
+ flowIntoCallNodeCand1(_, node, next, config) or
+ flowOutOfCallNodeCand1(_, node, next, config) or
+ store(node, _, next, _) or
+ read(node, _, next)
)
or
node instanceof CastNode
@@ -1318,6 +1088,13 @@ private module LocalFlowBigStep {
config.isSink(node)
}
+ pragma[noinline]
+ private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
+ additionalLocalFlowStepNodeCand1(node1, node2, config) and
+ nodeCand2(node1, _, _, false, config) and
+ nodeCand2(node2, _, _, false, unbind(config))
+ }
+
/**
* Holds if the local path from `node1` to `node2` is a prefix of a maximal
* subsequence of local flow steps in a dataflow path.
@@ -1334,33 +1111,33 @@ private module LocalFlowBigStep {
(
localFlowEntry(node1, config) and
(
- localFlowStep(node1, node2, config) and
+ localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
t = getErasedNodeTypeBound(node1)
or
- additionalLocalFlowStep(node1, node2, config) and
+ additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
t = getErasedNodeTypeBound(node2)
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
or
exists(Node mid |
localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
- localFlowStep(mid, node2, config) and
+ localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof CastNode and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
)
or
exists(Node mid |
localFlowStepPlus(node1, mid, _, _, config, cc) and
- additionalLocalFlowStep(mid, node2, config) and
+ additionalLocalFlowStepNodeCand2(mid, node2, config) and
not mid instanceof CastNode and
preservesValue = false and
t = getErasedNodeTypeBound(node2) and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
)
)
}
@@ -1371,311 +1148,380 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config,
+ Node node1, Node node2, boolean preservesValue, AccessPathFrontNil apf, Configuration config,
LocalCallContext callContext
) {
- localFlowStepPlus(node1, node2, preservesValue, t, config, callContext) and
+ localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, config)
}
-
- pragma[nomagic]
- predicate localFlowBigStepExt(
- NodeExt node1, NodeExt node2, boolean preservesValue, AccessPathFrontNil apf,
- Configuration config
- ) {
- localFlowBigStep(node1.getNode(), node2.getNode(), preservesValue, apf.getType(), config, _)
- or
- additionalLocalFlowStepExt(node1, node2, apf.getType(), config) and
- nodeCand2(node1, config) and
- nodeCand2(node2, unbind(config)) and
- preservesValue = false
- }
}
private import LocalFlowBigStep
pragma[nomagic]
-private predicate readExtCand2(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- readExt(node1, f, node2, config) and
- nodeCand2(node1, _, true, unbind(config)) and
+private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
+ read(node1, c, node2, config) and
+ nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- readStoreCand(f, unbind(config))
+ nodeCand2IsReadAndStored(c, unbind(config))
}
pragma[nomagic]
-private predicate storeExtCand2(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- storeExt(node1, f, node2, config) and
+private predicate storeCand2(
+ Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+) {
+ store(node1, tc, node2, contentType) and
nodeCand2(node1, config) and
- nodeCand2(node2, _, true, unbind(config)) and
- readStoreCand(f, unbind(config))
-}
-
-private newtype TAccessPathFront =
- TFrontNil(DataFlowType t) or
- TFrontHead(Content f)
-
-/**
- * The front of an `AccessPath`. This is either a head or a nil.
- */
-abstract private class AccessPathFront extends TAccessPathFront {
- abstract string toString();
-
- abstract DataFlowType getType();
-
- abstract boolean toBoolNonEmpty();
-
- predicate headUsesContent(Content f) { this = TFrontHead(f) }
-}
-
-private class AccessPathFrontNil extends AccessPathFront, TFrontNil {
- override string toString() {
- exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
- }
-
- override DataFlowType getType() { this = TFrontNil(result) }
-
- override boolean toBoolNonEmpty() { result = false }
-}
-
-private class AccessPathFrontHead extends AccessPathFront, TFrontHead {
- override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) }
-
- override DataFlowType getType() {
- exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
- }
-
- override boolean toBoolNonEmpty() { result = true }
+ nodeCand2(node2, _, _, true, unbind(config)) and
+ nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
}
/**
- * Holds if data can flow from a source to `node` with the given `apf`.
+ * Holds if `node` is reachable with access path front `apf` from a
+ * source in the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argApf` records the front of the
+ * access path of that argument.
*/
pragma[nomagic]
private predicate flowCandFwd(
- NodeExt node, boolean fromArg, AccessPathFront apf, Configuration config
+ Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
) {
- flowCandFwd0(node, fromArg, apf, config) and
- if node instanceof CastingNodeExt
- then compatibleTypes(node.getErasedNodeTypeBound(), apf.getType())
+ flowCandFwd0(node, fromArg, argApf, apf, config) and
+ if node instanceof CastingNode
+ then compatibleTypes(getErasedNodeTypeBound(node), apf.getType())
else any()
}
pragma[nomagic]
private predicate flowCandFwd0(
- NodeExt node, boolean fromArg, AccessPathFront apf, Configuration config
+ Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
) {
- nodeCand2(node, _, false, config) and
- config.isSource(node.getNode()) and
+ nodeCand2(node, _, _, false, config) and
+ config.isSource(node) and
fromArg = false and
- apf = TFrontNil(node.getErasedNodeTypeBound())
+ argApf = TAccessPathFrontNone() and
+ apf = TFrontNil(getErasedNodeTypeBound(node))
or
- exists(NodeExt mid |
- flowCandFwd(mid, fromArg, apf, config) and
- localFlowBigStepExt(mid, node, true, _, config)
+ exists(Node mid |
+ flowCandFwd(mid, fromArg, argApf, apf, config) and
+ localFlowBigStep(mid, node, true, _, config, _)
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(mid, fromArg, nil, config) and
- localFlowBigStepExt(mid, node, false, apf, config)
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(mid, fromArg, argApf, nil, config) and
+ localFlowBigStep(mid, node, false, apf, config, _)
)
or
- nodeCand2(node, unbind(config)) and
- (
- exists(NodeExt mid |
- flowCandFwd(mid, _, apf, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(mid, _, nil, config) and
- additionalJumpStepExt(mid, node, config) and
- fromArg = false and
- apf = TFrontNil(node.getErasedNodeTypeBound())
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowCandFwd(mid, _, apf, config) and
- flowIntoCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowCandFwd(mid, false, apf, config) and
- flowOutOfCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = false and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- flowCandFwd(mid, fromArg, apf, config) and
- argumentValueFlowsThrough(mid, node)
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil, DataFlowType t |
- flowCandFwd(mid, fromArg, nil, config) and
- argumentFlowsThrough(mid, node, t, config) and
- apf = TFrontNil(t)
- )
+ exists(Node mid |
+ flowCandFwd(mid, _, _, apf, config) and
+ nodeCand2(node, unbind(config)) and
+ jumpStep(mid, node, config) and
+ fromArg = false and
+ argApf = TAccessPathFrontNone()
)
or
- exists(NodeExt mid, Content f |
- flowCandFwd(mid, fromArg, _, config) and
- storeExtCand2(mid, f, node, config) and
- nodeCand2(node, _, true, unbind(config)) and
- apf.headUsesContent(f)
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(mid, _, _, nil, config) and
+ nodeCand2(node, unbind(config)) and
+ additionalJumpStep(mid, node, config) and
+ fromArg = false and
+ argApf = TAccessPathFrontNone() and
+ apf = TFrontNil(getErasedNodeTypeBound(node))
)
or
- exists(Content f |
- flowCandFwdRead(f, node, fromArg, config) and
- consCandFwd(f, apf, config) and
- nodeCand2(node, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ // store
+ exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ storeCand2(mid, tc, node, contentType, config) and
+ nodeCand2(node, _, _, true, unbind(config)) and
+ apf.headUsesContent(tc) and
+ compatibleTypes(apf0.getType(), contentType)
+ )
+ or
+ // read
+ exists(TypedContent tc |
+ flowCandFwdRead(tc, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(tc, apf, config) and
+ nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ )
+ or
+ // flow into a callable
+ flowCandFwdIn(_, node, _, _, apf, config) and
+ fromArg = true and
+ if nodeCand2(node, true, _, unbindBool(apf.toBoolNonEmpty()), config)
+ then argApf = TAccessPathFrontSome(apf)
+ else argApf = TAccessPathFrontNone()
+ or
+ // flow out of a callable
+ exists(DataFlowCall call |
+ flowCandFwdOut(call, node, fromArg, argApf, apf, config) and
+ fromArg = false
+ or
+ exists(AccessPathFront argApf0 |
+ flowCandFwdOutFromArg(call, node, argApf0, apf, config) and
+ flowCandFwdIsEntered(call, fromArg, argApf, argApf0, config)
+ )
)
}
pragma[nomagic]
-private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) {
- exists(NodeExt mid, NodeExt n |
- flowCandFwd(mid, _, apf, config) and
- storeExtCand2(mid, f, n, config) and
- nodeCand2(n, _, true, unbind(config)) and
- compatibleTypes(apf.getType(), f.getType())
+private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n, DataFlowType contentType |
+ flowCandFwd(mid, _, _, apf, config) and
+ storeCand2(mid, tc, n, contentType, config) and
+ nodeCand2(n, _, _, true, unbind(config)) and
+ compatibleTypes(apf.getType(), contentType)
)
}
pragma[nomagic]
-private predicate flowCandFwdRead(Content f, NodeExt node, boolean fromArg, Configuration config) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowCandFwd(mid, fromArg, apf0, config) and
- readExtCand2(mid, f, node, config) and
- apf0.headUsesContent(f)
+private predicate flowCandFwdRead0(
+ Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFrontHead apf, Configuration config
+) {
+ flowCandFwd(node1, fromArg, argApf, apf, config) and
+ readCand2(node1, c, node2, config) and
+ apf.headUsesContent(tc)
+}
+
+pragma[nomagic]
+private predicate flowCandFwdRead(
+ TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+) {
+ flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
+}
+
+pragma[nomagic]
+private predicate flowCandFwdIn(
+ DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ flowCandFwd(arg, fromArg, argApf, apf, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ flowCandFwd(ret, fromArg, argApf, apf, config) and
+ flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandFwdOutFromArg(
+ DataFlowCall call, Node node, AccessPathFront argApf, AccessPathFront apf, Configuration config
+) {
+ flowCandFwdOut(call, node, true, TAccessPathFrontSome(argApf), apf, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `flowCandFwd`.
+ */
+pragma[nomagic]
+private predicate flowCandFwdIsEntered(
+ DataFlowCall call, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ParameterNode p |
+ flowCandFwdIn(call, p, fromArg, argApf, apf, config) and
+ nodeCand2(p, true, TBooleanSome(_), unbindBool(apf.toBoolNonEmpty()), config)
)
}
/**
- * Holds if data can flow from a source to `node` with the given `apf` and
- * from there flow to a sink.
+ * Holds if `node` with access path front `apf` is part of a path from a
+ * source to a sink in the configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnApf`
+ * records the front of the access path of the returned value.
*/
pragma[nomagic]
-private predicate flowCand(NodeExt node, boolean toReturn, AccessPathFront apf, Configuration config) {
- flowCand0(node, toReturn, apf, config) and
- flowCandFwd(node, _, apf, config)
+private predicate flowCand(
+ Node node, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ flowCand0(node, toReturn, returnApf, apf, config) and
+ flowCandFwd(node, _, _, apf, config)
}
pragma[nomagic]
private predicate flowCand0(
- NodeExt node, boolean toReturn, AccessPathFront apf, Configuration config
+ Node node, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
) {
- flowCandFwd(node, _, apf, config) and
- config.isSink(node.getNode()) and
+ flowCandFwd(node, _, _, apf, config) and
+ config.isSink(node) and
toReturn = false and
+ returnApf = TAccessPathFrontNone() and
apf instanceof AccessPathFrontNil
or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flowCand(mid, toReturn, apf, config)
+ exists(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flowCand(mid, toReturn, returnApf, apf, config)
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- localFlowBigStepExt(node, mid, false, _, config) and
- flowCand(mid, toReturn, nil, config) and
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(node, _, _, apf, config) and
+ localFlowBigStep(node, mid, false, _, config, _) and
+ flowCand(mid, toReturn, returnApf, nil, config) and
apf instanceof AccessPathFrontNil
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flowCand(mid, _, apf, config) and
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flowCand(mid, _, _, apf, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone()
+ )
+ or
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(node, _, _, apf, config) and
+ additionalJumpStep(node, mid, config) and
+ flowCand(mid, _, _, nil, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone() and
+ apf instanceof AccessPathFrontNil
+ )
+ or
+ // store
+ exists(TypedContent tc |
+ flowCandStore(node, tc, apf, toReturn, returnApf, config) and
+ flowCandConsCand(tc, apf, config)
+ )
+ or
+ // read
+ exists(TypedContent tc, AccessPathFront apf0 |
+ flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(tc, apf0, config)
+ )
+ or
+ // flow into a callable
+ exists(DataFlowCall call |
+ flowCandIn(call, node, toReturn, returnApf, apf, config) and
toReturn = false
+ or
+ exists(AccessPathFront returnApf0 |
+ flowCandInToReturn(call, node, returnApf0, apf, config) and
+ flowCandIsReturned(call, toReturn, returnApf, returnApf0, config)
+ )
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- additionalJumpStepExt(node, mid, config) and
- flowCand(mid, _, nil, config) and
- toReturn = false and
- apf instanceof AccessPathFrontNil
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flowCand(mid, false, apf, config) and
- toReturn = false and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flowCand(mid, _, apf, config) and
- toReturn = true and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- argumentValueFlowsThrough(node, mid) and
- flowCand(mid, toReturn, apf, config)
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil |
- argumentFlowsThrough(node, mid, _, config) and
- flowCand(mid, toReturn, nil, config) and
- apf instanceof AccessPathFrontNil and
- flowCandFwd(node, _, apf, config)
- )
- or
- exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, apf0, config) and
- apf0.headUsesContent(f) and
- consCand(f, apf, config)
- )
- or
- exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, config) and
- apf.headUsesContent(f)
- )
+ // flow out of a callable
+ flowCandOut(_, node, _, _, apf, config) and
+ toReturn = true and
+ if flowCandFwd(node, true, _, apf, config)
+ then returnApf = TAccessPathFrontSome(apf)
+ else returnApf = TAccessPathFrontNone()
+}
+
+pragma[nomagic]
+private predicate readCandFwd(
+ Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
+) {
+ flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
}
pragma[nomagic]
private predicate flowCandRead(
- NodeExt node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
) {
- exists(NodeExt mid |
- readExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ readCandFwd(node, tc, apf, mid, config) and
+ flowCand(mid, toReturn, returnApf, apf0, config)
)
}
pragma[nomagic]
private predicate flowCandStore(
- NodeExt node, Content f, boolean toReturn, AccessPathFrontHead apf0, Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, Configuration config
) {
- exists(NodeExt mid |
- storeExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ flowCandFwd(node, _, _, apf, config) and
+ storeCand2(node, tc, mid, _, unbind(config)) and
+ flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
)
}
pragma[nomagic]
-private predicate consCand(Content f, AccessPathFront apf, Configuration config) {
- consCandFwd(f, apf, config) and
- exists(NodeExt n, AccessPathFrontHead apf0 |
- flowCandFwd(n, _, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandRead(n, f, _, apf, config)
+private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(tc, apf, config) and
+ flowCandRead(_, tc, _, _, _, apf, config)
+}
+
+pragma[nomagic]
+private predicate flowCandOut(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathFrontOption returnApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(Node out, boolean allowsFieldFlow |
+ flowCand(out, toReturn, returnApf, apf, config) and
+ flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandIn(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathFrontOption returnApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ flowCand(p, toReturn, returnApf, apf, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandInToReturn(
+ DataFlowCall call, ArgumentNode arg, AccessPathFront returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ flowCandIn(call, arg, true, TAccessPathFrontSome(returnApf), apf, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `flowCand`.
+ */
+pragma[nomagic]
+private predicate flowCandIsReturned(
+ DataFlowCall call, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ flowCandOut(call, ret, toReturn, returnApf, apf, config) and
+ flowCandFwd(ret, true, TAccessPathFrontSome(_), apf, config)
)
}
private newtype TAccessPath =
TNil(DataFlowType t) or
- TConsNil(Content f, DataFlowType t) { consCand(f, TFrontNil(t), _) } or
- TConsCons(Content f1, Content f2, int len) {
- consCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
+ TConsCons(TypedContent tc1, TypedContent tc2, int len) {
+ flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
}
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first two
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
* elements of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -1684,7 +1530,7 @@ private newtype TAccessPath =
abstract private class AccessPath extends TAccessPath {
abstract string toString();
- abstract Content getHead();
+ abstract TypedContent getHead();
abstract int len();
@@ -1695,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
/**
* Holds if this access path has `head` at the front and may be followed by `tail`.
*/
- abstract predicate pop(Content head, AccessPath tail);
+ abstract predicate pop(TypedContent head, AccessPath tail);
}
private class AccessPathNil extends AccessPath, TNil {
@@ -1705,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
override string toString() { result = concat(": " + ppReprType(t)) }
- override Content getHead() { none() }
+ override TypedContent getHead() { none() }
override int len() { result = 0 }
@@ -1713,357 +1559,469 @@ private class AccessPathNil extends AccessPath, TNil {
override AccessPathFront getFront() { result = TFrontNil(t) }
- override predicate pop(Content head, AccessPath tail) { none() }
+ override predicate pop(TypedContent head, AccessPath tail) { none() }
}
abstract private class AccessPathCons extends AccessPath { }
private class AccessPathConsNil extends AccessPathCons, TConsNil {
- private Content f;
+ private TypedContent tc;
private DataFlowType t;
- AccessPathConsNil() { this = TConsNil(f, t) }
+ AccessPathConsNil() { this = TConsNil(tc, t) }
override string toString() {
// The `concat` becomes "" if `ppReprType` has no result.
- result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
+ result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
}
- override Content getHead() { result = f }
+ override TypedContent getHead() { result = tc }
override int len() { result = 1 }
- override DataFlowType getType() { result = f.getContainerType() }
+ override DataFlowType getType() { result = tc.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f) }
+ override AccessPathFront getFront() { result = TFrontHead(tc) }
- override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
+ override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
}
private class AccessPathConsCons extends AccessPathCons, TConsCons {
- private Content f1;
- private Content f2;
+ private TypedContent tc1;
+ private TypedContent tc2;
private int len;
- AccessPathConsCons() { this = TConsCons(f1, f2, len) }
+ AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
override string toString() {
if len = 2
- then result = "[" + f1.toString() + ", " + f2.toString() + "]"
- else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
+ else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
}
- override Content getHead() { result = f1 }
+ override TypedContent getHead() { result = tc1 }
override int len() { result = len }
- override DataFlowType getType() { result = f1.getContainerType() }
+ override DataFlowType getType() { result = tc1.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f1) }
+ override AccessPathFront getFront() { result = TFrontHead(tc1) }
- override predicate pop(Content head, AccessPath tail) {
- head = f1 and
+ override predicate pop(TypedContent head, AccessPath tail) {
+ head = tc1 and
(
- tail = TConsCons(f2, _, len - 1)
+ tail = TConsCons(tc2, _, len - 1)
or
len = 2 and
- tail = TConsNil(f2, _)
+ tail = TConsNil(tc2, _)
)
}
}
-/** Gets the access path obtained by popping `f` from `ap`, if any. */
-private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
+/** Gets the access path obtained by popping `tc` from `ap`, if any. */
+private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
-/** Gets the access path obtained by pushing `f` onto `ap`. */
-private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
+/** Gets the access path obtained by pushing `tc` onto `ap`. */
+private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
+
+private newtype TAccessPathOption =
+ TAccessPathNone() or
+ TAccessPathSome(AccessPath ap)
+
+private class AccessPathOption extends TAccessPathOption {
+ string toString() {
+ this = TAccessPathNone() and result = ""
+ or
+ this = TAccessPathSome(any(AccessPath ap | result = ap.toString()))
+ }
+}
/**
- * Holds if data can flow from a source to `node` with the given `ap`.
+ * Holds if `node` is reachable with access path `ap` from a source in
+ * the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argAp` records the access path of that
+ * argument.
*/
private predicate flowFwd(
- NodeExt node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config
+ Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
) {
- flowFwd0(node, fromArg, apf, ap, config) and
- flowCand(node, _, apf, config)
+ flowFwd0(node, fromArg, argAp, apf, ap, config) and
+ flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
- NodeExt node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config
+ Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
) {
- flowCand(node, _, _, config) and
- config.isSource(node.getNode()) and
+ flowCand(node, _, _, _, config) and
+ config.isSource(node) and
fromArg = false and
- ap = TNil(node.getErasedNodeTypeBound()) and
+ argAp = TAccessPathNone() and
+ ap = TNil(getErasedNodeTypeBound(node)) and
apf = ap.(AccessPathNil).getFront()
or
- flowCand(node, _, _, unbind(config)) and
+ flowCand(node, _, _, _, unbind(config)) and
(
- exists(NodeExt mid |
- flowFwd(mid, fromArg, apf, ap, config) and
- localFlowBigStepExt(mid, node, true, _, config)
+ exists(Node mid |
+ flowFwd(mid, fromArg, argAp, apf, ap, config) and
+ localFlowBigStep(mid, node, true, _, config, _)
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(mid, fromArg, _, nil, config) and
- localFlowBigStepExt(mid, node, false, apf, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(mid, fromArg, argAp, _, nil, config) and
+ localFlowBigStep(mid, node, false, apf, config, _) and
apf = ap.(AccessPathNil).getFront()
)
or
- exists(NodeExt mid |
- flowFwd(mid, _, apf, ap, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
- )
- or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(mid, _, _, nil, config) and
- additionalJumpStepExt(mid, node, config) and
+ exists(Node mid |
+ flowFwd(mid, _, _, apf, ap, config) and
+ jumpStep(mid, node, config) and
fromArg = false and
- ap = TNil(node.getErasedNodeTypeBound()) and
- apf = ap.(AccessPathNil).getFront()
+ argAp = TAccessPathNone()
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowFwd(mid, _, apf, ap, config) and
- flowIntoCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowFwd(mid, false, apf, ap, config) and
- flowOutOfCallableNodeCand2(mid, node, allowsFieldFlow, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(mid, _, _, _, nil, config) and
+ additionalJumpStep(mid, node, config) and
fromArg = false and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- flowFwd(mid, fromArg, apf, ap, config) and
- argumentValueFlowsThrough(mid, node)
- )
- or
- exists(NodeExt mid, AccessPathNil nil, DataFlowType t |
- flowFwd(mid, fromArg, _, nil, config) and
- argumentFlowsThrough(mid, node, t, config) and
- ap = TNil(t) and
+ argAp = TAccessPathNone() and
+ ap = TNil(getErasedNodeTypeBound(node)) and
apf = ap.(AccessPathNil).getFront()
)
)
or
- exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, config) and
- ap = push(f, ap0)
+ // store
+ exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
+ or
+ // read
+ exists(TypedContent tc |
+ flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
+ flowFwdConsCand(tc, apf, ap, config)
)
or
- exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, config) and
- flowConsCandFwd(f, apf, ap, config)
+ // flow into a callable
+ flowFwdIn(_, node, _, _, apf, ap, config) and
+ fromArg = true and
+ if flowCand(node, true, _, apf, config)
+ then argAp = TAccessPathSome(ap)
+ else argAp = TAccessPathNone()
+ or
+ // flow out of a callable
+ exists(DataFlowCall call |
+ flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
+ fromArg = false
+ or
+ exists(AccessPath argAp0 |
+ flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and
+ flowFwdIsEntered(call, fromArg, argAp, argAp0, config)
+ )
)
}
pragma[nomagic]
private predicate flowFwdStore(
- NodeExt node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
- Configuration config
+ Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFront apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- flowFwdStore1(mid, f, node, apf0, apf, config)
+ exists(Node mid, AccessPathFront apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
+ flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
pragma[nomagic]
-private predicate flowFwdStore0(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, Configuration config
+private predicate storeCand(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
+ Configuration config
) {
- storeExtCand2(mid, f, node, config) and
- flowCand(mid, _, apf0, config)
+ storeCand2(mid, tc, node, _, config) and
+ flowCand(mid, _, _, apf0, config) and
+ apf.headUsesContent(tc)
}
pragma[noinline]
-private predicate flowFwdStore1(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, AccessPathFrontHead apf,
+private predicate flowFwdStore0(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
- flowFwdStore0(mid, f, node, apf0, config) and
- consCand(f, apf0, config) and
- apf.headUsesContent(f) and
- flowCand(node, _, apf, unbind(config))
+ storeCand(mid, tc, node, apf0, apf, config) and
+ flowCandConsCand(tc, apf0, config) and
+ flowCand(node, _, _, apf, unbind(config))
+}
+
+pragma[nomagic]
+private predicate flowFwdRead0(
+ Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
+ boolean fromArg, AccessPathOption argAp, Configuration config
+) {
+ flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
+ readCandFwd(node1, tc, apf0, node2, config)
}
pragma[nomagic]
private predicate flowFwdRead(
- NodeExt node, Content f, AccessPath ap0, boolean fromArg, Configuration config
+ Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- readExtCand2(mid, f, node, config) and
- apf0.headUsesContent(f) and
- flowCand(node, _, _, unbind(config))
+ exists(Node mid, TypedContent tc |
+ flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
+ flowCand(node, _, _, apf, unbind(config)) and
+ flowCandConsCand(tc, apf, unbind(config))
)
}
pragma[nomagic]
-private predicate flowConsCandFwd(
- Content f, AccessPathFront apf, AccessPath ap, Configuration config
+private predicate flowFwdConsCand(
+ TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
) {
- exists(NodeExt n |
- flowFwd(n, _, apf, ap, config) and
- flowFwdStore1(n, f, _, apf, _, config)
+ exists(Node n |
+ flowFwd(n, _, _, apf, ap, config) and
+ flowFwdStore0(n, tc, _, apf, _, config)
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdIn(
+ DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ flowFwd(arg, fromArg, argAp, apf, ap, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
+ flowCand(p, _, _, _, unbind(config))
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ flowFwd(ret, fromArg, argAp, apf, ap, config) and
+ flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
+ flowCand(node, _, _, _, unbind(config))
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdOutFromArg(
+ DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
+) {
+ flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `flowFwd`.
+ */
+pragma[nomagic]
+private predicate flowFwdIsEntered(
+ DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
+) {
+ exists(ParameterNode p, AccessPathFront apf |
+ flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and
+ flowCand(p, true, TAccessPathFrontSome(_), apf, config)
)
}
/**
- * Holds if data can flow from a source to `node` with the given `ap` and
- * from there flow to a sink.
+ * Holds if `node` with access path `ap` is part of a path from a source to
+ * a sink in the configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnAp`
+ * records the access path of the returned value.
*/
-private predicate flow(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flow0(node, toReturn, ap, config) and
- flowFwd(node, _, _, ap, config)
+private predicate flow(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flow0(node, toReturn, returnAp, ap, config) and
+ flowFwd(node, _, _, _, ap, config)
}
-private predicate flow0(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flowFwd(node, _, _, ap, config) and
- config.isSink(node.getNode()) and
+private predicate flow0(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flowFwd(node, _, _, _, ap, config) and
+ config.isSink(node) and
toReturn = false and
+ returnAp = TAccessPathNone() and
ap instanceof AccessPathNil
or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flow(mid, toReturn, ap, config)
+ exists(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flow(mid, toReturn, returnAp, ap, config)
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(node, _, _, ap, config) and
- localFlowBigStepExt(node, mid, false, _, config) and
- flow(mid, toReturn, nil, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(node, _, _, _, ap, config) and
+ localFlowBigStep(node, mid, false, _, config, _) and
+ flow(mid, toReturn, returnAp, nil, config) and
ap instanceof AccessPathNil
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flow(mid, _, ap, config) and
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flow(mid, _, _, ap, config) and
+ toReturn = false and
+ returnAp = TAccessPathNone()
+ )
+ or
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(node, _, _, _, ap, config) and
+ additionalJumpStep(node, mid, config) and
+ flow(mid, _, _, nil, config) and
+ toReturn = false and
+ returnAp = TAccessPathNone() and
+ ap instanceof AccessPathNil
+ )
+ or
+ // store
+ exists(TypedContent tc |
+ flowStore(tc, node, toReturn, returnAp, ap, config) and
+ flowConsCand(tc, ap, config)
+ )
+ or
+ // read
+ exists(Node mid, AccessPath ap0 |
+ readFlowFwd(node, _, mid, ap, ap0, config) and
+ flow(mid, toReturn, returnAp, ap0, config)
+ )
+ or
+ // flow into a callable
+ exists(DataFlowCall call |
+ flowIn(call, node, toReturn, returnAp, ap, config) and
toReturn = false
+ or
+ exists(AccessPath returnAp0 |
+ flowInToReturn(call, node, returnAp0, ap, config) and
+ flowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(node, _, _, ap, config) and
- additionalJumpStepExt(node, mid, config) and
- flow(mid, _, nil, config) and
- toReturn = false and
- ap instanceof AccessPathNil
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flow(mid, false, ap, config) and
- toReturn = false and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flow(mid, _, ap, config) and
- toReturn = true and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- argumentValueFlowsThrough(node, mid) and
- flow(mid, toReturn, ap, config)
- )
- or
- exists(NodeExt mid, AccessPathNil nil |
- argumentFlowsThrough(node, mid, _, config) and
- flow(mid, toReturn, nil, config) and
- ap instanceof AccessPathNil and
- flowFwd(node, _, _, ap, config)
- )
- or
- exists(Content f |
- flowStore(f, node, toReturn, ap, config) and
- flowConsCand(f, ap, config)
- )
- or
- exists(NodeExt mid, AccessPath ap0 |
- readFwd(node, _, mid, ap, ap0, config) and
- flow(mid, toReturn, ap0, config)
- )
+ // flow out of a callable
+ flowOut(_, node, _, _, ap, config) and
+ toReturn = true and
+ if flowFwd(node, true, TAccessPathSome(_), _, ap, config)
+ then returnAp = TAccessPathSome(ap)
+ else returnAp = TAccessPathNone()
}
pragma[nomagic]
-private predicate storeFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate storeFlowFwd(
+ Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- storeExtCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, config) and
- ap0 = push(f, ap)
+ storeCand2(node1, tc, node2, _, config) and
+ flowFwdStore(node2, tc, ap, _, _, _, config) and
+ ap0 = push(tc, ap)
}
pragma[nomagic]
private predicate flowStore(
- Content f, NodeExt node, boolean toReturn, AccessPath ap, Configuration config
+ TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
) {
- exists(NodeExt mid, AccessPath ap0 |
- storeFwd(node, f, mid, ap, ap0, config) and
- flow(mid, toReturn, ap0, config)
+ exists(Node mid, AccessPath ap0 |
+ storeFlowFwd(node, tc, mid, ap, ap0, config) and
+ flow(mid, toReturn, returnAp, ap0, config)
)
}
pragma[nomagic]
-private predicate readFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate readFlowFwd(
+ Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- readExtCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, config) and
- ap0 = pop(f, ap) and
- flowConsCandFwd(f, _, ap0, unbind(config))
+ exists(AccessPathFrontHead apf |
+ readCandFwd(node1, tc, apf, node2, config) and
+ flowFwdRead(node2, apf, ap, _, _, _, config) and
+ ap0 = pop(tc, ap) and
+ flowFwdConsCand(tc, _, ap0, unbind(config))
+ )
}
pragma[nomagic]
-private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
- exists(NodeExt n, NodeExt mid |
- flow(mid, _, ap, config) and
- readFwd(n, f, mid, _, ap, config)
+private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
+ exists(Node n, Node mid |
+ flow(mid, _, _, ap, config) and
+ readFlowFwd(n, tc, mid, _, ap, config)
+ )
+}
+
+pragma[nomagic]
+private predicate flowOut(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(Node out, boolean allowsFieldFlow |
+ flow(out, toReturn, returnAp, ap, config) and
+ flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config)
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowIn(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ flow(p, toReturn, returnAp, ap, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowInToReturn(
+ DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config
+) {
+ flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `flow`.
+ */
+pragma[nomagic]
+private predicate flowIsReturned(
+ DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ flowOut(call, ret, toReturn, returnAp, ap, config) and
+ flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
)
}
bindingset[conf, result]
private Configuration unbind(Configuration conf) { result >= conf and result <= conf }
-private predicate flow(Node n, Configuration config) { flow(TNormalNode(n), _, _, config) }
+private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) }
+
+pragma[noinline]
+private predicate parameterFlow(
+ ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config
+) {
+ flow(p, true, _, ap, config) and
+ c = p.getEnclosingCallable()
+}
private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParameterNode p, AccessPath ap) {
- exists(ReturnNodeExt ret, Configuration config | flow(TNormalNode(p), true, ap, config) |
- exists(Summary summary |
- parameterFlowReturn(p, ret, _, _, _, summary, config) and
- flow(ret, unbind(config))
- |
- // taint through
- summary = TSummaryTaint() and
- ap instanceof AccessPathNil
- or
- // taint setter
- summary = TSummaryTaintStore(_) and
- ap instanceof AccessPathNil
- or
- // taint getter
- summary = TSummaryReadTaint(ap.(AccessPathConsNil).getHead())
- )
- or
- exists(ContentOption contentIn |
- parameterValueFlowReturn(p, ret, _, contentIn, _) and
- flow(ret, unbind(config))
- |
- // value through/setter
- contentIn = TContentNone()
- or
- // value getter (+ setter)
- contentIn = TContentSome(ap.getHead())
- )
+ exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 |
+ parameterFlow(p, ap, ret.getEnclosingCallable(), config) and
+ flow(ret, true, TAccessPathSome(_), ap0, config) and
+ flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config)
)
}
@@ -2113,7 +2071,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, cc, sc, ap) and
config = mid.getConfiguration() and
- flow(TNormalNode(node), _, ap, unbind(config))
+ flow(node, _, _, ap, unbind(config))
)
} or
TPathNodeSink(Node node, Configuration config) {
@@ -2164,14 +2122,31 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
+ private predicate isHidden() {
+ nodeIsHidden(this.getNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ }
+
+ private PathNode getASuccessorIfHidden() {
+ this.isHidden() and
+ result = this.(PathNodeImpl).getASuccessorImpl()
+ }
+
/** Gets a successor of this node, if any. */
- PathNode getASuccessor() { none() }
+ final PathNode getASuccessor() {
+ result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
+ not this.isHidden() and
+ not result.isHidden()
+ }
/** Holds if this node is a source. */
predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
+ abstract PathNode getASuccessorImpl();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2246,7 +2221,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
result.getConfiguration() = unbind(this.getConfiguration())
}
- override PathNodeImpl getASuccessor() {
+ override PathNodeImpl getASuccessorImpl() {
// an intermediate step to another intermediate node
result = getSuccMid()
or
@@ -2283,7 +2258,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNode getASuccessor() { none() }
+ override PathNode getASuccessorImpl() { none() }
override predicate isSource() { config.isSource(node) }
}
@@ -2293,17 +2268,18 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
* a callable is recorded by `cc`.
*/
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
- exists(
- AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
- LocalCallContext localCC
- |
- pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
- localCC = getLocalCallContext(cc, enclosing)
+ exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
+ midnode = mid.getNode() and
+ conf = mid.getConfiguration() and
+ cc = mid.getCallContext() and
+ sc = mid.getSummaryCtx() and
+ localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
ap = ap0
or
- localFlowBigStep(midnode, node, false, ap.(AccessPathNil).getType(), conf, localCC) and
+ localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -2318,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
mid.getAp() instanceof AccessPathNil and
ap = TNil(getErasedNodeTypeBound(node))
or
- exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
+ exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2332,44 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
}
pragma[nomagic]
-private predicate pathIntoLocalStep(
- PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
- AccessPath ap0, Configuration conf
-) {
- midnode = mid.getNode() and
- cc = mid.getCallContext() and
- conf = mid.getConfiguration() and
- localFlowBigStep(midnode, _, _, _, conf, _) and
- enclosing = midnode.getEnclosingCallable() and
- sc = mid.getSummaryCtx() and
- ap0 = mid.getAp()
-}
-
-pragma[nomagic]
-private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
- readDirect(node1, f, node2) and
+private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ readCandFwd(node1, tc, _, node2, config) and
flow(node2, config)
}
pragma[nomagic]
-private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
+private predicate pathReadStep(
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+) {
ap0 = mid.getAp() and
- readCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
-private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- storeDirect(node1, f, node2) and
+private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ storeCand2(node1, tc, node2, _, config) and
flow(node2, config)
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
@@ -2399,11 +2363,11 @@ private predicate pathOutOfCallable1(
}
pragma[noinline]
-private Node getAnOutNodeCand(
+private Node getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config
) {
result = kind.getAnOutNode(call) and
- flow(TNormalNode(result), _, ap, config)
+ flow(result, _, _, ap, config)
}
/**
@@ -2415,7 +2379,7 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config |
pathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = getAnOutNodeCand(kind, call, ap, config)
+ out = getAnOutNodeFlow(kind, call, ap, config)
)
}
@@ -2439,7 +2403,7 @@ private predicate parameterCand(
DataFlowCallable callable, int i, AccessPath ap, Configuration config
) {
exists(ParameterNode p |
- flow(TNormalNode(p), _, ap, config) and
+ flow(p, _, _, ap, config) and
p.isParameterOf(callable, i)
)
}
@@ -2514,7 +2478,7 @@ pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
exists(DataFlowCall call, ReturnKindExt kind |
pathThroughCallable0(call, mid, kind, cc, ap) and
- out = getAnOutNodeCand(kind, call, ap, mid.getConfiguration())
+ out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration())
)
}
@@ -2555,10 +2519,7 @@ private module FlowExploration {
viableParamArg(_, node2, node1)
or
// flow out of a callable
- exists(DataFlowCall call, ReturnKindExt kind |
- getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = kind.getAnOutNode(call)
- )
+ viableReturnPosOut(_, getReturnPosition(node1), node2)
|
c1 = node1.getEnclosingCallable() and
c2 = node2.getEnclosingCallable() and
@@ -2603,10 +2564,10 @@ private module FlowExploration {
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
- TPartialCons(Content f, int len) { len in [1 .. 5] }
+ TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
* element of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -2615,7 +2576,7 @@ private module FlowExploration {
private class PartialAccessPath extends TPartialAccessPath {
abstract string toString();
- Content getHead() { this = TPartialCons(result, _) }
+ TypedContent getHead() { this = TPartialCons(result, _) }
int len() {
this = TPartialNil(_) and result = 0
@@ -2626,7 +2587,7 @@ private module FlowExploration {
DataFlowType getType() {
this = TPartialNil(result)
or
- exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
+ exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
@@ -2644,15 +2605,15 @@ private module FlowExploration {
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
override string toString() {
- exists(Content f, int len | this = TPartialCons(f, len) |
+ exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
if len = 1
- then result = "[" + f.toString() + "]"
- else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc.toString() + "]"
+ else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
override AccessPathFront getFront() {
- exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
+ exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
}
}
@@ -2828,11 +2789,12 @@ private module FlowExploration {
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
- exists(PartialAccessPath ap0, Content f |
- partialPathReadStep(mid, ap0, f, node, cc, config) and
+ exists(PartialAccessPath ap0, TypedContent tc |
+ partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, f, ap0, config)
+ apConsFwd(ap, tc, ap0, config) and
+ compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -2851,35 +2813,42 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
+ PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
+ PartialAccessPath ap2
) {
- ap1 = mid.getAp() and
- storeDirect(mid.getNode(), f, node) and
- ap2.getHead() = f and
- ap2.len() = unbindInt(ap1.len() + 1) and
- compatibleTypes(ap1.getType(), f.getType())
+ exists(Node midNode, DataFlowType contentType |
+ midNode = mid.getNode() and
+ ap1 = mid.getAp() and
+ store(midNode, tc, node, contentType) and
+ ap2.getHead() = tc and
+ ap2.len() = unbindInt(ap1.len() + 1) and
+ compatibleTypes(ap1.getType(), contentType)
+ )
}
pragma[nomagic]
private predicate apConsFwd(
- PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
+ PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
- partialPathStoreStep(mid, ap1, f, _, ap2) and
+ partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
+ PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
- ap = mid.getAp() and
- readStep(mid.getNode(), f, node) and
- ap.getHead() = f and
- config = mid.getConfiguration() and
- cc = mid.getCallContext()
+ exists(Node midNode |
+ midNode = mid.getNode() and
+ ap = mid.getAp() and
+ read(midNode, tc.getContent(), node) and
+ ap.getHead() = tc and
+ config = mid.getConfiguration() and
+ cc = mid.getCallContext()
+ )
}
private predicate partialPathOutOfCallable0(
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
index a1daeb66411..1aeedf717f7 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
*/
predicate isBarrier(Node node) { none() }
- /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */
- deprecated predicate isBarrierEdge(Node node1, Node node2) { none() }
-
/** Holds if data flow into `node` is prohibited. */
predicate isBarrierIn(Node node) { none() }
@@ -251,15 +248,11 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
-pragma[noinline]
-private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
- viableCallable(call) = result.getCallable() and
- kind = result.getKind()
-}
-
/**
- * Holds if `node` is reachable from a source in the given configuration
- * taking simple call contexts into consideration.
+ * Holds if `node` is reachable from a source in the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call.
*/
private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) {
not fullBarrier(node, config) and
@@ -293,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
exists(Node mid |
useFieldFlow(config) and
nodeCandFwd1(mid, fromArg, config) and
- storeDirect(mid, _, node) and
+ store(mid, _, node, _) and
not outBarrier(mid, config)
)
or
// read
- exists(Content f |
- nodeCandFwd1Read(f, node, fromArg, config) and
- storeCandFwd1(f, config) and
+ exists(Content c |
+ nodeCandFwd1Read(c, node, fromArg, config) and
+ nodeCandFwd1IsStored(c, config) and
not inBarrier(node, config)
)
or
@@ -317,13 +310,35 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
fromArg = false
or
nodeCandFwd1OutFromArg(call, node, config) and
- flowOutCandFwd1(call, fromArg, config)
+ nodeCandFwd1IsEntered(call, fromArg, config)
)
)
}
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
+pragma[nomagic]
+private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
+ exists(Node mid |
+ nodeCandFwd1(mid, fromArg, config) and
+ read(mid, c, node)
+ )
+}
+
+/**
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
+ exists(Node mid, Node node, TypedContent tc |
+ not fullBarrier(node, config) and
+ useFieldFlow(config) and
+ nodeCandFwd1(mid, config) and
+ store(mid, tc, node, _) and
+ c = tc.getContent()
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1ReturnPosition(
ReturnPosition pos, boolean fromArg, Configuration config
@@ -335,43 +350,10 @@ private predicate nodeCandFwd1ReturnPosition(
}
pragma[nomagic]
-private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
- exists(Node mid |
- nodeCandFwd1(mid, fromArg, config) and
- readDirect(mid, f, node)
- )
-}
-
-/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
- */
-pragma[nomagic]
-private predicate storeCandFwd1(Content f, Configuration config) {
- exists(Node mid, Node node |
- not fullBarrier(node, config) and
- useFieldFlow(config) and
- nodeCandFwd1(mid, config) and
- storeDirect(mid, f, node)
- )
-}
-
-pragma[nomagic]
-private predicate nodeCandFwd1ReturnKind(
- DataFlowCall call, ReturnKindExt kind, boolean fromArg, Configuration config
-) {
+private predicate nodeCandFwd1Out(DataFlowCall call, Node out, boolean fromArg, Configuration config) {
exists(ReturnPosition pos |
nodeCandFwd1ReturnPosition(pos, fromArg, config) and
- pos = viableReturnPos(call, kind)
- )
-}
-
-pragma[nomagic]
-private predicate nodeCandFwd1Out(
- DataFlowCall call, Node node, boolean fromArg, Configuration config
-) {
- exists(ReturnKindExt kind |
- nodeCandFwd1ReturnKind(call, kind, fromArg, config) and
- node = kind.getAnOutNode(call)
+ viableReturnPosOut(call, pos, out)
)
}
@@ -384,7 +366,7 @@ private predicate nodeCandFwd1OutFromArg(DataFlowCall call, Node node, Configura
* Holds if an argument to `call` is reached in the flow covered by `nodeCandFwd1`.
*/
pragma[nomagic]
-private predicate flowOutCandFwd1(DataFlowCall call, boolean fromArg, Configuration config) {
+private predicate nodeCandFwd1IsEntered(DataFlowCall call, boolean fromArg, Configuration config) {
exists(ArgumentNode arg |
nodeCandFwd1(arg, fromArg, config) and
viableParamArg(call, _, arg)
@@ -395,8 +377,11 @@ bindingset[result, b]
private boolean unbindBool(boolean b) { result != b.booleanNot() }
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is part of a path from a source to a sink in the
+ * configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink.
*/
pragma[nomagic]
private predicate nodeCand1(Node node, boolean toReturn, Configuration config) {
@@ -433,81 +418,86 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
)
or
// store
- exists(Content f |
- nodeCand1Store(f, node, toReturn, config) and
- readCand1(f, config)
+ exists(Content c |
+ nodeCand1Store(c, node, toReturn, config) and
+ nodeCand1IsRead(c, config)
)
or
// read
- exists(Node mid, Content f |
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ exists(Node mid, Content c |
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, toReturn, config)
)
or
// flow into a callable
exists(DataFlowCall call |
- nodeCand1Arg(call, node, false, config) and
+ nodeCand1In(call, node, false, config) and
toReturn = false
or
- nodeCand1ArgToReturn(call, node, config) and
- flowInCand1(call, toReturn, config)
+ nodeCand1InToReturn(call, node, config) and
+ nodeCand1IsReturned(call, toReturn, config)
)
or
// flow out of a callable
exists(ReturnPosition pos |
- nodeCand1ReturnPosition(pos, config) and
+ nodeCand1Out(pos, config) and
getReturnPosition(node) = pos and
toReturn = true
)
}
-pragma[nomagic]
-private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _, config) }
-
-pragma[nomagic]
-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`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate readCand1(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content c, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd1(node, unbind(config)) and
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, _, config)
)
}
pragma[nomagic]
-private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
- exists(Node mid |
+private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
+ exists(Node mid, TypedContent tc |
nodeCand1(mid, toReturn, config) and
- storeCandFwd1(f, unbind(config)) and
- storeDirect(node, f, mid)
+ nodeCandFwd1IsStored(c, unbind(config)) and
+ store(node, tc, mid, _) and
+ c = tc.getContent()
)
}
/**
- * Holds if `f` is the target of both a read and a store in the flow covered
+ * Holds if `c` is the target of both a read and a store in the flow covered
* by `nodeCand1`.
*/
-private predicate readStoreCand1(Content f, Configuration conf) {
- readCand1(f, conf) and
- nodeCand1Store(f, _, _, conf)
+private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
+ nodeCand1IsRead(c, conf) and
+ nodeCand1Store(c, _, _, conf)
}
pragma[nomagic]
-private predicate viableParamArgCandFwd1(
+private predicate viableReturnPosOutNodeCandFwd1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+) {
+ nodeCandFwd1ReturnPosition(pos, _, config) and
+ viableReturnPosOut(call, pos, out)
+}
+
+pragma[nomagic]
+private predicate nodeCand1Out(ReturnPosition pos, Configuration config) {
+ exists(DataFlowCall call, Node out |
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, config)
+ )
+}
+
+pragma[nomagic]
+private predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
) {
viableParamArg(call, p, arg) and
@@ -515,32 +505,35 @@ private predicate viableParamArgCandFwd1(
}
pragma[nomagic]
-private predicate nodeCand1Arg(
+private predicate nodeCand1In(
DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config
) {
exists(ParameterNode p |
nodeCand1(p, toReturn, config) and
- viableParamArgCandFwd1(call, p, arg, config)
+ viableParamArgNodeCandFwd1(call, p, arg, config)
)
}
pragma[nomagic]
-private predicate nodeCand1ArgToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
- nodeCand1Arg(call, arg, true, config)
+private predicate nodeCand1InToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
+ nodeCand1In(call, arg, true, config)
}
/**
* Holds if an output from `call` is reached in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate flowInCand1(DataFlowCall call, boolean toReturn, Configuration config) {
+private predicate nodeCand1IsReturned(DataFlowCall call, boolean toReturn, Configuration config) {
exists(Node out |
nodeCand1(out, toReturn, config) and
nodeCandFwd1OutFromArg(call, out, config)
)
}
-private predicate throughFlowNodeCand(Node node, Configuration config) {
+pragma[nomagic]
+private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _, config) }
+
+private predicate throughFlowNodeCand1(Node node, Configuration config) {
nodeCand1(node, true, config) and
not fullBarrier(node, config) and
not inBarrier(node, config) and
@@ -549,11 +542,11 @@ private predicate throughFlowNodeCand(Node node, Configuration config) {
/** Holds if flow may return from `callable`. */
pragma[nomagic]
-private predicate returnFlowCallableCand(
+private predicate returnFlowCallableNodeCand1(
DataFlowCallable callable, ReturnKindExt kind, Configuration config
) {
exists(ReturnNodeExt ret |
- throughFlowNodeCand(ret, config) and
+ throughFlowNodeCand1(ret, config) and
callable = ret.getEnclosingCallable() and
kind = ret.getKind()
)
@@ -563,10 +556,10 @@ private predicate returnFlowCallableCand(
* Holds if flow may enter through `p` and reach a return node making `p` a
* candidate for the origin of a summary.
*/
-private predicate parameterThroughFlowCand(ParameterNode p, Configuration config) {
+private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration config) {
exists(ReturnKindExt kind |
- throughFlowNodeCand(p, config) and
- returnFlowCallableCand(p.getEnclosingCallable(), kind, config) and
+ throughFlowNodeCand1(p, config) and
+ returnFlowCallableNodeCand1(p.getEnclosingCallable(), kind, config) and
// we don't expect a parameter to return stored in itself
not exists(int pos |
kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos)
@@ -575,420 +568,77 @@ private predicate parameterThroughFlowCand(ParameterNode p, Configuration config
}
pragma[nomagic]
-private predicate store(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
- nodeCand1(n2, unbind(config)) and
- (
- storeDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentSome(f), n2)
- )
-}
-
-pragma[nomagic]
-private predicate read(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
- nodeCand1(n2, unbind(config)) and
- (
- readDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentSome(f), TContentNone(), n2)
- )
-}
-
-/**
- * Holds if `p` can flow to `node` in the same callable with `summary`
- * representing the flow path. The type of the tracked object is `t2`, and if
- * the summary includes a store step, `t1` is the tracked type just prior to the
- * store, that is, the type of the stored object, otherwise `t1` is equal to `t2`.
- */
-pragma[nomagic]
-private predicate parameterFlow(
- ParameterNode p, Node node, DataFlowType t1, DataFlowType t2, Summary summary,
- Configuration config
-) {
- parameterThroughFlowCand(p, config) and
- p = node and
- t1 = getErasedNodeTypeBound(node) and
- t1 = t2 and
- summary = TSummaryVal()
- or
- throughFlowNodeCand(node, unbind(config)) and
- (
- exists(Node mid |
- parameterFlow(p, mid, t1, t2, summary, config) and
- localFlowStep(mid, node, config) and
- compatibleTypes(t2, getErasedNodeTypeBound(node))
- )
- or
- exists(Node mid, Summary midsum |
- parameterFlow(p, mid, _, _, midsum, config) and
- additionalLocalFlowStep(mid, node, config) and
- t1 = getErasedNodeTypeBound(node) and
- t1 = t2 and
- summary = midsum.additionalStep()
- )
- or
- // read step
- exists(Node mid, Content f, Summary midsum |
- parameterFlow(p, mid, _, _, midsum, config) and
- read(mid, f, node, config) and
- summary = midsum.readStep(f) and
- t1 = f.getType() and
- t1 = t2
- )
- or
- // store step
- exists(Node mid, Content f, Summary midsum |
- parameterFlow(p, mid, t1, /* t1 */ _, midsum, config) and
- store(mid, f, node, config) and
- summary = midsum.storeStep(f) and
- compatibleTypes(t1, f.getType()) and
- t2 = f.getContainerType()
- )
- or
- // value flow through a callable
- exists(Node arg |
- parameterFlow(p, arg, t1, t2, summary, config) and
- argumentValueFlowsThrough(_, arg, TContentNone(), TContentNone(), node) and
- compatibleTypes(t2, getErasedNodeTypeBound(node))
- )
- or
- // flow through a callable
- exists(Node arg, Summary s1, Summary s2 |
- parameterFlow(p, arg, _, _, s1, config) and
- argumentFlowsThrough(arg, node, t1, t2, s2, config) and
- summary = s1.compose(s2)
- )
- )
-}
-
-private predicate viableParamArgCand(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
-) {
- viableParamArg(call, p, arg) and
- nodeCand1(arg, unbind(config)) and
- nodeCand1(p, config) and
- not outBarrier(arg, config) and
- not inBarrier(p, config)
-}
-
-pragma[nomagic]
-private predicate parameterFlowReturn(
- ParameterNode p, ReturnNodeExt ret, ReturnKindExt kind, DataFlowType t1, DataFlowType t2,
- Summary summary, Configuration config
-) {
- parameterFlow(p, ret, t1, t2, summary, config) and
- kind = ret.getKind() and
- not summary.isPartial() and
- not exists(int pos | kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos))
-}
-
-pragma[nomagic]
-private predicate argumentFlowsThrough0(
- DataFlowCall call, ArgumentNode arg, ReturnKindExt kind, DataFlowType t1, DataFlowType t2,
- Summary summary, Configuration config
-) {
- exists(ParameterNode p |
- viableParamArgCand(call, p, arg, config) and
- parameterFlowReturn(p, _, kind, t1, t2, summary, config)
- )
-}
-
-/**
- * Holds if data can flow from `arg` to `out` through a call with `summary`
- * representing the flow path. The type of the tracked object is `t2`, and if
- * the summary includes a store step, `t1` is the tracked type just prior to the
- * store, that is, the type of the stored object, otherwise `t1` is equal to `t2`.
- */
-private predicate argumentFlowsThrough(
- ArgumentNode arg, Node out, DataFlowType t1, DataFlowType t2, Summary summary,
- Configuration config
-) {
- nodeCand1(out, unbind(config)) and
- not inBarrier(out, config) and
- compatibleTypes(t2, getErasedNodeTypeBound(out)) and
- exists(DataFlowCall call, ReturnKindExt kind |
- argumentFlowsThrough0(call, arg, kind, t1, t2, summary, config) and
- out = kind.getAnOutNode(call)
- )
-}
-
-pragma[noinline]
-private predicate readStoreNode(
- DataFlowCall call, ArgumentNode arg, Content f1, Configuration config
-) {
- exists(Content f2, Node out |
- argumentValueFlowsThrough(call, arg, TContentSome(f1), TContentSome(f2), out) and
- nodeCand1(out, config) and
- readStoreCand1(f2, unbind(config))
- )
-}
-
-private newtype TNodeExt =
- TNormalNode(Node node) { nodeCand1(node, _) } or
- TReadStoreNode(DataFlowCall call, ArgumentNode arg, Content f1, Configuration config) {
- nodeCand1(arg, config) and
- readStoreNode(call, arg, f1, config) and
- readStoreCand1(f1, unbind(config))
- } or
- TReadTaintNode(ArgumentNode arg, Content f, Configuration config) {
- argumentFlowsThrough(arg, _, _, _, TSummaryReadTaint(f), config)
- } or
- TTaintStoreNode(ArgumentNode arg, DataFlowType t, Configuration config) {
- argumentFlowsThrough(arg, _, t, _, TSummaryTaintStore(_), config)
- }
-
-/**
- * An extended data flow node. Either a normal node, or an intermediate node
- * used to split up a summarized flow steps.
- *
- * This is purely an internal implementation detail.
- */
-abstract private class NodeExt extends TNodeExt {
- /** Gets the underlying (normal) node, if any. */
- abstract Node getNode();
-
- abstract DataFlowType getErasedNodeTypeBound();
-
- abstract DataFlowCallable getEnclosingCallable();
-
- abstract predicate isCand1(Configuration config);
-
- abstract string toString();
-
- abstract predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- );
-}
-
-/** A `Node` at which a cast can occur such that the type should be checked. */
-abstract private class CastingNodeExt extends NodeExt { }
-
-private class NormalNodeExt extends NodeExt, TNormalNode {
- override Node getNode() { this = TNormalNode(result) }
-
- override DataFlowType getErasedNodeTypeBound() {
- result = getErasedRepr(this.getNode().getTypeBound())
- }
-
- override DataFlowCallable getEnclosingCallable() {
- result = this.getNode().getEnclosingCallable()
- }
-
- override predicate isCand1(Configuration config) { nodeCand1(this.getNode(), config) }
-
- override string toString() { result = this.getNode().toString() }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class NormalCastingNodeExt extends CastingNodeExt, NormalNodeExt {
- NormalCastingNodeExt() { this.getNode() instanceof CastingNode }
-}
-
-private class ReadStoreNodeExt extends CastingNodeExt, TReadStoreNode {
- private DataFlowCall call;
- private ArgumentNode arg;
- private Content f1;
- private Configuration config0;
-
- ReadStoreNodeExt() { this = TReadStoreNode(call, arg, f1, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = f1.getType() }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = "(inside) " + call.toString() + " [read " + f1 + "]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class ReadTaintNode extends NodeExt, TReadTaintNode {
- private ArgumentNode arg;
- private Content f;
- private Configuration config0;
-
- ReadTaintNode() { this = TReadTaintNode(arg, f, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = f.getType() }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = arg.toString() + " [read taint " + f + "]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class TaintStoreNode extends NodeExt, TTaintStoreNode {
- private ArgumentNode arg;
- private DataFlowType t;
- private Configuration config0;
-
- TaintStoreNode() { this = TTaintStoreNode(arg, t, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = t }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = arg.toString() + " [taint store]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private predicate additionalLocalFlowStepExt(
- NodeExt node1, NodeExt node2, DataFlowType t, Configuration config
-) {
- exists(ArgumentNode arg, Content f |
- node1 = TReadTaintNode(arg, f, config) and
- argumentFlowsThrough(arg, node2.getNode(), _, t, TSummaryReadTaint(f), config)
- )
- or
- node2 = TTaintStoreNode(node1.getNode(), t, config)
-}
-
-pragma[nomagic]
-private predicate readExt(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- read(node1.getNode(), f, node2.getNode(), config)
- or
- node2 = TReadStoreNode(_, node1.getNode(), f, config)
- or
- node2 = TReadTaintNode(node1.getNode(), f, config)
-}
-
-pragma[nomagic]
-private predicate storeExt(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- store(node1.getNode(), f, node2.getNode(), config)
- or
- exists(DataFlowCall call, ArgumentNode arg, Content f1, Node n2 |
- node1 = TReadStoreNode(call, arg, f1, config) and
- n2 = node2.getNode() and
- argumentValueFlowsThrough(call, arg, TContentSome(f1), TContentSome(f), n2) and
+private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
+ exists(TypedContent tc |
+ nodeCand1IsReadAndStored(c, config) and
nodeCand1(n2, unbind(config)) and
- readStoreCand1(f, unbind(config))
- )
- or
- exists(ArgumentNode arg, DataFlowType t |
- node1 = TTaintStoreNode(arg, t, config) and
- argumentFlowsThrough(arg, node2.getNode(), t, _, TSummaryTaintStore(f), config)
+ store(n1, tc, n2, _) and
+ c = tc.getContent()
)
}
-private predicate jumpStepExt(NodeExt node1, NodeExt node2, Configuration config) {
- jumpStep(node1.getNode(), node2.getNode(), config)
-}
-
-private predicate additionalJumpStepExt(NodeExt node1, NodeExt node2, Configuration config) {
- additionalJumpStep(node1.getNode(), node2.getNode(), config)
-}
-
-private predicate argumentValueFlowsThrough(NodeExt node1, NodeExt node2) {
- argumentValueFlowsThrough(_, node1.getNode(), TContentNone(), TContentNone(), node2.getNode())
-}
-
-private predicate argumentFlowsThrough(
- NodeExt arg, NodeExt out, DataFlowType t, Configuration config
-) {
- argumentFlowsThrough(arg.getNode(), out.getNode(), _, t, TSummaryTaint(), config)
-}
-
-/**
- * Holds if data can flow from `node1` to `node2` in one local step or a step
- * through a callable.
- */
-pragma[noinline]
-private predicate localFlowStepOrFlowThroughCallable(
- NodeExt node1, NodeExt node2, Configuration config
-) {
- exists(Node n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode()
- |
- nodeCand1(n1, config) and
- localFlowStep(n1, n2, config)
- or
- nodeCand1(n1, config) and
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentNone(), n2)
- )
-}
-
-/**
- * Holds if data can flow from `node1` to `node2` in one local step or a step
- * through a callable, in both cases using an additional flow step from the
- * configuration.
- */
-pragma[noinline]
-private predicate additionalLocalFlowStepOrFlowThroughCallable(
- NodeExt node1, NodeExt node2, Configuration config
-) {
- exists(Node n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode()
- |
- nodeCand1(n1, config) and
- additionalLocalFlowStep(n1, n2, config)
- or
- argumentFlowsThrough(n1, n2, _, _, TSummaryTaint(), config)
- )
- or
- additionalLocalFlowStepExt(node1, node2, _, config)
+pragma[nomagic]
+private predicate read(Node n1, Content c, Node n2, Configuration config) {
+ nodeCand1IsReadAndStored(c, config) and
+ nodeCand1(n2, unbind(config)) and
+ read(n1, c, n2)
}
pragma[noinline]
-private ReturnPosition getReturnPosition1(ReturnNodeExt node, Configuration config) {
- result = getReturnPosition(node) and
- nodeCand1(node, config)
+private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ localFlowStep(node1, node2, config)
+}
+
+pragma[noinline]
+private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ additionalLocalFlowStep(node1, node2, config)
+}
+
+pragma[nomagic]
+private predicate viableReturnPosOutNodeCand1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+) {
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, config)
}
/**
- * Holds if data can flow out of a callable from `node1` to `node2`, either
+ * Holds if data can flow out of `call` from `ret` to `out`, 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 flowOutOfCallableNodeCand1(ReturnNodeExt node1, Node node2, Configuration config) {
- nodeCand1(node2, config) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- exists(DataFlowCall call, ReturnKindExt kind |
- getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
- node2 = kind.getAnOutNode(call)
- )
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand1(
+ DataFlowCall call, ReturnNodeExt ret, Node out, Configuration config
+) {
+ viableReturnPosOutNodeCand1(call, getReturnPosition(ret), out, config) and
+ nodeCand1(ret, config) and
+ not outBarrier(ret, config) and
+ not inBarrier(out, config)
+}
+
+pragma[nomagic]
+private predicate viableParamArgNodeCand1(
+ DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+) {
+ viableParamArgNodeCandFwd1(call, p, arg, config) and
+ nodeCand1(arg, config)
}
/**
- * Holds if data can flow into a callable and that this step is part of a
+ * Holds if data can flow into `call` and that this step is part of a
* path from a source to a sink.
*/
-private predicate flowIntoCallableNodeCand1(
- ArgumentNode node1, ParameterNode node2, Configuration config
+pragma[nomagic]
+private predicate flowIntoCallNodeCand1(
+ DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config
) {
- viableParamArgCand(_, node2, node1, config)
+ viableParamArgNodeCand1(call, p, arg, config) and
+ nodeCand1(p, config) and
+ not outBarrier(arg, config) and
+ not inBarrier(p, config)
}
/**
@@ -999,7 +649,7 @@ private predicate flowIntoCallableNodeCand1(
private int branch(Node n1, Configuration conf) {
result =
strictcount(Node n |
- flowOutOfCallableNodeCand1(n1, n, conf) or flowIntoCallableNodeCand1(n1, n, conf)
+ flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1011,269 +661,391 @@ private int branch(Node n1, Configuration conf) {
private int join(Node n2, Configuration conf) {
result =
strictcount(Node n |
- flowOutOfCallableNodeCand1(n, n2, conf) or flowIntoCallableNodeCand1(n, n2, conf)
+ flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
/**
- * Holds if data can flow out of a callable from `node1` to `node2`, either
+ * Holds if data can flow out of `call` from `ret` to `out`, 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. The
* `allowsFieldFlow` flag indicates whether the branching is within the limit
* specified by the configuration.
*/
-private predicate flowOutOfCallableNodeCand1(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand1(
+ DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config
) {
- exists(ReturnNodeExt n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode() and
- flowOutOfCallableNodeCand1(n1, n2, config) and
- exists(int b, int j |
- b = branch(n1, config) and
- j = join(n2, config) and
- if b.minimum(j) <= config.fieldFlowBranchLimit()
- then allowsFieldFlow = true
- else allowsFieldFlow = false
- )
+ flowOutOfCallNodeCand1(call, ret, out, config) and
+ exists(int b, int j |
+ b = branch(ret, config) and
+ j = join(out, config) and
+ if b.minimum(j) <= config.fieldFlowBranchLimit()
+ then allowsFieldFlow = true
+ else allowsFieldFlow = false
)
}
/**
- * Holds if data can flow into a callable and that this step is part of a
+ * Holds if data can flow into `call` and that this step is part of a
* path from a source to a sink. The `allowsFieldFlow` flag indicates whether
* the branching is within the limit specified by the configuration.
*/
-private predicate flowIntoCallableNodeCand1(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+pragma[nomagic]
+private predicate flowIntoCallNodeCand1(
+ DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow,
+ Configuration config
) {
- exists(ArgumentNode n1, ParameterNode n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode() and
- flowIntoCallableNodeCand1(n1, n2, config) and
- exists(int b, int j |
- b = branch(n1, config) and
- j = join(n2, config) and
- if b.minimum(j) <= config.fieldFlowBranchLimit()
- then allowsFieldFlow = true
- else allowsFieldFlow = false
- )
+ flowIntoCallNodeCand1(call, arg, p, config) and
+ exists(int b, int j |
+ b = branch(arg, config) and
+ j = join(p, config) and
+ if b.minimum(j) <= config.fieldFlowBranchLimit()
+ then allowsFieldFlow = true
+ else allowsFieldFlow = false
)
}
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is reachable from a source in the configuration `config`.
+ * The Boolean `stored` records whether the tracked value is stored into a
+ * field of `node`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argStored` records whether the tracked
+ * value was stored into a field of the argument.
*/
-private predicate nodeCandFwd2(NodeExt node, boolean fromArg, boolean stored, Configuration config) {
- nodeCand1(node.getNode(), config) and
- config.isSource(node.getNode()) and
+private predicate nodeCandFwd2(
+ Node node, boolean fromArg, BooleanOption argStored, boolean stored, Configuration config
+) {
+ nodeCand1(node, config) and
+ config.isSource(node) and
fromArg = false and
+ argStored = TBooleanNone() and
stored = false
or
- node.isCand1(unbind(config)) and
+ nodeCand1(node, unbind(config)) and
(
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, stored, config) and
- localFlowStepOrFlowThroughCallable(mid, node, config)
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, stored, config) and
+ localFlowStepNodeCand1(mid, node, config)
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, stored, config) and
- additionalLocalFlowStepOrFlowThroughCallable(mid, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, stored, config) and
+ additionalLocalFlowStepNodeCand1(mid, node, config) and
stored = false
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, _, stored, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
+ exists(Node mid |
+ nodeCandFwd2(mid, _, _, stored, config) and
+ jumpStep(mid, node, config) and
+ fromArg = false and
+ argStored = TBooleanNone()
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, _, stored, config) and
- additionalJumpStepExt(mid, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, _, _, stored, config) and
+ additionalJumpStep(mid, node, config) and
fromArg = false and
+ argStored = TBooleanNone() and
stored = false
)
or
// store
- exists(NodeExt mid, Content f |
- nodeCandFwd2(mid, fromArg, _, config) and
- storeExt(mid, f, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, _, config) and
+ storeCand1(mid, _, node, config) and
stored = true
)
or
// read
- exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, config) and
- storeCandFwd2(f, stored, config)
+ exists(Content c |
+ nodeCandFwd2Read(c, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(c, stored, config)
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- nodeCandFwd2(mid, _, stored, config) and
- flowIntoCallableNodeCand1(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (stored = false or allowsFieldFlow = true)
- )
+ // flow into a callable
+ nodeCandFwd2In(_, node, _, _, stored, config) and
+ fromArg = true and
+ if parameterThroughFlowNodeCand1(node, config)
+ then argStored = TBooleanSome(stored)
+ else argStored = TBooleanNone()
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- nodeCandFwd2(mid, false, stored, config) and
- flowOutOfCallableNodeCand1(mid, node, allowsFieldFlow, config) and
- fromArg = false and
- (stored = false or allowsFieldFlow = true)
+ // flow out of a callable
+ exists(DataFlowCall call |
+ nodeCandFwd2Out(call, node, fromArg, argStored, stored, config) and
+ fromArg = false
+ or
+ exists(boolean argStored0 |
+ nodeCandFwd2OutFromArg(call, node, argStored0, stored, config) and
+ nodeCandFwd2IsEntered(call, fromArg, argStored, argStored0, config)
+ )
)
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
*/
pragma[noinline]
-private predicate storeCandFwd2(Content f, boolean stored, Configuration config) {
- exists(NodeExt mid, NodeExt node |
+private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
+ exists(Node mid, Node node |
useFieldFlow(config) and
- node.isCand1(unbind(config)) and
- nodeCandFwd2(mid, _, stored, config) and
- storeExt(mid, f, node, config)
+ nodeCand1(node, unbind(config)) and
+ nodeCandFwd2(mid, _, _, stored, config) and
+ storeCand1(mid, c, node, config)
)
}
pragma[nomagic]
-private predicate nodeCandFwd2Read(Content f, NodeExt node, boolean fromArg, Configuration config) {
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, true, config) and
- readExt(mid, f, node, config)
+private predicate nodeCandFwd2Read(
+ Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+) {
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, true, config) and
+ read(mid, c, node, config)
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2In(
+ DataFlowCall call, ParameterNode p, boolean fromArg, BooleanOption argStored, boolean stored,
+ Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ nodeCandFwd2(arg, fromArg, argStored, stored, config) and
+ flowIntoCallNodeCand1(call, arg, p, allowsFieldFlow, config)
+ |
+ stored = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2Out(
+ DataFlowCall call, Node out, boolean fromArg, BooleanOption argStored, boolean stored,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ nodeCandFwd2(ret, fromArg, argStored, stored, config) and
+ flowOutOfCallNodeCand1(call, ret, out, allowsFieldFlow, config)
+ |
+ stored = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2OutFromArg(
+ DataFlowCall call, Node out, boolean argStored, boolean stored, Configuration config
+) {
+ nodeCandFwd2Out(call, out, true, TBooleanSome(argStored), stored, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `nodeCandFwd2`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd2IsEntered(
+ DataFlowCall call, boolean fromArg, BooleanOption argStored, boolean stored, Configuration config
+) {
+ exists(ParameterNode p |
+ nodeCandFwd2In(call, p, fromArg, argStored, stored, config) and
+ parameterThroughFlowNodeCand1(p, config)
)
}
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is part of a path from a source to a sink in the
+ * configuration `config`. The Boolean `read` records whether the tracked
+ * value must be read from a field of `node` in order to reach a sink.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnRead`
+ * records whether a field must be read from the returned value.
*/
-private predicate nodeCand2(NodeExt node, boolean toReturn, boolean read, Configuration config) {
- nodeCandFwd2(node, _, false, config) and
- config.isSink(node.getNode()) and
+private predicate nodeCand2(
+ Node node, boolean toReturn, BooleanOption returnRead, boolean read, Configuration config
+) {
+ nodeCandFwd2(node, _, _, false, config) and
+ config.isSink(node) and
toReturn = false and
+ returnRead = TBooleanNone() and
read = false
or
- nodeCandFwd2(node, _, unbindBool(read), unbind(config)) and
+ nodeCandFwd2(node, _, _, unbindBool(read), unbind(config)) and
(
- exists(NodeExt mid |
- localFlowStepOrFlowThroughCallable(node, mid, config) and
- nodeCand2(mid, toReturn, read, config)
+ exists(Node mid |
+ localFlowStepNodeCand1(node, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, read, config)
)
or
- exists(NodeExt mid |
- additionalLocalFlowStepOrFlowThroughCallable(node, mid, config) and
- nodeCand2(mid, toReturn, read, config) and
+ exists(Node mid |
+ additionalLocalFlowStepNodeCand1(node, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, read, config) and
read = false
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- nodeCand2(mid, _, read, config) and
- toReturn = false
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ nodeCand2(mid, _, _, read, config) and
+ toReturn = false and
+ returnRead = TBooleanNone()
)
or
- exists(NodeExt mid |
- additionalJumpStepExt(node, mid, config) and
- nodeCand2(mid, _, read, config) and
+ exists(Node mid |
+ additionalJumpStep(node, mid, config) and
+ nodeCand2(mid, _, _, read, config) and
toReturn = false and
+ returnRead = TBooleanNone() and
read = false
)
or
// store
- exists(Content f |
- nodeCand2Store(f, node, toReturn, read, config) and
- readCand2(f, read, config)
+ exists(Content c |
+ nodeCand2Store(c, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(c, read, config)
)
or
// read
- exists(NodeExt mid, Content f, boolean read0 |
- readExt(node, f, mid, config) and
- storeCandFwd2(f, unbindBool(read0), unbind(config)) and
- nodeCand2(mid, toReturn, read0, config) and
+ exists(Node mid, Content c, boolean read0 |
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
+ nodeCand2(mid, toReturn, returnRead, read0, config) and
read = true
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand1(node, mid, allowsFieldFlow, config) and
- nodeCand2(mid, false, read, config) and
- toReturn = false and
- (read = false or allowsFieldFlow = true)
+ // flow into a callable
+ exists(DataFlowCall call |
+ nodeCand2In(call, node, toReturn, returnRead, read, config) and
+ toReturn = false
+ or
+ exists(boolean returnRead0 |
+ nodeCand2InToReturn(call, node, returnRead0, read, config) and
+ nodeCand2IsReturned(call, toReturn, returnRead, returnRead0, config)
+ )
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand1(node, mid, allowsFieldFlow, config) and
- nodeCand2(mid, _, read, config) and
- toReturn = true and
- (read = false or allowsFieldFlow = true)
- )
+ // flow out of a callable
+ nodeCand2Out(_, node, _, _, read, config) and
+ toReturn = true and
+ if nodeCandFwd2(node, true, TBooleanSome(_), unbindBool(read), config)
+ then returnRead = TBooleanSome(read)
+ else returnRead = TBooleanNone()
)
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate readCand2(Content f, boolean read, Configuration config) {
- exists(NodeExt mid, NodeExt node |
+private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
+ exists(Node mid, Node node |
useFieldFlow(config) and
- nodeCandFwd2(node, _, true, unbind(config)) and
- readExt(node, f, mid, config) and
- storeCandFwd2(f, unbindBool(read), unbind(config)) and
- nodeCand2(mid, _, read, config)
+ nodeCandFwd2(node, _, _, true, unbind(config)) and
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
+ nodeCand2(mid, _, _, read, config)
)
}
pragma[nomagic]
private predicate nodeCand2Store(
- Content f, NodeExt node, boolean toReturn, boolean stored, Configuration config
+ Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
+ Configuration config
) {
- exists(NodeExt mid |
- storeExt(node, f, mid, config) and
- nodeCand2(mid, toReturn, true, config) and
- nodeCandFwd2(node, _, stored, unbind(config))
- )
-}
-
-pragma[nomagic]
-private predicate storeCand2(Content f, boolean stored, Configuration conf) {
- exists(NodeExt node |
- nodeCand2Store(f, node, _, stored, conf) and
- nodeCand2(node, _, stored, conf)
+ exists(Node mid |
+ storeCand1(node, c, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, true, config) and
+ nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
/**
- * Holds if `f` is the target of both a store and a read in the path graph
- * covered by `nodeCand2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
*/
-pragma[noinline]
-private predicate readStoreCand(Content f, Configuration conf) {
- exists(boolean apNonEmpty |
- storeCand2(f, apNonEmpty, conf) and
- readCand2(f, apNonEmpty, conf)
+pragma[nomagic]
+private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
+ exists(Node node |
+ nodeCand2Store(c, node, _, _, stored, conf) and
+ nodeCand2(node, _, _, stored, conf)
)
}
-private predicate nodeCand2(NodeExt node, Configuration config) { nodeCand2(node, _, _, config) }
+/**
+ * Holds if `c` is the target of both a store and a read in the path graph
+ * covered by `nodeCand2`.
+ */
+pragma[noinline]
+private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
+ exists(boolean apNonEmpty |
+ nodeCand2IsStored(c, apNonEmpty, conf) and
+ nodeCand2IsRead(c, apNonEmpty, conf)
+ )
+}
pragma[nomagic]
-private predicate flowOutOfCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate nodeCand2Out(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
) {
- flowOutOfCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ exists(Node out, boolean allowsFieldFlow |
+ nodeCand2(out, toReturn, returnRead, read, config) and
+ flowOutOfCallNodeCand1(call, ret, out, allowsFieldFlow, config)
+ |
+ read = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCand2In(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ nodeCand2(p, toReturn, returnRead, read, config) and
+ flowIntoCallNodeCand1(call, arg, p, allowsFieldFlow, config)
+ |
+ read = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCand2InToReturn(
+ DataFlowCall call, ArgumentNode arg, boolean returnRead, boolean read, Configuration config
+) {
+ nodeCand2In(call, arg, true, TBooleanSome(returnRead), read, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `nodeCand2`.
+ */
+pragma[nomagic]
+private predicate nodeCand2IsReturned(
+ DataFlowCall call, boolean toReturn, BooleanOption returnRead, boolean read, Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ nodeCand2Out(call, ret, toReturn, returnRead, read, config) and
+ nodeCandFwd2(ret, true, TBooleanSome(_), read, config)
+ )
+}
+
+private predicate nodeCand2(Node node, Configuration config) { nodeCand2(node, _, _, _, config) }
+
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand2(
+ DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
+) {
+ flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
nodeCand2(node2, config) and
nodeCand2(node1, unbind(config))
}
pragma[nomagic]
-private predicate flowIntoCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate flowIntoCallNodeCand2(
+ DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ Configuration config
) {
- flowIntoCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
nodeCand2(node2, config) and
nodeCand2(node1, unbind(config))
}
@@ -1284,15 +1056,15 @@ private module LocalFlowBigStep {
* flow steps in a dataflow path.
*/
private predicate localFlowEntry(Node node, Configuration config) {
- nodeCand2(TNormalNode(node), config) and
+ nodeCand2(node, config) and
(
config.isSource(node) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
node instanceof ParameterNode or
- node instanceof OutNode or
- node instanceof PostUpdateNode or
- readDirect(_, _, node) or
+ node instanceof OutNodeExt or
+ store(_, _, node, _) or
+ read(_, _, node) or
node instanceof CastNode
)
}
@@ -1302,15 +1074,13 @@ private module LocalFlowBigStep {
* flow steps in a dataflow path.
*/
private predicate localFlowExit(Node node, Configuration config) {
- exists(Node next | nodeCand2(TNormalNode(next), config) |
+ exists(Node next | nodeCand2(next, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
- flowIntoCallableNodeCand1(node, next, config) or
- flowOutOfCallableNodeCand1(node, next, config) or
- argumentFlowsThrough(node, next, _, _, _, config) or
- argumentValueFlowsThrough(_, node, TContentNone(), TContentNone(), next) or
- storeDirect(node, _, next) or
- readDirect(node, _, next)
+ flowIntoCallNodeCand1(_, node, next, config) or
+ flowOutOfCallNodeCand1(_, node, next, config) or
+ store(node, _, next, _) or
+ read(node, _, next)
)
or
node instanceof CastNode
@@ -1318,6 +1088,13 @@ private module LocalFlowBigStep {
config.isSink(node)
}
+ pragma[noinline]
+ private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
+ additionalLocalFlowStepNodeCand1(node1, node2, config) and
+ nodeCand2(node1, _, _, false, config) and
+ nodeCand2(node2, _, _, false, unbind(config))
+ }
+
/**
* Holds if the local path from `node1` to `node2` is a prefix of a maximal
* subsequence of local flow steps in a dataflow path.
@@ -1334,33 +1111,33 @@ private module LocalFlowBigStep {
(
localFlowEntry(node1, config) and
(
- localFlowStep(node1, node2, config) and
+ localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
t = getErasedNodeTypeBound(node1)
or
- additionalLocalFlowStep(node1, node2, config) and
+ additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
t = getErasedNodeTypeBound(node2)
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
or
exists(Node mid |
localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
- localFlowStep(mid, node2, config) and
+ localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof CastNode and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
)
or
exists(Node mid |
localFlowStepPlus(node1, mid, _, _, config, cc) and
- additionalLocalFlowStep(mid, node2, config) and
+ additionalLocalFlowStepNodeCand2(mid, node2, config) and
not mid instanceof CastNode and
preservesValue = false and
t = getErasedNodeTypeBound(node2) and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
)
)
}
@@ -1371,311 +1148,380 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config,
+ Node node1, Node node2, boolean preservesValue, AccessPathFrontNil apf, Configuration config,
LocalCallContext callContext
) {
- localFlowStepPlus(node1, node2, preservesValue, t, config, callContext) and
+ localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, config)
}
-
- pragma[nomagic]
- predicate localFlowBigStepExt(
- NodeExt node1, NodeExt node2, boolean preservesValue, AccessPathFrontNil apf,
- Configuration config
- ) {
- localFlowBigStep(node1.getNode(), node2.getNode(), preservesValue, apf.getType(), config, _)
- or
- additionalLocalFlowStepExt(node1, node2, apf.getType(), config) and
- nodeCand2(node1, config) and
- nodeCand2(node2, unbind(config)) and
- preservesValue = false
- }
}
private import LocalFlowBigStep
pragma[nomagic]
-private predicate readExtCand2(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- readExt(node1, f, node2, config) and
- nodeCand2(node1, _, true, unbind(config)) and
+private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
+ read(node1, c, node2, config) and
+ nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- readStoreCand(f, unbind(config))
+ nodeCand2IsReadAndStored(c, unbind(config))
}
pragma[nomagic]
-private predicate storeExtCand2(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- storeExt(node1, f, node2, config) and
+private predicate storeCand2(
+ Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+) {
+ store(node1, tc, node2, contentType) and
nodeCand2(node1, config) and
- nodeCand2(node2, _, true, unbind(config)) and
- readStoreCand(f, unbind(config))
-}
-
-private newtype TAccessPathFront =
- TFrontNil(DataFlowType t) or
- TFrontHead(Content f)
-
-/**
- * The front of an `AccessPath`. This is either a head or a nil.
- */
-abstract private class AccessPathFront extends TAccessPathFront {
- abstract string toString();
-
- abstract DataFlowType getType();
-
- abstract boolean toBoolNonEmpty();
-
- predicate headUsesContent(Content f) { this = TFrontHead(f) }
-}
-
-private class AccessPathFrontNil extends AccessPathFront, TFrontNil {
- override string toString() {
- exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
- }
-
- override DataFlowType getType() { this = TFrontNil(result) }
-
- override boolean toBoolNonEmpty() { result = false }
-}
-
-private class AccessPathFrontHead extends AccessPathFront, TFrontHead {
- override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) }
-
- override DataFlowType getType() {
- exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
- }
-
- override boolean toBoolNonEmpty() { result = true }
+ nodeCand2(node2, _, _, true, unbind(config)) and
+ nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
}
/**
- * Holds if data can flow from a source to `node` with the given `apf`.
+ * Holds if `node` is reachable with access path front `apf` from a
+ * source in the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argApf` records the front of the
+ * access path of that argument.
*/
pragma[nomagic]
private predicate flowCandFwd(
- NodeExt node, boolean fromArg, AccessPathFront apf, Configuration config
+ Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
) {
- flowCandFwd0(node, fromArg, apf, config) and
- if node instanceof CastingNodeExt
- then compatibleTypes(node.getErasedNodeTypeBound(), apf.getType())
+ flowCandFwd0(node, fromArg, argApf, apf, config) and
+ if node instanceof CastingNode
+ then compatibleTypes(getErasedNodeTypeBound(node), apf.getType())
else any()
}
pragma[nomagic]
private predicate flowCandFwd0(
- NodeExt node, boolean fromArg, AccessPathFront apf, Configuration config
+ Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
) {
- nodeCand2(node, _, false, config) and
- config.isSource(node.getNode()) and
+ nodeCand2(node, _, _, false, config) and
+ config.isSource(node) and
fromArg = false and
- apf = TFrontNil(node.getErasedNodeTypeBound())
+ argApf = TAccessPathFrontNone() and
+ apf = TFrontNil(getErasedNodeTypeBound(node))
or
- exists(NodeExt mid |
- flowCandFwd(mid, fromArg, apf, config) and
- localFlowBigStepExt(mid, node, true, _, config)
+ exists(Node mid |
+ flowCandFwd(mid, fromArg, argApf, apf, config) and
+ localFlowBigStep(mid, node, true, _, config, _)
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(mid, fromArg, nil, config) and
- localFlowBigStepExt(mid, node, false, apf, config)
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(mid, fromArg, argApf, nil, config) and
+ localFlowBigStep(mid, node, false, apf, config, _)
)
or
- nodeCand2(node, unbind(config)) and
- (
- exists(NodeExt mid |
- flowCandFwd(mid, _, apf, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(mid, _, nil, config) and
- additionalJumpStepExt(mid, node, config) and
- fromArg = false and
- apf = TFrontNil(node.getErasedNodeTypeBound())
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowCandFwd(mid, _, apf, config) and
- flowIntoCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowCandFwd(mid, false, apf, config) and
- flowOutOfCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = false and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- flowCandFwd(mid, fromArg, apf, config) and
- argumentValueFlowsThrough(mid, node)
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil, DataFlowType t |
- flowCandFwd(mid, fromArg, nil, config) and
- argumentFlowsThrough(mid, node, t, config) and
- apf = TFrontNil(t)
- )
+ exists(Node mid |
+ flowCandFwd(mid, _, _, apf, config) and
+ nodeCand2(node, unbind(config)) and
+ jumpStep(mid, node, config) and
+ fromArg = false and
+ argApf = TAccessPathFrontNone()
)
or
- exists(NodeExt mid, Content f |
- flowCandFwd(mid, fromArg, _, config) and
- storeExtCand2(mid, f, node, config) and
- nodeCand2(node, _, true, unbind(config)) and
- apf.headUsesContent(f)
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(mid, _, _, nil, config) and
+ nodeCand2(node, unbind(config)) and
+ additionalJumpStep(mid, node, config) and
+ fromArg = false and
+ argApf = TAccessPathFrontNone() and
+ apf = TFrontNil(getErasedNodeTypeBound(node))
)
or
- exists(Content f |
- flowCandFwdRead(f, node, fromArg, config) and
- consCandFwd(f, apf, config) and
- nodeCand2(node, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ // store
+ exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ storeCand2(mid, tc, node, contentType, config) and
+ nodeCand2(node, _, _, true, unbind(config)) and
+ apf.headUsesContent(tc) and
+ compatibleTypes(apf0.getType(), contentType)
+ )
+ or
+ // read
+ exists(TypedContent tc |
+ flowCandFwdRead(tc, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(tc, apf, config) and
+ nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ )
+ or
+ // flow into a callable
+ flowCandFwdIn(_, node, _, _, apf, config) and
+ fromArg = true and
+ if nodeCand2(node, true, _, unbindBool(apf.toBoolNonEmpty()), config)
+ then argApf = TAccessPathFrontSome(apf)
+ else argApf = TAccessPathFrontNone()
+ or
+ // flow out of a callable
+ exists(DataFlowCall call |
+ flowCandFwdOut(call, node, fromArg, argApf, apf, config) and
+ fromArg = false
+ or
+ exists(AccessPathFront argApf0 |
+ flowCandFwdOutFromArg(call, node, argApf0, apf, config) and
+ flowCandFwdIsEntered(call, fromArg, argApf, argApf0, config)
+ )
)
}
pragma[nomagic]
-private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) {
- exists(NodeExt mid, NodeExt n |
- flowCandFwd(mid, _, apf, config) and
- storeExtCand2(mid, f, n, config) and
- nodeCand2(n, _, true, unbind(config)) and
- compatibleTypes(apf.getType(), f.getType())
+private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n, DataFlowType contentType |
+ flowCandFwd(mid, _, _, apf, config) and
+ storeCand2(mid, tc, n, contentType, config) and
+ nodeCand2(n, _, _, true, unbind(config)) and
+ compatibleTypes(apf.getType(), contentType)
)
}
pragma[nomagic]
-private predicate flowCandFwdRead(Content f, NodeExt node, boolean fromArg, Configuration config) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowCandFwd(mid, fromArg, apf0, config) and
- readExtCand2(mid, f, node, config) and
- apf0.headUsesContent(f)
+private predicate flowCandFwdRead0(
+ Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFrontHead apf, Configuration config
+) {
+ flowCandFwd(node1, fromArg, argApf, apf, config) and
+ readCand2(node1, c, node2, config) and
+ apf.headUsesContent(tc)
+}
+
+pragma[nomagic]
+private predicate flowCandFwdRead(
+ TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+) {
+ flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
+}
+
+pragma[nomagic]
+private predicate flowCandFwdIn(
+ DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ flowCandFwd(arg, fromArg, argApf, apf, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ flowCandFwd(ret, fromArg, argApf, apf, config) and
+ flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandFwdOutFromArg(
+ DataFlowCall call, Node node, AccessPathFront argApf, AccessPathFront apf, Configuration config
+) {
+ flowCandFwdOut(call, node, true, TAccessPathFrontSome(argApf), apf, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `flowCandFwd`.
+ */
+pragma[nomagic]
+private predicate flowCandFwdIsEntered(
+ DataFlowCall call, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ParameterNode p |
+ flowCandFwdIn(call, p, fromArg, argApf, apf, config) and
+ nodeCand2(p, true, TBooleanSome(_), unbindBool(apf.toBoolNonEmpty()), config)
)
}
/**
- * Holds if data can flow from a source to `node` with the given `apf` and
- * from there flow to a sink.
+ * Holds if `node` with access path front `apf` is part of a path from a
+ * source to a sink in the configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnApf`
+ * records the front of the access path of the returned value.
*/
pragma[nomagic]
-private predicate flowCand(NodeExt node, boolean toReturn, AccessPathFront apf, Configuration config) {
- flowCand0(node, toReturn, apf, config) and
- flowCandFwd(node, _, apf, config)
+private predicate flowCand(
+ Node node, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ flowCand0(node, toReturn, returnApf, apf, config) and
+ flowCandFwd(node, _, _, apf, config)
}
pragma[nomagic]
private predicate flowCand0(
- NodeExt node, boolean toReturn, AccessPathFront apf, Configuration config
+ Node node, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
) {
- flowCandFwd(node, _, apf, config) and
- config.isSink(node.getNode()) and
+ flowCandFwd(node, _, _, apf, config) and
+ config.isSink(node) and
toReturn = false and
+ returnApf = TAccessPathFrontNone() and
apf instanceof AccessPathFrontNil
or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flowCand(mid, toReturn, apf, config)
+ exists(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flowCand(mid, toReturn, returnApf, apf, config)
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- localFlowBigStepExt(node, mid, false, _, config) and
- flowCand(mid, toReturn, nil, config) and
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(node, _, _, apf, config) and
+ localFlowBigStep(node, mid, false, _, config, _) and
+ flowCand(mid, toReturn, returnApf, nil, config) and
apf instanceof AccessPathFrontNil
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flowCand(mid, _, apf, config) and
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flowCand(mid, _, _, apf, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone()
+ )
+ or
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(node, _, _, apf, config) and
+ additionalJumpStep(node, mid, config) and
+ flowCand(mid, _, _, nil, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone() and
+ apf instanceof AccessPathFrontNil
+ )
+ or
+ // store
+ exists(TypedContent tc |
+ flowCandStore(node, tc, apf, toReturn, returnApf, config) and
+ flowCandConsCand(tc, apf, config)
+ )
+ or
+ // read
+ exists(TypedContent tc, AccessPathFront apf0 |
+ flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(tc, apf0, config)
+ )
+ or
+ // flow into a callable
+ exists(DataFlowCall call |
+ flowCandIn(call, node, toReturn, returnApf, apf, config) and
toReturn = false
+ or
+ exists(AccessPathFront returnApf0 |
+ flowCandInToReturn(call, node, returnApf0, apf, config) and
+ flowCandIsReturned(call, toReturn, returnApf, returnApf0, config)
+ )
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- additionalJumpStepExt(node, mid, config) and
- flowCand(mid, _, nil, config) and
- toReturn = false and
- apf instanceof AccessPathFrontNil
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flowCand(mid, false, apf, config) and
- toReturn = false and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flowCand(mid, _, apf, config) and
- toReturn = true and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- argumentValueFlowsThrough(node, mid) and
- flowCand(mid, toReturn, apf, config)
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil |
- argumentFlowsThrough(node, mid, _, config) and
- flowCand(mid, toReturn, nil, config) and
- apf instanceof AccessPathFrontNil and
- flowCandFwd(node, _, apf, config)
- )
- or
- exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, apf0, config) and
- apf0.headUsesContent(f) and
- consCand(f, apf, config)
- )
- or
- exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, config) and
- apf.headUsesContent(f)
- )
+ // flow out of a callable
+ flowCandOut(_, node, _, _, apf, config) and
+ toReturn = true and
+ if flowCandFwd(node, true, _, apf, config)
+ then returnApf = TAccessPathFrontSome(apf)
+ else returnApf = TAccessPathFrontNone()
+}
+
+pragma[nomagic]
+private predicate readCandFwd(
+ Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
+) {
+ flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
}
pragma[nomagic]
private predicate flowCandRead(
- NodeExt node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
) {
- exists(NodeExt mid |
- readExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ readCandFwd(node, tc, apf, mid, config) and
+ flowCand(mid, toReturn, returnApf, apf0, config)
)
}
pragma[nomagic]
private predicate flowCandStore(
- NodeExt node, Content f, boolean toReturn, AccessPathFrontHead apf0, Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, Configuration config
) {
- exists(NodeExt mid |
- storeExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ flowCandFwd(node, _, _, apf, config) and
+ storeCand2(node, tc, mid, _, unbind(config)) and
+ flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
)
}
pragma[nomagic]
-private predicate consCand(Content f, AccessPathFront apf, Configuration config) {
- consCandFwd(f, apf, config) and
- exists(NodeExt n, AccessPathFrontHead apf0 |
- flowCandFwd(n, _, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandRead(n, f, _, apf, config)
+private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(tc, apf, config) and
+ flowCandRead(_, tc, _, _, _, apf, config)
+}
+
+pragma[nomagic]
+private predicate flowCandOut(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathFrontOption returnApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(Node out, boolean allowsFieldFlow |
+ flowCand(out, toReturn, returnApf, apf, config) and
+ flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandIn(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathFrontOption returnApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ flowCand(p, toReturn, returnApf, apf, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandInToReturn(
+ DataFlowCall call, ArgumentNode arg, AccessPathFront returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ flowCandIn(call, arg, true, TAccessPathFrontSome(returnApf), apf, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `flowCand`.
+ */
+pragma[nomagic]
+private predicate flowCandIsReturned(
+ DataFlowCall call, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ flowCandOut(call, ret, toReturn, returnApf, apf, config) and
+ flowCandFwd(ret, true, TAccessPathFrontSome(_), apf, config)
)
}
private newtype TAccessPath =
TNil(DataFlowType t) or
- TConsNil(Content f, DataFlowType t) { consCand(f, TFrontNil(t), _) } or
- TConsCons(Content f1, Content f2, int len) {
- consCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
+ TConsCons(TypedContent tc1, TypedContent tc2, int len) {
+ flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
}
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first two
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
* elements of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -1684,7 +1530,7 @@ private newtype TAccessPath =
abstract private class AccessPath extends TAccessPath {
abstract string toString();
- abstract Content getHead();
+ abstract TypedContent getHead();
abstract int len();
@@ -1695,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
/**
* Holds if this access path has `head` at the front and may be followed by `tail`.
*/
- abstract predicate pop(Content head, AccessPath tail);
+ abstract predicate pop(TypedContent head, AccessPath tail);
}
private class AccessPathNil extends AccessPath, TNil {
@@ -1705,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
override string toString() { result = concat(": " + ppReprType(t)) }
- override Content getHead() { none() }
+ override TypedContent getHead() { none() }
override int len() { result = 0 }
@@ -1713,357 +1559,469 @@ private class AccessPathNil extends AccessPath, TNil {
override AccessPathFront getFront() { result = TFrontNil(t) }
- override predicate pop(Content head, AccessPath tail) { none() }
+ override predicate pop(TypedContent head, AccessPath tail) { none() }
}
abstract private class AccessPathCons extends AccessPath { }
private class AccessPathConsNil extends AccessPathCons, TConsNil {
- private Content f;
+ private TypedContent tc;
private DataFlowType t;
- AccessPathConsNil() { this = TConsNil(f, t) }
+ AccessPathConsNil() { this = TConsNil(tc, t) }
override string toString() {
// The `concat` becomes "" if `ppReprType` has no result.
- result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
+ result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
}
- override Content getHead() { result = f }
+ override TypedContent getHead() { result = tc }
override int len() { result = 1 }
- override DataFlowType getType() { result = f.getContainerType() }
+ override DataFlowType getType() { result = tc.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f) }
+ override AccessPathFront getFront() { result = TFrontHead(tc) }
- override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
+ override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
}
private class AccessPathConsCons extends AccessPathCons, TConsCons {
- private Content f1;
- private Content f2;
+ private TypedContent tc1;
+ private TypedContent tc2;
private int len;
- AccessPathConsCons() { this = TConsCons(f1, f2, len) }
+ AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
override string toString() {
if len = 2
- then result = "[" + f1.toString() + ", " + f2.toString() + "]"
- else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
+ else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
}
- override Content getHead() { result = f1 }
+ override TypedContent getHead() { result = tc1 }
override int len() { result = len }
- override DataFlowType getType() { result = f1.getContainerType() }
+ override DataFlowType getType() { result = tc1.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f1) }
+ override AccessPathFront getFront() { result = TFrontHead(tc1) }
- override predicate pop(Content head, AccessPath tail) {
- head = f1 and
+ override predicate pop(TypedContent head, AccessPath tail) {
+ head = tc1 and
(
- tail = TConsCons(f2, _, len - 1)
+ tail = TConsCons(tc2, _, len - 1)
or
len = 2 and
- tail = TConsNil(f2, _)
+ tail = TConsNil(tc2, _)
)
}
}
-/** Gets the access path obtained by popping `f` from `ap`, if any. */
-private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
+/** Gets the access path obtained by popping `tc` from `ap`, if any. */
+private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
-/** Gets the access path obtained by pushing `f` onto `ap`. */
-private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
+/** Gets the access path obtained by pushing `tc` onto `ap`. */
+private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
+
+private newtype TAccessPathOption =
+ TAccessPathNone() or
+ TAccessPathSome(AccessPath ap)
+
+private class AccessPathOption extends TAccessPathOption {
+ string toString() {
+ this = TAccessPathNone() and result = ""
+ or
+ this = TAccessPathSome(any(AccessPath ap | result = ap.toString()))
+ }
+}
/**
- * Holds if data can flow from a source to `node` with the given `ap`.
+ * Holds if `node` is reachable with access path `ap` from a source in
+ * the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argAp` records the access path of that
+ * argument.
*/
private predicate flowFwd(
- NodeExt node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config
+ Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
) {
- flowFwd0(node, fromArg, apf, ap, config) and
- flowCand(node, _, apf, config)
+ flowFwd0(node, fromArg, argAp, apf, ap, config) and
+ flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
- NodeExt node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config
+ Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
) {
- flowCand(node, _, _, config) and
- config.isSource(node.getNode()) and
+ flowCand(node, _, _, _, config) and
+ config.isSource(node) and
fromArg = false and
- ap = TNil(node.getErasedNodeTypeBound()) and
+ argAp = TAccessPathNone() and
+ ap = TNil(getErasedNodeTypeBound(node)) and
apf = ap.(AccessPathNil).getFront()
or
- flowCand(node, _, _, unbind(config)) and
+ flowCand(node, _, _, _, unbind(config)) and
(
- exists(NodeExt mid |
- flowFwd(mid, fromArg, apf, ap, config) and
- localFlowBigStepExt(mid, node, true, _, config)
+ exists(Node mid |
+ flowFwd(mid, fromArg, argAp, apf, ap, config) and
+ localFlowBigStep(mid, node, true, _, config, _)
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(mid, fromArg, _, nil, config) and
- localFlowBigStepExt(mid, node, false, apf, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(mid, fromArg, argAp, _, nil, config) and
+ localFlowBigStep(mid, node, false, apf, config, _) and
apf = ap.(AccessPathNil).getFront()
)
or
- exists(NodeExt mid |
- flowFwd(mid, _, apf, ap, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
- )
- or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(mid, _, _, nil, config) and
- additionalJumpStepExt(mid, node, config) and
+ exists(Node mid |
+ flowFwd(mid, _, _, apf, ap, config) and
+ jumpStep(mid, node, config) and
fromArg = false and
- ap = TNil(node.getErasedNodeTypeBound()) and
- apf = ap.(AccessPathNil).getFront()
+ argAp = TAccessPathNone()
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowFwd(mid, _, apf, ap, config) and
- flowIntoCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowFwd(mid, false, apf, ap, config) and
- flowOutOfCallableNodeCand2(mid, node, allowsFieldFlow, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(mid, _, _, _, nil, config) and
+ additionalJumpStep(mid, node, config) and
fromArg = false and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- flowFwd(mid, fromArg, apf, ap, config) and
- argumentValueFlowsThrough(mid, node)
- )
- or
- exists(NodeExt mid, AccessPathNil nil, DataFlowType t |
- flowFwd(mid, fromArg, _, nil, config) and
- argumentFlowsThrough(mid, node, t, config) and
- ap = TNil(t) and
+ argAp = TAccessPathNone() and
+ ap = TNil(getErasedNodeTypeBound(node)) and
apf = ap.(AccessPathNil).getFront()
)
)
or
- exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, config) and
- ap = push(f, ap0)
+ // store
+ exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
+ or
+ // read
+ exists(TypedContent tc |
+ flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
+ flowFwdConsCand(tc, apf, ap, config)
)
or
- exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, config) and
- flowConsCandFwd(f, apf, ap, config)
+ // flow into a callable
+ flowFwdIn(_, node, _, _, apf, ap, config) and
+ fromArg = true and
+ if flowCand(node, true, _, apf, config)
+ then argAp = TAccessPathSome(ap)
+ else argAp = TAccessPathNone()
+ or
+ // flow out of a callable
+ exists(DataFlowCall call |
+ flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
+ fromArg = false
+ or
+ exists(AccessPath argAp0 |
+ flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and
+ flowFwdIsEntered(call, fromArg, argAp, argAp0, config)
+ )
)
}
pragma[nomagic]
private predicate flowFwdStore(
- NodeExt node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
- Configuration config
+ Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFront apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- flowFwdStore1(mid, f, node, apf0, apf, config)
+ exists(Node mid, AccessPathFront apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
+ flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
pragma[nomagic]
-private predicate flowFwdStore0(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, Configuration config
+private predicate storeCand(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
+ Configuration config
) {
- storeExtCand2(mid, f, node, config) and
- flowCand(mid, _, apf0, config)
+ storeCand2(mid, tc, node, _, config) and
+ flowCand(mid, _, _, apf0, config) and
+ apf.headUsesContent(tc)
}
pragma[noinline]
-private predicate flowFwdStore1(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, AccessPathFrontHead apf,
+private predicate flowFwdStore0(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
- flowFwdStore0(mid, f, node, apf0, config) and
- consCand(f, apf0, config) and
- apf.headUsesContent(f) and
- flowCand(node, _, apf, unbind(config))
+ storeCand(mid, tc, node, apf0, apf, config) and
+ flowCandConsCand(tc, apf0, config) and
+ flowCand(node, _, _, apf, unbind(config))
+}
+
+pragma[nomagic]
+private predicate flowFwdRead0(
+ Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
+ boolean fromArg, AccessPathOption argAp, Configuration config
+) {
+ flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
+ readCandFwd(node1, tc, apf0, node2, config)
}
pragma[nomagic]
private predicate flowFwdRead(
- NodeExt node, Content f, AccessPath ap0, boolean fromArg, Configuration config
+ Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- readExtCand2(mid, f, node, config) and
- apf0.headUsesContent(f) and
- flowCand(node, _, _, unbind(config))
+ exists(Node mid, TypedContent tc |
+ flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
+ flowCand(node, _, _, apf, unbind(config)) and
+ flowCandConsCand(tc, apf, unbind(config))
)
}
pragma[nomagic]
-private predicate flowConsCandFwd(
- Content f, AccessPathFront apf, AccessPath ap, Configuration config
+private predicate flowFwdConsCand(
+ TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
) {
- exists(NodeExt n |
- flowFwd(n, _, apf, ap, config) and
- flowFwdStore1(n, f, _, apf, _, config)
+ exists(Node n |
+ flowFwd(n, _, _, apf, ap, config) and
+ flowFwdStore0(n, tc, _, apf, _, config)
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdIn(
+ DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ flowFwd(arg, fromArg, argAp, apf, ap, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
+ flowCand(p, _, _, _, unbind(config))
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ flowFwd(ret, fromArg, argAp, apf, ap, config) and
+ flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
+ flowCand(node, _, _, _, unbind(config))
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdOutFromArg(
+ DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
+) {
+ flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `flowFwd`.
+ */
+pragma[nomagic]
+private predicate flowFwdIsEntered(
+ DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
+) {
+ exists(ParameterNode p, AccessPathFront apf |
+ flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and
+ flowCand(p, true, TAccessPathFrontSome(_), apf, config)
)
}
/**
- * Holds if data can flow from a source to `node` with the given `ap` and
- * from there flow to a sink.
+ * Holds if `node` with access path `ap` is part of a path from a source to
+ * a sink in the configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnAp`
+ * records the access path of the returned value.
*/
-private predicate flow(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flow0(node, toReturn, ap, config) and
- flowFwd(node, _, _, ap, config)
+private predicate flow(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flow0(node, toReturn, returnAp, ap, config) and
+ flowFwd(node, _, _, _, ap, config)
}
-private predicate flow0(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flowFwd(node, _, _, ap, config) and
- config.isSink(node.getNode()) and
+private predicate flow0(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flowFwd(node, _, _, _, ap, config) and
+ config.isSink(node) and
toReturn = false and
+ returnAp = TAccessPathNone() and
ap instanceof AccessPathNil
or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flow(mid, toReturn, ap, config)
+ exists(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flow(mid, toReturn, returnAp, ap, config)
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(node, _, _, ap, config) and
- localFlowBigStepExt(node, mid, false, _, config) and
- flow(mid, toReturn, nil, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(node, _, _, _, ap, config) and
+ localFlowBigStep(node, mid, false, _, config, _) and
+ flow(mid, toReturn, returnAp, nil, config) and
ap instanceof AccessPathNil
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flow(mid, _, ap, config) and
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flow(mid, _, _, ap, config) and
+ toReturn = false and
+ returnAp = TAccessPathNone()
+ )
+ or
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(node, _, _, _, ap, config) and
+ additionalJumpStep(node, mid, config) and
+ flow(mid, _, _, nil, config) and
+ toReturn = false and
+ returnAp = TAccessPathNone() and
+ ap instanceof AccessPathNil
+ )
+ or
+ // store
+ exists(TypedContent tc |
+ flowStore(tc, node, toReturn, returnAp, ap, config) and
+ flowConsCand(tc, ap, config)
+ )
+ or
+ // read
+ exists(Node mid, AccessPath ap0 |
+ readFlowFwd(node, _, mid, ap, ap0, config) and
+ flow(mid, toReturn, returnAp, ap0, config)
+ )
+ or
+ // flow into a callable
+ exists(DataFlowCall call |
+ flowIn(call, node, toReturn, returnAp, ap, config) and
toReturn = false
+ or
+ exists(AccessPath returnAp0 |
+ flowInToReturn(call, node, returnAp0, ap, config) and
+ flowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(node, _, _, ap, config) and
- additionalJumpStepExt(node, mid, config) and
- flow(mid, _, nil, config) and
- toReturn = false and
- ap instanceof AccessPathNil
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flow(mid, false, ap, config) and
- toReturn = false and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flow(mid, _, ap, config) and
- toReturn = true and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- argumentValueFlowsThrough(node, mid) and
- flow(mid, toReturn, ap, config)
- )
- or
- exists(NodeExt mid, AccessPathNil nil |
- argumentFlowsThrough(node, mid, _, config) and
- flow(mid, toReturn, nil, config) and
- ap instanceof AccessPathNil and
- flowFwd(node, _, _, ap, config)
- )
- or
- exists(Content f |
- flowStore(f, node, toReturn, ap, config) and
- flowConsCand(f, ap, config)
- )
- or
- exists(NodeExt mid, AccessPath ap0 |
- readFwd(node, _, mid, ap, ap0, config) and
- flow(mid, toReturn, ap0, config)
- )
+ // flow out of a callable
+ flowOut(_, node, _, _, ap, config) and
+ toReturn = true and
+ if flowFwd(node, true, TAccessPathSome(_), _, ap, config)
+ then returnAp = TAccessPathSome(ap)
+ else returnAp = TAccessPathNone()
}
pragma[nomagic]
-private predicate storeFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate storeFlowFwd(
+ Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- storeExtCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, config) and
- ap0 = push(f, ap)
+ storeCand2(node1, tc, node2, _, config) and
+ flowFwdStore(node2, tc, ap, _, _, _, config) and
+ ap0 = push(tc, ap)
}
pragma[nomagic]
private predicate flowStore(
- Content f, NodeExt node, boolean toReturn, AccessPath ap, Configuration config
+ TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
) {
- exists(NodeExt mid, AccessPath ap0 |
- storeFwd(node, f, mid, ap, ap0, config) and
- flow(mid, toReturn, ap0, config)
+ exists(Node mid, AccessPath ap0 |
+ storeFlowFwd(node, tc, mid, ap, ap0, config) and
+ flow(mid, toReturn, returnAp, ap0, config)
)
}
pragma[nomagic]
-private predicate readFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate readFlowFwd(
+ Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- readExtCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, config) and
- ap0 = pop(f, ap) and
- flowConsCandFwd(f, _, ap0, unbind(config))
+ exists(AccessPathFrontHead apf |
+ readCandFwd(node1, tc, apf, node2, config) and
+ flowFwdRead(node2, apf, ap, _, _, _, config) and
+ ap0 = pop(tc, ap) and
+ flowFwdConsCand(tc, _, ap0, unbind(config))
+ )
}
pragma[nomagic]
-private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
- exists(NodeExt n, NodeExt mid |
- flow(mid, _, ap, config) and
- readFwd(n, f, mid, _, ap, config)
+private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
+ exists(Node n, Node mid |
+ flow(mid, _, _, ap, config) and
+ readFlowFwd(n, tc, mid, _, ap, config)
+ )
+}
+
+pragma[nomagic]
+private predicate flowOut(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(Node out, boolean allowsFieldFlow |
+ flow(out, toReturn, returnAp, ap, config) and
+ flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config)
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowIn(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ flow(p, toReturn, returnAp, ap, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowInToReturn(
+ DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config
+) {
+ flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `flow`.
+ */
+pragma[nomagic]
+private predicate flowIsReturned(
+ DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ flowOut(call, ret, toReturn, returnAp, ap, config) and
+ flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
)
}
bindingset[conf, result]
private Configuration unbind(Configuration conf) { result >= conf and result <= conf }
-private predicate flow(Node n, Configuration config) { flow(TNormalNode(n), _, _, config) }
+private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) }
+
+pragma[noinline]
+private predicate parameterFlow(
+ ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config
+) {
+ flow(p, true, _, ap, config) and
+ c = p.getEnclosingCallable()
+}
private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParameterNode p, AccessPath ap) {
- exists(ReturnNodeExt ret, Configuration config | flow(TNormalNode(p), true, ap, config) |
- exists(Summary summary |
- parameterFlowReturn(p, ret, _, _, _, summary, config) and
- flow(ret, unbind(config))
- |
- // taint through
- summary = TSummaryTaint() and
- ap instanceof AccessPathNil
- or
- // taint setter
- summary = TSummaryTaintStore(_) and
- ap instanceof AccessPathNil
- or
- // taint getter
- summary = TSummaryReadTaint(ap.(AccessPathConsNil).getHead())
- )
- or
- exists(ContentOption contentIn |
- parameterValueFlowReturn(p, ret, _, contentIn, _) and
- flow(ret, unbind(config))
- |
- // value through/setter
- contentIn = TContentNone()
- or
- // value getter (+ setter)
- contentIn = TContentSome(ap.getHead())
- )
+ exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 |
+ parameterFlow(p, ap, ret.getEnclosingCallable(), config) and
+ flow(ret, true, TAccessPathSome(_), ap0, config) and
+ flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config)
)
}
@@ -2113,7 +2071,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, cc, sc, ap) and
config = mid.getConfiguration() and
- flow(TNormalNode(node), _, ap, unbind(config))
+ flow(node, _, _, ap, unbind(config))
)
} or
TPathNodeSink(Node node, Configuration config) {
@@ -2164,14 +2122,31 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
+ private predicate isHidden() {
+ nodeIsHidden(this.getNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ }
+
+ private PathNode getASuccessorIfHidden() {
+ this.isHidden() and
+ result = this.(PathNodeImpl).getASuccessorImpl()
+ }
+
/** Gets a successor of this node, if any. */
- PathNode getASuccessor() { none() }
+ final PathNode getASuccessor() {
+ result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
+ not this.isHidden() and
+ not result.isHidden()
+ }
/** Holds if this node is a source. */
predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
+ abstract PathNode getASuccessorImpl();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2246,7 +2221,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
result.getConfiguration() = unbind(this.getConfiguration())
}
- override PathNodeImpl getASuccessor() {
+ override PathNodeImpl getASuccessorImpl() {
// an intermediate step to another intermediate node
result = getSuccMid()
or
@@ -2283,7 +2258,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNode getASuccessor() { none() }
+ override PathNode getASuccessorImpl() { none() }
override predicate isSource() { config.isSource(node) }
}
@@ -2293,17 +2268,18 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
* a callable is recorded by `cc`.
*/
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
- exists(
- AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
- LocalCallContext localCC
- |
- pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
- localCC = getLocalCallContext(cc, enclosing)
+ exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
+ midnode = mid.getNode() and
+ conf = mid.getConfiguration() and
+ cc = mid.getCallContext() and
+ sc = mid.getSummaryCtx() and
+ localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
ap = ap0
or
- localFlowBigStep(midnode, node, false, ap.(AccessPathNil).getType(), conf, localCC) and
+ localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -2318,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
mid.getAp() instanceof AccessPathNil and
ap = TNil(getErasedNodeTypeBound(node))
or
- exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
+ exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2332,44 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
}
pragma[nomagic]
-private predicate pathIntoLocalStep(
- PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
- AccessPath ap0, Configuration conf
-) {
- midnode = mid.getNode() and
- cc = mid.getCallContext() and
- conf = mid.getConfiguration() and
- localFlowBigStep(midnode, _, _, _, conf, _) and
- enclosing = midnode.getEnclosingCallable() and
- sc = mid.getSummaryCtx() and
- ap0 = mid.getAp()
-}
-
-pragma[nomagic]
-private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
- readDirect(node1, f, node2) and
+private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ readCandFwd(node1, tc, _, node2, config) and
flow(node2, config)
}
pragma[nomagic]
-private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
+private predicate pathReadStep(
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+) {
ap0 = mid.getAp() and
- readCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
-private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- storeDirect(node1, f, node2) and
+private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ storeCand2(node1, tc, node2, _, config) and
flow(node2, config)
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
@@ -2399,11 +2363,11 @@ private predicate pathOutOfCallable1(
}
pragma[noinline]
-private Node getAnOutNodeCand(
+private Node getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config
) {
result = kind.getAnOutNode(call) and
- flow(TNormalNode(result), _, ap, config)
+ flow(result, _, _, ap, config)
}
/**
@@ -2415,7 +2379,7 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config |
pathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = getAnOutNodeCand(kind, call, ap, config)
+ out = getAnOutNodeFlow(kind, call, ap, config)
)
}
@@ -2439,7 +2403,7 @@ private predicate parameterCand(
DataFlowCallable callable, int i, AccessPath ap, Configuration config
) {
exists(ParameterNode p |
- flow(TNormalNode(p), _, ap, config) and
+ flow(p, _, _, ap, config) and
p.isParameterOf(callable, i)
)
}
@@ -2514,7 +2478,7 @@ pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
exists(DataFlowCall call, ReturnKindExt kind |
pathThroughCallable0(call, mid, kind, cc, ap) and
- out = getAnOutNodeCand(kind, call, ap, mid.getConfiguration())
+ out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration())
)
}
@@ -2555,10 +2519,7 @@ private module FlowExploration {
viableParamArg(_, node2, node1)
or
// flow out of a callable
- exists(DataFlowCall call, ReturnKindExt kind |
- getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = kind.getAnOutNode(call)
- )
+ viableReturnPosOut(_, getReturnPosition(node1), node2)
|
c1 = node1.getEnclosingCallable() and
c2 = node2.getEnclosingCallable() and
@@ -2603,10 +2564,10 @@ private module FlowExploration {
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
- TPartialCons(Content f, int len) { len in [1 .. 5] }
+ TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
* element of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -2615,7 +2576,7 @@ private module FlowExploration {
private class PartialAccessPath extends TPartialAccessPath {
abstract string toString();
- Content getHead() { this = TPartialCons(result, _) }
+ TypedContent getHead() { this = TPartialCons(result, _) }
int len() {
this = TPartialNil(_) and result = 0
@@ -2626,7 +2587,7 @@ private module FlowExploration {
DataFlowType getType() {
this = TPartialNil(result)
or
- exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
+ exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
@@ -2644,15 +2605,15 @@ private module FlowExploration {
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
override string toString() {
- exists(Content f, int len | this = TPartialCons(f, len) |
+ exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
if len = 1
- then result = "[" + f.toString() + "]"
- else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc.toString() + "]"
+ else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
override AccessPathFront getFront() {
- exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
+ exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
}
}
@@ -2828,11 +2789,12 @@ private module FlowExploration {
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
- exists(PartialAccessPath ap0, Content f |
- partialPathReadStep(mid, ap0, f, node, cc, config) and
+ exists(PartialAccessPath ap0, TypedContent tc |
+ partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, f, ap0, config)
+ apConsFwd(ap, tc, ap0, config) and
+ compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -2851,35 +2813,42 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
+ PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
+ PartialAccessPath ap2
) {
- ap1 = mid.getAp() and
- storeDirect(mid.getNode(), f, node) and
- ap2.getHead() = f and
- ap2.len() = unbindInt(ap1.len() + 1) and
- compatibleTypes(ap1.getType(), f.getType())
+ exists(Node midNode, DataFlowType contentType |
+ midNode = mid.getNode() and
+ ap1 = mid.getAp() and
+ store(midNode, tc, node, contentType) and
+ ap2.getHead() = tc and
+ ap2.len() = unbindInt(ap1.len() + 1) and
+ compatibleTypes(ap1.getType(), contentType)
+ )
}
pragma[nomagic]
private predicate apConsFwd(
- PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
+ PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
- partialPathStoreStep(mid, ap1, f, _, ap2) and
+ partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
+ PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
- ap = mid.getAp() and
- readStep(mid.getNode(), f, node) and
- ap.getHead() = f and
- config = mid.getConfiguration() and
- cc = mid.getCallContext()
+ exists(Node midNode |
+ midNode = mid.getNode() and
+ ap = mid.getAp() and
+ read(midNode, tc.getContent(), node) and
+ ap.getHead() = tc and
+ config = mid.getConfiguration() and
+ cc = mid.getCallContext()
+ )
}
private predicate partialPathOutOfCallable0(
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
index a1daeb66411..1aeedf717f7 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
*/
predicate isBarrier(Node node) { none() }
- /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */
- deprecated predicate isBarrierEdge(Node node1, Node node2) { none() }
-
/** Holds if data flow into `node` is prohibited. */
predicate isBarrierIn(Node node) { none() }
@@ -251,15 +248,11 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
-pragma[noinline]
-private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
- viableCallable(call) = result.getCallable() and
- kind = result.getKind()
-}
-
/**
- * Holds if `node` is reachable from a source in the given configuration
- * taking simple call contexts into consideration.
+ * Holds if `node` is reachable from a source in the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call.
*/
private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) {
not fullBarrier(node, config) and
@@ -293,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
exists(Node mid |
useFieldFlow(config) and
nodeCandFwd1(mid, fromArg, config) and
- storeDirect(mid, _, node) and
+ store(mid, _, node, _) and
not outBarrier(mid, config)
)
or
// read
- exists(Content f |
- nodeCandFwd1Read(f, node, fromArg, config) and
- storeCandFwd1(f, config) and
+ exists(Content c |
+ nodeCandFwd1Read(c, node, fromArg, config) and
+ nodeCandFwd1IsStored(c, config) and
not inBarrier(node, config)
)
or
@@ -317,13 +310,35 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
fromArg = false
or
nodeCandFwd1OutFromArg(call, node, config) and
- flowOutCandFwd1(call, fromArg, config)
+ nodeCandFwd1IsEntered(call, fromArg, config)
)
)
}
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
+pragma[nomagic]
+private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
+ exists(Node mid |
+ nodeCandFwd1(mid, fromArg, config) and
+ read(mid, c, node)
+ )
+}
+
+/**
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
+ exists(Node mid, Node node, TypedContent tc |
+ not fullBarrier(node, config) and
+ useFieldFlow(config) and
+ nodeCandFwd1(mid, config) and
+ store(mid, tc, node, _) and
+ c = tc.getContent()
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1ReturnPosition(
ReturnPosition pos, boolean fromArg, Configuration config
@@ -335,43 +350,10 @@ private predicate nodeCandFwd1ReturnPosition(
}
pragma[nomagic]
-private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
- exists(Node mid |
- nodeCandFwd1(mid, fromArg, config) and
- readDirect(mid, f, node)
- )
-}
-
-/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
- */
-pragma[nomagic]
-private predicate storeCandFwd1(Content f, Configuration config) {
- exists(Node mid, Node node |
- not fullBarrier(node, config) and
- useFieldFlow(config) and
- nodeCandFwd1(mid, config) and
- storeDirect(mid, f, node)
- )
-}
-
-pragma[nomagic]
-private predicate nodeCandFwd1ReturnKind(
- DataFlowCall call, ReturnKindExt kind, boolean fromArg, Configuration config
-) {
+private predicate nodeCandFwd1Out(DataFlowCall call, Node out, boolean fromArg, Configuration config) {
exists(ReturnPosition pos |
nodeCandFwd1ReturnPosition(pos, fromArg, config) and
- pos = viableReturnPos(call, kind)
- )
-}
-
-pragma[nomagic]
-private predicate nodeCandFwd1Out(
- DataFlowCall call, Node node, boolean fromArg, Configuration config
-) {
- exists(ReturnKindExt kind |
- nodeCandFwd1ReturnKind(call, kind, fromArg, config) and
- node = kind.getAnOutNode(call)
+ viableReturnPosOut(call, pos, out)
)
}
@@ -384,7 +366,7 @@ private predicate nodeCandFwd1OutFromArg(DataFlowCall call, Node node, Configura
* Holds if an argument to `call` is reached in the flow covered by `nodeCandFwd1`.
*/
pragma[nomagic]
-private predicate flowOutCandFwd1(DataFlowCall call, boolean fromArg, Configuration config) {
+private predicate nodeCandFwd1IsEntered(DataFlowCall call, boolean fromArg, Configuration config) {
exists(ArgumentNode arg |
nodeCandFwd1(arg, fromArg, config) and
viableParamArg(call, _, arg)
@@ -395,8 +377,11 @@ bindingset[result, b]
private boolean unbindBool(boolean b) { result != b.booleanNot() }
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is part of a path from a source to a sink in the
+ * configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink.
*/
pragma[nomagic]
private predicate nodeCand1(Node node, boolean toReturn, Configuration config) {
@@ -433,81 +418,86 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
)
or
// store
- exists(Content f |
- nodeCand1Store(f, node, toReturn, config) and
- readCand1(f, config)
+ exists(Content c |
+ nodeCand1Store(c, node, toReturn, config) and
+ nodeCand1IsRead(c, config)
)
or
// read
- exists(Node mid, Content f |
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ exists(Node mid, Content c |
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, toReturn, config)
)
or
// flow into a callable
exists(DataFlowCall call |
- nodeCand1Arg(call, node, false, config) and
+ nodeCand1In(call, node, false, config) and
toReturn = false
or
- nodeCand1ArgToReturn(call, node, config) and
- flowInCand1(call, toReturn, config)
+ nodeCand1InToReturn(call, node, config) and
+ nodeCand1IsReturned(call, toReturn, config)
)
or
// flow out of a callable
exists(ReturnPosition pos |
- nodeCand1ReturnPosition(pos, config) and
+ nodeCand1Out(pos, config) and
getReturnPosition(node) = pos and
toReturn = true
)
}
-pragma[nomagic]
-private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _, config) }
-
-pragma[nomagic]
-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`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate readCand1(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content c, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd1(node, unbind(config)) and
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, _, config)
)
}
pragma[nomagic]
-private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
- exists(Node mid |
+private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
+ exists(Node mid, TypedContent tc |
nodeCand1(mid, toReturn, config) and
- storeCandFwd1(f, unbind(config)) and
- storeDirect(node, f, mid)
+ nodeCandFwd1IsStored(c, unbind(config)) and
+ store(node, tc, mid, _) and
+ c = tc.getContent()
)
}
/**
- * Holds if `f` is the target of both a read and a store in the flow covered
+ * Holds if `c` is the target of both a read and a store in the flow covered
* by `nodeCand1`.
*/
-private predicate readStoreCand1(Content f, Configuration conf) {
- readCand1(f, conf) and
- nodeCand1Store(f, _, _, conf)
+private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
+ nodeCand1IsRead(c, conf) and
+ nodeCand1Store(c, _, _, conf)
}
pragma[nomagic]
-private predicate viableParamArgCandFwd1(
+private predicate viableReturnPosOutNodeCandFwd1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+) {
+ nodeCandFwd1ReturnPosition(pos, _, config) and
+ viableReturnPosOut(call, pos, out)
+}
+
+pragma[nomagic]
+private predicate nodeCand1Out(ReturnPosition pos, Configuration config) {
+ exists(DataFlowCall call, Node out |
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, config)
+ )
+}
+
+pragma[nomagic]
+private predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
) {
viableParamArg(call, p, arg) and
@@ -515,32 +505,35 @@ private predicate viableParamArgCandFwd1(
}
pragma[nomagic]
-private predicate nodeCand1Arg(
+private predicate nodeCand1In(
DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config
) {
exists(ParameterNode p |
nodeCand1(p, toReturn, config) and
- viableParamArgCandFwd1(call, p, arg, config)
+ viableParamArgNodeCandFwd1(call, p, arg, config)
)
}
pragma[nomagic]
-private predicate nodeCand1ArgToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
- nodeCand1Arg(call, arg, true, config)
+private predicate nodeCand1InToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
+ nodeCand1In(call, arg, true, config)
}
/**
* Holds if an output from `call` is reached in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate flowInCand1(DataFlowCall call, boolean toReturn, Configuration config) {
+private predicate nodeCand1IsReturned(DataFlowCall call, boolean toReturn, Configuration config) {
exists(Node out |
nodeCand1(out, toReturn, config) and
nodeCandFwd1OutFromArg(call, out, config)
)
}
-private predicate throughFlowNodeCand(Node node, Configuration config) {
+pragma[nomagic]
+private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _, config) }
+
+private predicate throughFlowNodeCand1(Node node, Configuration config) {
nodeCand1(node, true, config) and
not fullBarrier(node, config) and
not inBarrier(node, config) and
@@ -549,11 +542,11 @@ private predicate throughFlowNodeCand(Node node, Configuration config) {
/** Holds if flow may return from `callable`. */
pragma[nomagic]
-private predicate returnFlowCallableCand(
+private predicate returnFlowCallableNodeCand1(
DataFlowCallable callable, ReturnKindExt kind, Configuration config
) {
exists(ReturnNodeExt ret |
- throughFlowNodeCand(ret, config) and
+ throughFlowNodeCand1(ret, config) and
callable = ret.getEnclosingCallable() and
kind = ret.getKind()
)
@@ -563,10 +556,10 @@ private predicate returnFlowCallableCand(
* Holds if flow may enter through `p` and reach a return node making `p` a
* candidate for the origin of a summary.
*/
-private predicate parameterThroughFlowCand(ParameterNode p, Configuration config) {
+private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration config) {
exists(ReturnKindExt kind |
- throughFlowNodeCand(p, config) and
- returnFlowCallableCand(p.getEnclosingCallable(), kind, config) and
+ throughFlowNodeCand1(p, config) and
+ returnFlowCallableNodeCand1(p.getEnclosingCallable(), kind, config) and
// we don't expect a parameter to return stored in itself
not exists(int pos |
kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos)
@@ -575,420 +568,77 @@ private predicate parameterThroughFlowCand(ParameterNode p, Configuration config
}
pragma[nomagic]
-private predicate store(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
- nodeCand1(n2, unbind(config)) and
- (
- storeDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentSome(f), n2)
- )
-}
-
-pragma[nomagic]
-private predicate read(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
- nodeCand1(n2, unbind(config)) and
- (
- readDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentSome(f), TContentNone(), n2)
- )
-}
-
-/**
- * Holds if `p` can flow to `node` in the same callable with `summary`
- * representing the flow path. The type of the tracked object is `t2`, and if
- * the summary includes a store step, `t1` is the tracked type just prior to the
- * store, that is, the type of the stored object, otherwise `t1` is equal to `t2`.
- */
-pragma[nomagic]
-private predicate parameterFlow(
- ParameterNode p, Node node, DataFlowType t1, DataFlowType t2, Summary summary,
- Configuration config
-) {
- parameterThroughFlowCand(p, config) and
- p = node and
- t1 = getErasedNodeTypeBound(node) and
- t1 = t2 and
- summary = TSummaryVal()
- or
- throughFlowNodeCand(node, unbind(config)) and
- (
- exists(Node mid |
- parameterFlow(p, mid, t1, t2, summary, config) and
- localFlowStep(mid, node, config) and
- compatibleTypes(t2, getErasedNodeTypeBound(node))
- )
- or
- exists(Node mid, Summary midsum |
- parameterFlow(p, mid, _, _, midsum, config) and
- additionalLocalFlowStep(mid, node, config) and
- t1 = getErasedNodeTypeBound(node) and
- t1 = t2 and
- summary = midsum.additionalStep()
- )
- or
- // read step
- exists(Node mid, Content f, Summary midsum |
- parameterFlow(p, mid, _, _, midsum, config) and
- read(mid, f, node, config) and
- summary = midsum.readStep(f) and
- t1 = f.getType() and
- t1 = t2
- )
- or
- // store step
- exists(Node mid, Content f, Summary midsum |
- parameterFlow(p, mid, t1, /* t1 */ _, midsum, config) and
- store(mid, f, node, config) and
- summary = midsum.storeStep(f) and
- compatibleTypes(t1, f.getType()) and
- t2 = f.getContainerType()
- )
- or
- // value flow through a callable
- exists(Node arg |
- parameterFlow(p, arg, t1, t2, summary, config) and
- argumentValueFlowsThrough(_, arg, TContentNone(), TContentNone(), node) and
- compatibleTypes(t2, getErasedNodeTypeBound(node))
- )
- or
- // flow through a callable
- exists(Node arg, Summary s1, Summary s2 |
- parameterFlow(p, arg, _, _, s1, config) and
- argumentFlowsThrough(arg, node, t1, t2, s2, config) and
- summary = s1.compose(s2)
- )
- )
-}
-
-private predicate viableParamArgCand(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
-) {
- viableParamArg(call, p, arg) and
- nodeCand1(arg, unbind(config)) and
- nodeCand1(p, config) and
- not outBarrier(arg, config) and
- not inBarrier(p, config)
-}
-
-pragma[nomagic]
-private predicate parameterFlowReturn(
- ParameterNode p, ReturnNodeExt ret, ReturnKindExt kind, DataFlowType t1, DataFlowType t2,
- Summary summary, Configuration config
-) {
- parameterFlow(p, ret, t1, t2, summary, config) and
- kind = ret.getKind() and
- not summary.isPartial() and
- not exists(int pos | kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos))
-}
-
-pragma[nomagic]
-private predicate argumentFlowsThrough0(
- DataFlowCall call, ArgumentNode arg, ReturnKindExt kind, DataFlowType t1, DataFlowType t2,
- Summary summary, Configuration config
-) {
- exists(ParameterNode p |
- viableParamArgCand(call, p, arg, config) and
- parameterFlowReturn(p, _, kind, t1, t2, summary, config)
- )
-}
-
-/**
- * Holds if data can flow from `arg` to `out` through a call with `summary`
- * representing the flow path. The type of the tracked object is `t2`, and if
- * the summary includes a store step, `t1` is the tracked type just prior to the
- * store, that is, the type of the stored object, otherwise `t1` is equal to `t2`.
- */
-private predicate argumentFlowsThrough(
- ArgumentNode arg, Node out, DataFlowType t1, DataFlowType t2, Summary summary,
- Configuration config
-) {
- nodeCand1(out, unbind(config)) and
- not inBarrier(out, config) and
- compatibleTypes(t2, getErasedNodeTypeBound(out)) and
- exists(DataFlowCall call, ReturnKindExt kind |
- argumentFlowsThrough0(call, arg, kind, t1, t2, summary, config) and
- out = kind.getAnOutNode(call)
- )
-}
-
-pragma[noinline]
-private predicate readStoreNode(
- DataFlowCall call, ArgumentNode arg, Content f1, Configuration config
-) {
- exists(Content f2, Node out |
- argumentValueFlowsThrough(call, arg, TContentSome(f1), TContentSome(f2), out) and
- nodeCand1(out, config) and
- readStoreCand1(f2, unbind(config))
- )
-}
-
-private newtype TNodeExt =
- TNormalNode(Node node) { nodeCand1(node, _) } or
- TReadStoreNode(DataFlowCall call, ArgumentNode arg, Content f1, Configuration config) {
- nodeCand1(arg, config) and
- readStoreNode(call, arg, f1, config) and
- readStoreCand1(f1, unbind(config))
- } or
- TReadTaintNode(ArgumentNode arg, Content f, Configuration config) {
- argumentFlowsThrough(arg, _, _, _, TSummaryReadTaint(f), config)
- } or
- TTaintStoreNode(ArgumentNode arg, DataFlowType t, Configuration config) {
- argumentFlowsThrough(arg, _, t, _, TSummaryTaintStore(_), config)
- }
-
-/**
- * An extended data flow node. Either a normal node, or an intermediate node
- * used to split up a summarized flow steps.
- *
- * This is purely an internal implementation detail.
- */
-abstract private class NodeExt extends TNodeExt {
- /** Gets the underlying (normal) node, if any. */
- abstract Node getNode();
-
- abstract DataFlowType getErasedNodeTypeBound();
-
- abstract DataFlowCallable getEnclosingCallable();
-
- abstract predicate isCand1(Configuration config);
-
- abstract string toString();
-
- abstract predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- );
-}
-
-/** A `Node` at which a cast can occur such that the type should be checked. */
-abstract private class CastingNodeExt extends NodeExt { }
-
-private class NormalNodeExt extends NodeExt, TNormalNode {
- override Node getNode() { this = TNormalNode(result) }
-
- override DataFlowType getErasedNodeTypeBound() {
- result = getErasedRepr(this.getNode().getTypeBound())
- }
-
- override DataFlowCallable getEnclosingCallable() {
- result = this.getNode().getEnclosingCallable()
- }
-
- override predicate isCand1(Configuration config) { nodeCand1(this.getNode(), config) }
-
- override string toString() { result = this.getNode().toString() }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class NormalCastingNodeExt extends CastingNodeExt, NormalNodeExt {
- NormalCastingNodeExt() { this.getNode() instanceof CastingNode }
-}
-
-private class ReadStoreNodeExt extends CastingNodeExt, TReadStoreNode {
- private DataFlowCall call;
- private ArgumentNode arg;
- private Content f1;
- private Configuration config0;
-
- ReadStoreNodeExt() { this = TReadStoreNode(call, arg, f1, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = f1.getType() }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = "(inside) " + call.toString() + " [read " + f1 + "]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class ReadTaintNode extends NodeExt, TReadTaintNode {
- private ArgumentNode arg;
- private Content f;
- private Configuration config0;
-
- ReadTaintNode() { this = TReadTaintNode(arg, f, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = f.getType() }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = arg.toString() + " [read taint " + f + "]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class TaintStoreNode extends NodeExt, TTaintStoreNode {
- private ArgumentNode arg;
- private DataFlowType t;
- private Configuration config0;
-
- TaintStoreNode() { this = TTaintStoreNode(arg, t, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = t }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = arg.toString() + " [taint store]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private predicate additionalLocalFlowStepExt(
- NodeExt node1, NodeExt node2, DataFlowType t, Configuration config
-) {
- exists(ArgumentNode arg, Content f |
- node1 = TReadTaintNode(arg, f, config) and
- argumentFlowsThrough(arg, node2.getNode(), _, t, TSummaryReadTaint(f), config)
- )
- or
- node2 = TTaintStoreNode(node1.getNode(), t, config)
-}
-
-pragma[nomagic]
-private predicate readExt(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- read(node1.getNode(), f, node2.getNode(), config)
- or
- node2 = TReadStoreNode(_, node1.getNode(), f, config)
- or
- node2 = TReadTaintNode(node1.getNode(), f, config)
-}
-
-pragma[nomagic]
-private predicate storeExt(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- store(node1.getNode(), f, node2.getNode(), config)
- or
- exists(DataFlowCall call, ArgumentNode arg, Content f1, Node n2 |
- node1 = TReadStoreNode(call, arg, f1, config) and
- n2 = node2.getNode() and
- argumentValueFlowsThrough(call, arg, TContentSome(f1), TContentSome(f), n2) and
+private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
+ exists(TypedContent tc |
+ nodeCand1IsReadAndStored(c, config) and
nodeCand1(n2, unbind(config)) and
- readStoreCand1(f, unbind(config))
- )
- or
- exists(ArgumentNode arg, DataFlowType t |
- node1 = TTaintStoreNode(arg, t, config) and
- argumentFlowsThrough(arg, node2.getNode(), t, _, TSummaryTaintStore(f), config)
+ store(n1, tc, n2, _) and
+ c = tc.getContent()
)
}
-private predicate jumpStepExt(NodeExt node1, NodeExt node2, Configuration config) {
- jumpStep(node1.getNode(), node2.getNode(), config)
-}
-
-private predicate additionalJumpStepExt(NodeExt node1, NodeExt node2, Configuration config) {
- additionalJumpStep(node1.getNode(), node2.getNode(), config)
-}
-
-private predicate argumentValueFlowsThrough(NodeExt node1, NodeExt node2) {
- argumentValueFlowsThrough(_, node1.getNode(), TContentNone(), TContentNone(), node2.getNode())
-}
-
-private predicate argumentFlowsThrough(
- NodeExt arg, NodeExt out, DataFlowType t, Configuration config
-) {
- argumentFlowsThrough(arg.getNode(), out.getNode(), _, t, TSummaryTaint(), config)
-}
-
-/**
- * Holds if data can flow from `node1` to `node2` in one local step or a step
- * through a callable.
- */
-pragma[noinline]
-private predicate localFlowStepOrFlowThroughCallable(
- NodeExt node1, NodeExt node2, Configuration config
-) {
- exists(Node n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode()
- |
- nodeCand1(n1, config) and
- localFlowStep(n1, n2, config)
- or
- nodeCand1(n1, config) and
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentNone(), n2)
- )
-}
-
-/**
- * Holds if data can flow from `node1` to `node2` in one local step or a step
- * through a callable, in both cases using an additional flow step from the
- * configuration.
- */
-pragma[noinline]
-private predicate additionalLocalFlowStepOrFlowThroughCallable(
- NodeExt node1, NodeExt node2, Configuration config
-) {
- exists(Node n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode()
- |
- nodeCand1(n1, config) and
- additionalLocalFlowStep(n1, n2, config)
- or
- argumentFlowsThrough(n1, n2, _, _, TSummaryTaint(), config)
- )
- or
- additionalLocalFlowStepExt(node1, node2, _, config)
+pragma[nomagic]
+private predicate read(Node n1, Content c, Node n2, Configuration config) {
+ nodeCand1IsReadAndStored(c, config) and
+ nodeCand1(n2, unbind(config)) and
+ read(n1, c, n2)
}
pragma[noinline]
-private ReturnPosition getReturnPosition1(ReturnNodeExt node, Configuration config) {
- result = getReturnPosition(node) and
- nodeCand1(node, config)
+private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ localFlowStep(node1, node2, config)
+}
+
+pragma[noinline]
+private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ additionalLocalFlowStep(node1, node2, config)
+}
+
+pragma[nomagic]
+private predicate viableReturnPosOutNodeCand1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+) {
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, config)
}
/**
- * Holds if data can flow out of a callable from `node1` to `node2`, either
+ * Holds if data can flow out of `call` from `ret` to `out`, 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 flowOutOfCallableNodeCand1(ReturnNodeExt node1, Node node2, Configuration config) {
- nodeCand1(node2, config) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- exists(DataFlowCall call, ReturnKindExt kind |
- getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
- node2 = kind.getAnOutNode(call)
- )
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand1(
+ DataFlowCall call, ReturnNodeExt ret, Node out, Configuration config
+) {
+ viableReturnPosOutNodeCand1(call, getReturnPosition(ret), out, config) and
+ nodeCand1(ret, config) and
+ not outBarrier(ret, config) and
+ not inBarrier(out, config)
+}
+
+pragma[nomagic]
+private predicate viableParamArgNodeCand1(
+ DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+) {
+ viableParamArgNodeCandFwd1(call, p, arg, config) and
+ nodeCand1(arg, config)
}
/**
- * Holds if data can flow into a callable and that this step is part of a
+ * Holds if data can flow into `call` and that this step is part of a
* path from a source to a sink.
*/
-private predicate flowIntoCallableNodeCand1(
- ArgumentNode node1, ParameterNode node2, Configuration config
+pragma[nomagic]
+private predicate flowIntoCallNodeCand1(
+ DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config
) {
- viableParamArgCand(_, node2, node1, config)
+ viableParamArgNodeCand1(call, p, arg, config) and
+ nodeCand1(p, config) and
+ not outBarrier(arg, config) and
+ not inBarrier(p, config)
}
/**
@@ -999,7 +649,7 @@ private predicate flowIntoCallableNodeCand1(
private int branch(Node n1, Configuration conf) {
result =
strictcount(Node n |
- flowOutOfCallableNodeCand1(n1, n, conf) or flowIntoCallableNodeCand1(n1, n, conf)
+ flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1011,269 +661,391 @@ private int branch(Node n1, Configuration conf) {
private int join(Node n2, Configuration conf) {
result =
strictcount(Node n |
- flowOutOfCallableNodeCand1(n, n2, conf) or flowIntoCallableNodeCand1(n, n2, conf)
+ flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
/**
- * Holds if data can flow out of a callable from `node1` to `node2`, either
+ * Holds if data can flow out of `call` from `ret` to `out`, 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. The
* `allowsFieldFlow` flag indicates whether the branching is within the limit
* specified by the configuration.
*/
-private predicate flowOutOfCallableNodeCand1(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand1(
+ DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config
) {
- exists(ReturnNodeExt n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode() and
- flowOutOfCallableNodeCand1(n1, n2, config) and
- exists(int b, int j |
- b = branch(n1, config) and
- j = join(n2, config) and
- if b.minimum(j) <= config.fieldFlowBranchLimit()
- then allowsFieldFlow = true
- else allowsFieldFlow = false
- )
+ flowOutOfCallNodeCand1(call, ret, out, config) and
+ exists(int b, int j |
+ b = branch(ret, config) and
+ j = join(out, config) and
+ if b.minimum(j) <= config.fieldFlowBranchLimit()
+ then allowsFieldFlow = true
+ else allowsFieldFlow = false
)
}
/**
- * Holds if data can flow into a callable and that this step is part of a
+ * Holds if data can flow into `call` and that this step is part of a
* path from a source to a sink. The `allowsFieldFlow` flag indicates whether
* the branching is within the limit specified by the configuration.
*/
-private predicate flowIntoCallableNodeCand1(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+pragma[nomagic]
+private predicate flowIntoCallNodeCand1(
+ DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow,
+ Configuration config
) {
- exists(ArgumentNode n1, ParameterNode n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode() and
- flowIntoCallableNodeCand1(n1, n2, config) and
- exists(int b, int j |
- b = branch(n1, config) and
- j = join(n2, config) and
- if b.minimum(j) <= config.fieldFlowBranchLimit()
- then allowsFieldFlow = true
- else allowsFieldFlow = false
- )
+ flowIntoCallNodeCand1(call, arg, p, config) and
+ exists(int b, int j |
+ b = branch(arg, config) and
+ j = join(p, config) and
+ if b.minimum(j) <= config.fieldFlowBranchLimit()
+ then allowsFieldFlow = true
+ else allowsFieldFlow = false
)
}
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is reachable from a source in the configuration `config`.
+ * The Boolean `stored` records whether the tracked value is stored into a
+ * field of `node`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argStored` records whether the tracked
+ * value was stored into a field of the argument.
*/
-private predicate nodeCandFwd2(NodeExt node, boolean fromArg, boolean stored, Configuration config) {
- nodeCand1(node.getNode(), config) and
- config.isSource(node.getNode()) and
+private predicate nodeCandFwd2(
+ Node node, boolean fromArg, BooleanOption argStored, boolean stored, Configuration config
+) {
+ nodeCand1(node, config) and
+ config.isSource(node) and
fromArg = false and
+ argStored = TBooleanNone() and
stored = false
or
- node.isCand1(unbind(config)) and
+ nodeCand1(node, unbind(config)) and
(
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, stored, config) and
- localFlowStepOrFlowThroughCallable(mid, node, config)
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, stored, config) and
+ localFlowStepNodeCand1(mid, node, config)
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, stored, config) and
- additionalLocalFlowStepOrFlowThroughCallable(mid, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, stored, config) and
+ additionalLocalFlowStepNodeCand1(mid, node, config) and
stored = false
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, _, stored, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
+ exists(Node mid |
+ nodeCandFwd2(mid, _, _, stored, config) and
+ jumpStep(mid, node, config) and
+ fromArg = false and
+ argStored = TBooleanNone()
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, _, stored, config) and
- additionalJumpStepExt(mid, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, _, _, stored, config) and
+ additionalJumpStep(mid, node, config) and
fromArg = false and
+ argStored = TBooleanNone() and
stored = false
)
or
// store
- exists(NodeExt mid, Content f |
- nodeCandFwd2(mid, fromArg, _, config) and
- storeExt(mid, f, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, _, config) and
+ storeCand1(mid, _, node, config) and
stored = true
)
or
// read
- exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, config) and
- storeCandFwd2(f, stored, config)
+ exists(Content c |
+ nodeCandFwd2Read(c, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(c, stored, config)
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- nodeCandFwd2(mid, _, stored, config) and
- flowIntoCallableNodeCand1(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (stored = false or allowsFieldFlow = true)
- )
+ // flow into a callable
+ nodeCandFwd2In(_, node, _, _, stored, config) and
+ fromArg = true and
+ if parameterThroughFlowNodeCand1(node, config)
+ then argStored = TBooleanSome(stored)
+ else argStored = TBooleanNone()
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- nodeCandFwd2(mid, false, stored, config) and
- flowOutOfCallableNodeCand1(mid, node, allowsFieldFlow, config) and
- fromArg = false and
- (stored = false or allowsFieldFlow = true)
+ // flow out of a callable
+ exists(DataFlowCall call |
+ nodeCandFwd2Out(call, node, fromArg, argStored, stored, config) and
+ fromArg = false
+ or
+ exists(boolean argStored0 |
+ nodeCandFwd2OutFromArg(call, node, argStored0, stored, config) and
+ nodeCandFwd2IsEntered(call, fromArg, argStored, argStored0, config)
+ )
)
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
*/
pragma[noinline]
-private predicate storeCandFwd2(Content f, boolean stored, Configuration config) {
- exists(NodeExt mid, NodeExt node |
+private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
+ exists(Node mid, Node node |
useFieldFlow(config) and
- node.isCand1(unbind(config)) and
- nodeCandFwd2(mid, _, stored, config) and
- storeExt(mid, f, node, config)
+ nodeCand1(node, unbind(config)) and
+ nodeCandFwd2(mid, _, _, stored, config) and
+ storeCand1(mid, c, node, config)
)
}
pragma[nomagic]
-private predicate nodeCandFwd2Read(Content f, NodeExt node, boolean fromArg, Configuration config) {
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, true, config) and
- readExt(mid, f, node, config)
+private predicate nodeCandFwd2Read(
+ Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+) {
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, true, config) and
+ read(mid, c, node, config)
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2In(
+ DataFlowCall call, ParameterNode p, boolean fromArg, BooleanOption argStored, boolean stored,
+ Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ nodeCandFwd2(arg, fromArg, argStored, stored, config) and
+ flowIntoCallNodeCand1(call, arg, p, allowsFieldFlow, config)
+ |
+ stored = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2Out(
+ DataFlowCall call, Node out, boolean fromArg, BooleanOption argStored, boolean stored,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ nodeCandFwd2(ret, fromArg, argStored, stored, config) and
+ flowOutOfCallNodeCand1(call, ret, out, allowsFieldFlow, config)
+ |
+ stored = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2OutFromArg(
+ DataFlowCall call, Node out, boolean argStored, boolean stored, Configuration config
+) {
+ nodeCandFwd2Out(call, out, true, TBooleanSome(argStored), stored, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `nodeCandFwd2`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd2IsEntered(
+ DataFlowCall call, boolean fromArg, BooleanOption argStored, boolean stored, Configuration config
+) {
+ exists(ParameterNode p |
+ nodeCandFwd2In(call, p, fromArg, argStored, stored, config) and
+ parameterThroughFlowNodeCand1(p, config)
)
}
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is part of a path from a source to a sink in the
+ * configuration `config`. The Boolean `read` records whether the tracked
+ * value must be read from a field of `node` in order to reach a sink.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnRead`
+ * records whether a field must be read from the returned value.
*/
-private predicate nodeCand2(NodeExt node, boolean toReturn, boolean read, Configuration config) {
- nodeCandFwd2(node, _, false, config) and
- config.isSink(node.getNode()) and
+private predicate nodeCand2(
+ Node node, boolean toReturn, BooleanOption returnRead, boolean read, Configuration config
+) {
+ nodeCandFwd2(node, _, _, false, config) and
+ config.isSink(node) and
toReturn = false and
+ returnRead = TBooleanNone() and
read = false
or
- nodeCandFwd2(node, _, unbindBool(read), unbind(config)) and
+ nodeCandFwd2(node, _, _, unbindBool(read), unbind(config)) and
(
- exists(NodeExt mid |
- localFlowStepOrFlowThroughCallable(node, mid, config) and
- nodeCand2(mid, toReturn, read, config)
+ exists(Node mid |
+ localFlowStepNodeCand1(node, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, read, config)
)
or
- exists(NodeExt mid |
- additionalLocalFlowStepOrFlowThroughCallable(node, mid, config) and
- nodeCand2(mid, toReturn, read, config) and
+ exists(Node mid |
+ additionalLocalFlowStepNodeCand1(node, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, read, config) and
read = false
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- nodeCand2(mid, _, read, config) and
- toReturn = false
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ nodeCand2(mid, _, _, read, config) and
+ toReturn = false and
+ returnRead = TBooleanNone()
)
or
- exists(NodeExt mid |
- additionalJumpStepExt(node, mid, config) and
- nodeCand2(mid, _, read, config) and
+ exists(Node mid |
+ additionalJumpStep(node, mid, config) and
+ nodeCand2(mid, _, _, read, config) and
toReturn = false and
+ returnRead = TBooleanNone() and
read = false
)
or
// store
- exists(Content f |
- nodeCand2Store(f, node, toReturn, read, config) and
- readCand2(f, read, config)
+ exists(Content c |
+ nodeCand2Store(c, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(c, read, config)
)
or
// read
- exists(NodeExt mid, Content f, boolean read0 |
- readExt(node, f, mid, config) and
- storeCandFwd2(f, unbindBool(read0), unbind(config)) and
- nodeCand2(mid, toReturn, read0, config) and
+ exists(Node mid, Content c, boolean read0 |
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
+ nodeCand2(mid, toReturn, returnRead, read0, config) and
read = true
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand1(node, mid, allowsFieldFlow, config) and
- nodeCand2(mid, false, read, config) and
- toReturn = false and
- (read = false or allowsFieldFlow = true)
+ // flow into a callable
+ exists(DataFlowCall call |
+ nodeCand2In(call, node, toReturn, returnRead, read, config) and
+ toReturn = false
+ or
+ exists(boolean returnRead0 |
+ nodeCand2InToReturn(call, node, returnRead0, read, config) and
+ nodeCand2IsReturned(call, toReturn, returnRead, returnRead0, config)
+ )
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand1(node, mid, allowsFieldFlow, config) and
- nodeCand2(mid, _, read, config) and
- toReturn = true and
- (read = false or allowsFieldFlow = true)
- )
+ // flow out of a callable
+ nodeCand2Out(_, node, _, _, read, config) and
+ toReturn = true and
+ if nodeCandFwd2(node, true, TBooleanSome(_), unbindBool(read), config)
+ then returnRead = TBooleanSome(read)
+ else returnRead = TBooleanNone()
)
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate readCand2(Content f, boolean read, Configuration config) {
- exists(NodeExt mid, NodeExt node |
+private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
+ exists(Node mid, Node node |
useFieldFlow(config) and
- nodeCandFwd2(node, _, true, unbind(config)) and
- readExt(node, f, mid, config) and
- storeCandFwd2(f, unbindBool(read), unbind(config)) and
- nodeCand2(mid, _, read, config)
+ nodeCandFwd2(node, _, _, true, unbind(config)) and
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
+ nodeCand2(mid, _, _, read, config)
)
}
pragma[nomagic]
private predicate nodeCand2Store(
- Content f, NodeExt node, boolean toReturn, boolean stored, Configuration config
+ Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
+ Configuration config
) {
- exists(NodeExt mid |
- storeExt(node, f, mid, config) and
- nodeCand2(mid, toReturn, true, config) and
- nodeCandFwd2(node, _, stored, unbind(config))
- )
-}
-
-pragma[nomagic]
-private predicate storeCand2(Content f, boolean stored, Configuration conf) {
- exists(NodeExt node |
- nodeCand2Store(f, node, _, stored, conf) and
- nodeCand2(node, _, stored, conf)
+ exists(Node mid |
+ storeCand1(node, c, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, true, config) and
+ nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
/**
- * Holds if `f` is the target of both a store and a read in the path graph
- * covered by `nodeCand2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
*/
-pragma[noinline]
-private predicate readStoreCand(Content f, Configuration conf) {
- exists(boolean apNonEmpty |
- storeCand2(f, apNonEmpty, conf) and
- readCand2(f, apNonEmpty, conf)
+pragma[nomagic]
+private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
+ exists(Node node |
+ nodeCand2Store(c, node, _, _, stored, conf) and
+ nodeCand2(node, _, _, stored, conf)
)
}
-private predicate nodeCand2(NodeExt node, Configuration config) { nodeCand2(node, _, _, config) }
+/**
+ * Holds if `c` is the target of both a store and a read in the path graph
+ * covered by `nodeCand2`.
+ */
+pragma[noinline]
+private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
+ exists(boolean apNonEmpty |
+ nodeCand2IsStored(c, apNonEmpty, conf) and
+ nodeCand2IsRead(c, apNonEmpty, conf)
+ )
+}
pragma[nomagic]
-private predicate flowOutOfCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate nodeCand2Out(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
) {
- flowOutOfCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ exists(Node out, boolean allowsFieldFlow |
+ nodeCand2(out, toReturn, returnRead, read, config) and
+ flowOutOfCallNodeCand1(call, ret, out, allowsFieldFlow, config)
+ |
+ read = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCand2In(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ nodeCand2(p, toReturn, returnRead, read, config) and
+ flowIntoCallNodeCand1(call, arg, p, allowsFieldFlow, config)
+ |
+ read = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCand2InToReturn(
+ DataFlowCall call, ArgumentNode arg, boolean returnRead, boolean read, Configuration config
+) {
+ nodeCand2In(call, arg, true, TBooleanSome(returnRead), read, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `nodeCand2`.
+ */
+pragma[nomagic]
+private predicate nodeCand2IsReturned(
+ DataFlowCall call, boolean toReturn, BooleanOption returnRead, boolean read, Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ nodeCand2Out(call, ret, toReturn, returnRead, read, config) and
+ nodeCandFwd2(ret, true, TBooleanSome(_), read, config)
+ )
+}
+
+private predicate nodeCand2(Node node, Configuration config) { nodeCand2(node, _, _, _, config) }
+
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand2(
+ DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
+) {
+ flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
nodeCand2(node2, config) and
nodeCand2(node1, unbind(config))
}
pragma[nomagic]
-private predicate flowIntoCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate flowIntoCallNodeCand2(
+ DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ Configuration config
) {
- flowIntoCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
nodeCand2(node2, config) and
nodeCand2(node1, unbind(config))
}
@@ -1284,15 +1056,15 @@ private module LocalFlowBigStep {
* flow steps in a dataflow path.
*/
private predicate localFlowEntry(Node node, Configuration config) {
- nodeCand2(TNormalNode(node), config) and
+ nodeCand2(node, config) and
(
config.isSource(node) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
node instanceof ParameterNode or
- node instanceof OutNode or
- node instanceof PostUpdateNode or
- readDirect(_, _, node) or
+ node instanceof OutNodeExt or
+ store(_, _, node, _) or
+ read(_, _, node) or
node instanceof CastNode
)
}
@@ -1302,15 +1074,13 @@ private module LocalFlowBigStep {
* flow steps in a dataflow path.
*/
private predicate localFlowExit(Node node, Configuration config) {
- exists(Node next | nodeCand2(TNormalNode(next), config) |
+ exists(Node next | nodeCand2(next, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
- flowIntoCallableNodeCand1(node, next, config) or
- flowOutOfCallableNodeCand1(node, next, config) or
- argumentFlowsThrough(node, next, _, _, _, config) or
- argumentValueFlowsThrough(_, node, TContentNone(), TContentNone(), next) or
- storeDirect(node, _, next) or
- readDirect(node, _, next)
+ flowIntoCallNodeCand1(_, node, next, config) or
+ flowOutOfCallNodeCand1(_, node, next, config) or
+ store(node, _, next, _) or
+ read(node, _, next)
)
or
node instanceof CastNode
@@ -1318,6 +1088,13 @@ private module LocalFlowBigStep {
config.isSink(node)
}
+ pragma[noinline]
+ private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
+ additionalLocalFlowStepNodeCand1(node1, node2, config) and
+ nodeCand2(node1, _, _, false, config) and
+ nodeCand2(node2, _, _, false, unbind(config))
+ }
+
/**
* Holds if the local path from `node1` to `node2` is a prefix of a maximal
* subsequence of local flow steps in a dataflow path.
@@ -1334,33 +1111,33 @@ private module LocalFlowBigStep {
(
localFlowEntry(node1, config) and
(
- localFlowStep(node1, node2, config) and
+ localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
t = getErasedNodeTypeBound(node1)
or
- additionalLocalFlowStep(node1, node2, config) and
+ additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
t = getErasedNodeTypeBound(node2)
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
or
exists(Node mid |
localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
- localFlowStep(mid, node2, config) and
+ localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof CastNode and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
)
or
exists(Node mid |
localFlowStepPlus(node1, mid, _, _, config, cc) and
- additionalLocalFlowStep(mid, node2, config) and
+ additionalLocalFlowStepNodeCand2(mid, node2, config) and
not mid instanceof CastNode and
preservesValue = false and
t = getErasedNodeTypeBound(node2) and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
)
)
}
@@ -1371,311 +1148,380 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config,
+ Node node1, Node node2, boolean preservesValue, AccessPathFrontNil apf, Configuration config,
LocalCallContext callContext
) {
- localFlowStepPlus(node1, node2, preservesValue, t, config, callContext) and
+ localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, config)
}
-
- pragma[nomagic]
- predicate localFlowBigStepExt(
- NodeExt node1, NodeExt node2, boolean preservesValue, AccessPathFrontNil apf,
- Configuration config
- ) {
- localFlowBigStep(node1.getNode(), node2.getNode(), preservesValue, apf.getType(), config, _)
- or
- additionalLocalFlowStepExt(node1, node2, apf.getType(), config) and
- nodeCand2(node1, config) and
- nodeCand2(node2, unbind(config)) and
- preservesValue = false
- }
}
private import LocalFlowBigStep
pragma[nomagic]
-private predicate readExtCand2(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- readExt(node1, f, node2, config) and
- nodeCand2(node1, _, true, unbind(config)) and
+private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
+ read(node1, c, node2, config) and
+ nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- readStoreCand(f, unbind(config))
+ nodeCand2IsReadAndStored(c, unbind(config))
}
pragma[nomagic]
-private predicate storeExtCand2(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- storeExt(node1, f, node2, config) and
+private predicate storeCand2(
+ Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+) {
+ store(node1, tc, node2, contentType) and
nodeCand2(node1, config) and
- nodeCand2(node2, _, true, unbind(config)) and
- readStoreCand(f, unbind(config))
-}
-
-private newtype TAccessPathFront =
- TFrontNil(DataFlowType t) or
- TFrontHead(Content f)
-
-/**
- * The front of an `AccessPath`. This is either a head or a nil.
- */
-abstract private class AccessPathFront extends TAccessPathFront {
- abstract string toString();
-
- abstract DataFlowType getType();
-
- abstract boolean toBoolNonEmpty();
-
- predicate headUsesContent(Content f) { this = TFrontHead(f) }
-}
-
-private class AccessPathFrontNil extends AccessPathFront, TFrontNil {
- override string toString() {
- exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
- }
-
- override DataFlowType getType() { this = TFrontNil(result) }
-
- override boolean toBoolNonEmpty() { result = false }
-}
-
-private class AccessPathFrontHead extends AccessPathFront, TFrontHead {
- override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) }
-
- override DataFlowType getType() {
- exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
- }
-
- override boolean toBoolNonEmpty() { result = true }
+ nodeCand2(node2, _, _, true, unbind(config)) and
+ nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
}
/**
- * Holds if data can flow from a source to `node` with the given `apf`.
+ * Holds if `node` is reachable with access path front `apf` from a
+ * source in the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argApf` records the front of the
+ * access path of that argument.
*/
pragma[nomagic]
private predicate flowCandFwd(
- NodeExt node, boolean fromArg, AccessPathFront apf, Configuration config
+ Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
) {
- flowCandFwd0(node, fromArg, apf, config) and
- if node instanceof CastingNodeExt
- then compatibleTypes(node.getErasedNodeTypeBound(), apf.getType())
+ flowCandFwd0(node, fromArg, argApf, apf, config) and
+ if node instanceof CastingNode
+ then compatibleTypes(getErasedNodeTypeBound(node), apf.getType())
else any()
}
pragma[nomagic]
private predicate flowCandFwd0(
- NodeExt node, boolean fromArg, AccessPathFront apf, Configuration config
+ Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
) {
- nodeCand2(node, _, false, config) and
- config.isSource(node.getNode()) and
+ nodeCand2(node, _, _, false, config) and
+ config.isSource(node) and
fromArg = false and
- apf = TFrontNil(node.getErasedNodeTypeBound())
+ argApf = TAccessPathFrontNone() and
+ apf = TFrontNil(getErasedNodeTypeBound(node))
or
- exists(NodeExt mid |
- flowCandFwd(mid, fromArg, apf, config) and
- localFlowBigStepExt(mid, node, true, _, config)
+ exists(Node mid |
+ flowCandFwd(mid, fromArg, argApf, apf, config) and
+ localFlowBigStep(mid, node, true, _, config, _)
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(mid, fromArg, nil, config) and
- localFlowBigStepExt(mid, node, false, apf, config)
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(mid, fromArg, argApf, nil, config) and
+ localFlowBigStep(mid, node, false, apf, config, _)
)
or
- nodeCand2(node, unbind(config)) and
- (
- exists(NodeExt mid |
- flowCandFwd(mid, _, apf, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(mid, _, nil, config) and
- additionalJumpStepExt(mid, node, config) and
- fromArg = false and
- apf = TFrontNil(node.getErasedNodeTypeBound())
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowCandFwd(mid, _, apf, config) and
- flowIntoCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowCandFwd(mid, false, apf, config) and
- flowOutOfCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = false and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- flowCandFwd(mid, fromArg, apf, config) and
- argumentValueFlowsThrough(mid, node)
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil, DataFlowType t |
- flowCandFwd(mid, fromArg, nil, config) and
- argumentFlowsThrough(mid, node, t, config) and
- apf = TFrontNil(t)
- )
+ exists(Node mid |
+ flowCandFwd(mid, _, _, apf, config) and
+ nodeCand2(node, unbind(config)) and
+ jumpStep(mid, node, config) and
+ fromArg = false and
+ argApf = TAccessPathFrontNone()
)
or
- exists(NodeExt mid, Content f |
- flowCandFwd(mid, fromArg, _, config) and
- storeExtCand2(mid, f, node, config) and
- nodeCand2(node, _, true, unbind(config)) and
- apf.headUsesContent(f)
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(mid, _, _, nil, config) and
+ nodeCand2(node, unbind(config)) and
+ additionalJumpStep(mid, node, config) and
+ fromArg = false and
+ argApf = TAccessPathFrontNone() and
+ apf = TFrontNil(getErasedNodeTypeBound(node))
)
or
- exists(Content f |
- flowCandFwdRead(f, node, fromArg, config) and
- consCandFwd(f, apf, config) and
- nodeCand2(node, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ // store
+ exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ storeCand2(mid, tc, node, contentType, config) and
+ nodeCand2(node, _, _, true, unbind(config)) and
+ apf.headUsesContent(tc) and
+ compatibleTypes(apf0.getType(), contentType)
+ )
+ or
+ // read
+ exists(TypedContent tc |
+ flowCandFwdRead(tc, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(tc, apf, config) and
+ nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ )
+ or
+ // flow into a callable
+ flowCandFwdIn(_, node, _, _, apf, config) and
+ fromArg = true and
+ if nodeCand2(node, true, _, unbindBool(apf.toBoolNonEmpty()), config)
+ then argApf = TAccessPathFrontSome(apf)
+ else argApf = TAccessPathFrontNone()
+ or
+ // flow out of a callable
+ exists(DataFlowCall call |
+ flowCandFwdOut(call, node, fromArg, argApf, apf, config) and
+ fromArg = false
+ or
+ exists(AccessPathFront argApf0 |
+ flowCandFwdOutFromArg(call, node, argApf0, apf, config) and
+ flowCandFwdIsEntered(call, fromArg, argApf, argApf0, config)
+ )
)
}
pragma[nomagic]
-private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) {
- exists(NodeExt mid, NodeExt n |
- flowCandFwd(mid, _, apf, config) and
- storeExtCand2(mid, f, n, config) and
- nodeCand2(n, _, true, unbind(config)) and
- compatibleTypes(apf.getType(), f.getType())
+private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n, DataFlowType contentType |
+ flowCandFwd(mid, _, _, apf, config) and
+ storeCand2(mid, tc, n, contentType, config) and
+ nodeCand2(n, _, _, true, unbind(config)) and
+ compatibleTypes(apf.getType(), contentType)
)
}
pragma[nomagic]
-private predicate flowCandFwdRead(Content f, NodeExt node, boolean fromArg, Configuration config) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowCandFwd(mid, fromArg, apf0, config) and
- readExtCand2(mid, f, node, config) and
- apf0.headUsesContent(f)
+private predicate flowCandFwdRead0(
+ Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFrontHead apf, Configuration config
+) {
+ flowCandFwd(node1, fromArg, argApf, apf, config) and
+ readCand2(node1, c, node2, config) and
+ apf.headUsesContent(tc)
+}
+
+pragma[nomagic]
+private predicate flowCandFwdRead(
+ TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+) {
+ flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
+}
+
+pragma[nomagic]
+private predicate flowCandFwdIn(
+ DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ flowCandFwd(arg, fromArg, argApf, apf, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ flowCandFwd(ret, fromArg, argApf, apf, config) and
+ flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandFwdOutFromArg(
+ DataFlowCall call, Node node, AccessPathFront argApf, AccessPathFront apf, Configuration config
+) {
+ flowCandFwdOut(call, node, true, TAccessPathFrontSome(argApf), apf, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `flowCandFwd`.
+ */
+pragma[nomagic]
+private predicate flowCandFwdIsEntered(
+ DataFlowCall call, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ParameterNode p |
+ flowCandFwdIn(call, p, fromArg, argApf, apf, config) and
+ nodeCand2(p, true, TBooleanSome(_), unbindBool(apf.toBoolNonEmpty()), config)
)
}
/**
- * Holds if data can flow from a source to `node` with the given `apf` and
- * from there flow to a sink.
+ * Holds if `node` with access path front `apf` is part of a path from a
+ * source to a sink in the configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnApf`
+ * records the front of the access path of the returned value.
*/
pragma[nomagic]
-private predicate flowCand(NodeExt node, boolean toReturn, AccessPathFront apf, Configuration config) {
- flowCand0(node, toReturn, apf, config) and
- flowCandFwd(node, _, apf, config)
+private predicate flowCand(
+ Node node, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ flowCand0(node, toReturn, returnApf, apf, config) and
+ flowCandFwd(node, _, _, apf, config)
}
pragma[nomagic]
private predicate flowCand0(
- NodeExt node, boolean toReturn, AccessPathFront apf, Configuration config
+ Node node, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
) {
- flowCandFwd(node, _, apf, config) and
- config.isSink(node.getNode()) and
+ flowCandFwd(node, _, _, apf, config) and
+ config.isSink(node) and
toReturn = false and
+ returnApf = TAccessPathFrontNone() and
apf instanceof AccessPathFrontNil
or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flowCand(mid, toReturn, apf, config)
+ exists(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flowCand(mid, toReturn, returnApf, apf, config)
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- localFlowBigStepExt(node, mid, false, _, config) and
- flowCand(mid, toReturn, nil, config) and
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(node, _, _, apf, config) and
+ localFlowBigStep(node, mid, false, _, config, _) and
+ flowCand(mid, toReturn, returnApf, nil, config) and
apf instanceof AccessPathFrontNil
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flowCand(mid, _, apf, config) and
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flowCand(mid, _, _, apf, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone()
+ )
+ or
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(node, _, _, apf, config) and
+ additionalJumpStep(node, mid, config) and
+ flowCand(mid, _, _, nil, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone() and
+ apf instanceof AccessPathFrontNil
+ )
+ or
+ // store
+ exists(TypedContent tc |
+ flowCandStore(node, tc, apf, toReturn, returnApf, config) and
+ flowCandConsCand(tc, apf, config)
+ )
+ or
+ // read
+ exists(TypedContent tc, AccessPathFront apf0 |
+ flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(tc, apf0, config)
+ )
+ or
+ // flow into a callable
+ exists(DataFlowCall call |
+ flowCandIn(call, node, toReturn, returnApf, apf, config) and
toReturn = false
+ or
+ exists(AccessPathFront returnApf0 |
+ flowCandInToReturn(call, node, returnApf0, apf, config) and
+ flowCandIsReturned(call, toReturn, returnApf, returnApf0, config)
+ )
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- additionalJumpStepExt(node, mid, config) and
- flowCand(mid, _, nil, config) and
- toReturn = false and
- apf instanceof AccessPathFrontNil
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flowCand(mid, false, apf, config) and
- toReturn = false and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flowCand(mid, _, apf, config) and
- toReturn = true and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- argumentValueFlowsThrough(node, mid) and
- flowCand(mid, toReturn, apf, config)
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil |
- argumentFlowsThrough(node, mid, _, config) and
- flowCand(mid, toReturn, nil, config) and
- apf instanceof AccessPathFrontNil and
- flowCandFwd(node, _, apf, config)
- )
- or
- exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, apf0, config) and
- apf0.headUsesContent(f) and
- consCand(f, apf, config)
- )
- or
- exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, config) and
- apf.headUsesContent(f)
- )
+ // flow out of a callable
+ flowCandOut(_, node, _, _, apf, config) and
+ toReturn = true and
+ if flowCandFwd(node, true, _, apf, config)
+ then returnApf = TAccessPathFrontSome(apf)
+ else returnApf = TAccessPathFrontNone()
+}
+
+pragma[nomagic]
+private predicate readCandFwd(
+ Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
+) {
+ flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
}
pragma[nomagic]
private predicate flowCandRead(
- NodeExt node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
) {
- exists(NodeExt mid |
- readExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ readCandFwd(node, tc, apf, mid, config) and
+ flowCand(mid, toReturn, returnApf, apf0, config)
)
}
pragma[nomagic]
private predicate flowCandStore(
- NodeExt node, Content f, boolean toReturn, AccessPathFrontHead apf0, Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, Configuration config
) {
- exists(NodeExt mid |
- storeExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ flowCandFwd(node, _, _, apf, config) and
+ storeCand2(node, tc, mid, _, unbind(config)) and
+ flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
)
}
pragma[nomagic]
-private predicate consCand(Content f, AccessPathFront apf, Configuration config) {
- consCandFwd(f, apf, config) and
- exists(NodeExt n, AccessPathFrontHead apf0 |
- flowCandFwd(n, _, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandRead(n, f, _, apf, config)
+private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(tc, apf, config) and
+ flowCandRead(_, tc, _, _, _, apf, config)
+}
+
+pragma[nomagic]
+private predicate flowCandOut(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathFrontOption returnApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(Node out, boolean allowsFieldFlow |
+ flowCand(out, toReturn, returnApf, apf, config) and
+ flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandIn(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathFrontOption returnApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ flowCand(p, toReturn, returnApf, apf, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandInToReturn(
+ DataFlowCall call, ArgumentNode arg, AccessPathFront returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ flowCandIn(call, arg, true, TAccessPathFrontSome(returnApf), apf, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `flowCand`.
+ */
+pragma[nomagic]
+private predicate flowCandIsReturned(
+ DataFlowCall call, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ flowCandOut(call, ret, toReturn, returnApf, apf, config) and
+ flowCandFwd(ret, true, TAccessPathFrontSome(_), apf, config)
)
}
private newtype TAccessPath =
TNil(DataFlowType t) or
- TConsNil(Content f, DataFlowType t) { consCand(f, TFrontNil(t), _) } or
- TConsCons(Content f1, Content f2, int len) {
- consCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
+ TConsCons(TypedContent tc1, TypedContent tc2, int len) {
+ flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
}
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first two
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
* elements of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -1684,7 +1530,7 @@ private newtype TAccessPath =
abstract private class AccessPath extends TAccessPath {
abstract string toString();
- abstract Content getHead();
+ abstract TypedContent getHead();
abstract int len();
@@ -1695,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
/**
* Holds if this access path has `head` at the front and may be followed by `tail`.
*/
- abstract predicate pop(Content head, AccessPath tail);
+ abstract predicate pop(TypedContent head, AccessPath tail);
}
private class AccessPathNil extends AccessPath, TNil {
@@ -1705,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
override string toString() { result = concat(": " + ppReprType(t)) }
- override Content getHead() { none() }
+ override TypedContent getHead() { none() }
override int len() { result = 0 }
@@ -1713,357 +1559,469 @@ private class AccessPathNil extends AccessPath, TNil {
override AccessPathFront getFront() { result = TFrontNil(t) }
- override predicate pop(Content head, AccessPath tail) { none() }
+ override predicate pop(TypedContent head, AccessPath tail) { none() }
}
abstract private class AccessPathCons extends AccessPath { }
private class AccessPathConsNil extends AccessPathCons, TConsNil {
- private Content f;
+ private TypedContent tc;
private DataFlowType t;
- AccessPathConsNil() { this = TConsNil(f, t) }
+ AccessPathConsNil() { this = TConsNil(tc, t) }
override string toString() {
// The `concat` becomes "" if `ppReprType` has no result.
- result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
+ result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
}
- override Content getHead() { result = f }
+ override TypedContent getHead() { result = tc }
override int len() { result = 1 }
- override DataFlowType getType() { result = f.getContainerType() }
+ override DataFlowType getType() { result = tc.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f) }
+ override AccessPathFront getFront() { result = TFrontHead(tc) }
- override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
+ override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
}
private class AccessPathConsCons extends AccessPathCons, TConsCons {
- private Content f1;
- private Content f2;
+ private TypedContent tc1;
+ private TypedContent tc2;
private int len;
- AccessPathConsCons() { this = TConsCons(f1, f2, len) }
+ AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
override string toString() {
if len = 2
- then result = "[" + f1.toString() + ", " + f2.toString() + "]"
- else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
+ else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
}
- override Content getHead() { result = f1 }
+ override TypedContent getHead() { result = tc1 }
override int len() { result = len }
- override DataFlowType getType() { result = f1.getContainerType() }
+ override DataFlowType getType() { result = tc1.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f1) }
+ override AccessPathFront getFront() { result = TFrontHead(tc1) }
- override predicate pop(Content head, AccessPath tail) {
- head = f1 and
+ override predicate pop(TypedContent head, AccessPath tail) {
+ head = tc1 and
(
- tail = TConsCons(f2, _, len - 1)
+ tail = TConsCons(tc2, _, len - 1)
or
len = 2 and
- tail = TConsNil(f2, _)
+ tail = TConsNil(tc2, _)
)
}
}
-/** Gets the access path obtained by popping `f` from `ap`, if any. */
-private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
+/** Gets the access path obtained by popping `tc` from `ap`, if any. */
+private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
-/** Gets the access path obtained by pushing `f` onto `ap`. */
-private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
+/** Gets the access path obtained by pushing `tc` onto `ap`. */
+private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
+
+private newtype TAccessPathOption =
+ TAccessPathNone() or
+ TAccessPathSome(AccessPath ap)
+
+private class AccessPathOption extends TAccessPathOption {
+ string toString() {
+ this = TAccessPathNone() and result = ""
+ or
+ this = TAccessPathSome(any(AccessPath ap | result = ap.toString()))
+ }
+}
/**
- * Holds if data can flow from a source to `node` with the given `ap`.
+ * Holds if `node` is reachable with access path `ap` from a source in
+ * the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argAp` records the access path of that
+ * argument.
*/
private predicate flowFwd(
- NodeExt node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config
+ Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
) {
- flowFwd0(node, fromArg, apf, ap, config) and
- flowCand(node, _, apf, config)
+ flowFwd0(node, fromArg, argAp, apf, ap, config) and
+ flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
- NodeExt node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config
+ Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
) {
- flowCand(node, _, _, config) and
- config.isSource(node.getNode()) and
+ flowCand(node, _, _, _, config) and
+ config.isSource(node) and
fromArg = false and
- ap = TNil(node.getErasedNodeTypeBound()) and
+ argAp = TAccessPathNone() and
+ ap = TNil(getErasedNodeTypeBound(node)) and
apf = ap.(AccessPathNil).getFront()
or
- flowCand(node, _, _, unbind(config)) and
+ flowCand(node, _, _, _, unbind(config)) and
(
- exists(NodeExt mid |
- flowFwd(mid, fromArg, apf, ap, config) and
- localFlowBigStepExt(mid, node, true, _, config)
+ exists(Node mid |
+ flowFwd(mid, fromArg, argAp, apf, ap, config) and
+ localFlowBigStep(mid, node, true, _, config, _)
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(mid, fromArg, _, nil, config) and
- localFlowBigStepExt(mid, node, false, apf, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(mid, fromArg, argAp, _, nil, config) and
+ localFlowBigStep(mid, node, false, apf, config, _) and
apf = ap.(AccessPathNil).getFront()
)
or
- exists(NodeExt mid |
- flowFwd(mid, _, apf, ap, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
- )
- or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(mid, _, _, nil, config) and
- additionalJumpStepExt(mid, node, config) and
+ exists(Node mid |
+ flowFwd(mid, _, _, apf, ap, config) and
+ jumpStep(mid, node, config) and
fromArg = false and
- ap = TNil(node.getErasedNodeTypeBound()) and
- apf = ap.(AccessPathNil).getFront()
+ argAp = TAccessPathNone()
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowFwd(mid, _, apf, ap, config) and
- flowIntoCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowFwd(mid, false, apf, ap, config) and
- flowOutOfCallableNodeCand2(mid, node, allowsFieldFlow, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(mid, _, _, _, nil, config) and
+ additionalJumpStep(mid, node, config) and
fromArg = false and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- flowFwd(mid, fromArg, apf, ap, config) and
- argumentValueFlowsThrough(mid, node)
- )
- or
- exists(NodeExt mid, AccessPathNil nil, DataFlowType t |
- flowFwd(mid, fromArg, _, nil, config) and
- argumentFlowsThrough(mid, node, t, config) and
- ap = TNil(t) and
+ argAp = TAccessPathNone() and
+ ap = TNil(getErasedNodeTypeBound(node)) and
apf = ap.(AccessPathNil).getFront()
)
)
or
- exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, config) and
- ap = push(f, ap0)
+ // store
+ exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
+ or
+ // read
+ exists(TypedContent tc |
+ flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
+ flowFwdConsCand(tc, apf, ap, config)
)
or
- exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, config) and
- flowConsCandFwd(f, apf, ap, config)
+ // flow into a callable
+ flowFwdIn(_, node, _, _, apf, ap, config) and
+ fromArg = true and
+ if flowCand(node, true, _, apf, config)
+ then argAp = TAccessPathSome(ap)
+ else argAp = TAccessPathNone()
+ or
+ // flow out of a callable
+ exists(DataFlowCall call |
+ flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
+ fromArg = false
+ or
+ exists(AccessPath argAp0 |
+ flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and
+ flowFwdIsEntered(call, fromArg, argAp, argAp0, config)
+ )
)
}
pragma[nomagic]
private predicate flowFwdStore(
- NodeExt node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
- Configuration config
+ Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFront apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- flowFwdStore1(mid, f, node, apf0, apf, config)
+ exists(Node mid, AccessPathFront apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
+ flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
pragma[nomagic]
-private predicate flowFwdStore0(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, Configuration config
+private predicate storeCand(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
+ Configuration config
) {
- storeExtCand2(mid, f, node, config) and
- flowCand(mid, _, apf0, config)
+ storeCand2(mid, tc, node, _, config) and
+ flowCand(mid, _, _, apf0, config) and
+ apf.headUsesContent(tc)
}
pragma[noinline]
-private predicate flowFwdStore1(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, AccessPathFrontHead apf,
+private predicate flowFwdStore0(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
- flowFwdStore0(mid, f, node, apf0, config) and
- consCand(f, apf0, config) and
- apf.headUsesContent(f) and
- flowCand(node, _, apf, unbind(config))
+ storeCand(mid, tc, node, apf0, apf, config) and
+ flowCandConsCand(tc, apf0, config) and
+ flowCand(node, _, _, apf, unbind(config))
+}
+
+pragma[nomagic]
+private predicate flowFwdRead0(
+ Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
+ boolean fromArg, AccessPathOption argAp, Configuration config
+) {
+ flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
+ readCandFwd(node1, tc, apf0, node2, config)
}
pragma[nomagic]
private predicate flowFwdRead(
- NodeExt node, Content f, AccessPath ap0, boolean fromArg, Configuration config
+ Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- readExtCand2(mid, f, node, config) and
- apf0.headUsesContent(f) and
- flowCand(node, _, _, unbind(config))
+ exists(Node mid, TypedContent tc |
+ flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
+ flowCand(node, _, _, apf, unbind(config)) and
+ flowCandConsCand(tc, apf, unbind(config))
)
}
pragma[nomagic]
-private predicate flowConsCandFwd(
- Content f, AccessPathFront apf, AccessPath ap, Configuration config
+private predicate flowFwdConsCand(
+ TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
) {
- exists(NodeExt n |
- flowFwd(n, _, apf, ap, config) and
- flowFwdStore1(n, f, _, apf, _, config)
+ exists(Node n |
+ flowFwd(n, _, _, apf, ap, config) and
+ flowFwdStore0(n, tc, _, apf, _, config)
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdIn(
+ DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ flowFwd(arg, fromArg, argAp, apf, ap, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
+ flowCand(p, _, _, _, unbind(config))
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ flowFwd(ret, fromArg, argAp, apf, ap, config) and
+ flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
+ flowCand(node, _, _, _, unbind(config))
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdOutFromArg(
+ DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
+) {
+ flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `flowFwd`.
+ */
+pragma[nomagic]
+private predicate flowFwdIsEntered(
+ DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
+) {
+ exists(ParameterNode p, AccessPathFront apf |
+ flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and
+ flowCand(p, true, TAccessPathFrontSome(_), apf, config)
)
}
/**
- * Holds if data can flow from a source to `node` with the given `ap` and
- * from there flow to a sink.
+ * Holds if `node` with access path `ap` is part of a path from a source to
+ * a sink in the configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnAp`
+ * records the access path of the returned value.
*/
-private predicate flow(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flow0(node, toReturn, ap, config) and
- flowFwd(node, _, _, ap, config)
+private predicate flow(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flow0(node, toReturn, returnAp, ap, config) and
+ flowFwd(node, _, _, _, ap, config)
}
-private predicate flow0(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flowFwd(node, _, _, ap, config) and
- config.isSink(node.getNode()) and
+private predicate flow0(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flowFwd(node, _, _, _, ap, config) and
+ config.isSink(node) and
toReturn = false and
+ returnAp = TAccessPathNone() and
ap instanceof AccessPathNil
or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flow(mid, toReturn, ap, config)
+ exists(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flow(mid, toReturn, returnAp, ap, config)
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(node, _, _, ap, config) and
- localFlowBigStepExt(node, mid, false, _, config) and
- flow(mid, toReturn, nil, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(node, _, _, _, ap, config) and
+ localFlowBigStep(node, mid, false, _, config, _) and
+ flow(mid, toReturn, returnAp, nil, config) and
ap instanceof AccessPathNil
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flow(mid, _, ap, config) and
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flow(mid, _, _, ap, config) and
+ toReturn = false and
+ returnAp = TAccessPathNone()
+ )
+ or
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(node, _, _, _, ap, config) and
+ additionalJumpStep(node, mid, config) and
+ flow(mid, _, _, nil, config) and
+ toReturn = false and
+ returnAp = TAccessPathNone() and
+ ap instanceof AccessPathNil
+ )
+ or
+ // store
+ exists(TypedContent tc |
+ flowStore(tc, node, toReturn, returnAp, ap, config) and
+ flowConsCand(tc, ap, config)
+ )
+ or
+ // read
+ exists(Node mid, AccessPath ap0 |
+ readFlowFwd(node, _, mid, ap, ap0, config) and
+ flow(mid, toReturn, returnAp, ap0, config)
+ )
+ or
+ // flow into a callable
+ exists(DataFlowCall call |
+ flowIn(call, node, toReturn, returnAp, ap, config) and
toReturn = false
+ or
+ exists(AccessPath returnAp0 |
+ flowInToReturn(call, node, returnAp0, ap, config) and
+ flowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(node, _, _, ap, config) and
- additionalJumpStepExt(node, mid, config) and
- flow(mid, _, nil, config) and
- toReturn = false and
- ap instanceof AccessPathNil
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flow(mid, false, ap, config) and
- toReturn = false and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flow(mid, _, ap, config) and
- toReturn = true and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- argumentValueFlowsThrough(node, mid) and
- flow(mid, toReturn, ap, config)
- )
- or
- exists(NodeExt mid, AccessPathNil nil |
- argumentFlowsThrough(node, mid, _, config) and
- flow(mid, toReturn, nil, config) and
- ap instanceof AccessPathNil and
- flowFwd(node, _, _, ap, config)
- )
- or
- exists(Content f |
- flowStore(f, node, toReturn, ap, config) and
- flowConsCand(f, ap, config)
- )
- or
- exists(NodeExt mid, AccessPath ap0 |
- readFwd(node, _, mid, ap, ap0, config) and
- flow(mid, toReturn, ap0, config)
- )
+ // flow out of a callable
+ flowOut(_, node, _, _, ap, config) and
+ toReturn = true and
+ if flowFwd(node, true, TAccessPathSome(_), _, ap, config)
+ then returnAp = TAccessPathSome(ap)
+ else returnAp = TAccessPathNone()
}
pragma[nomagic]
-private predicate storeFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate storeFlowFwd(
+ Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- storeExtCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, config) and
- ap0 = push(f, ap)
+ storeCand2(node1, tc, node2, _, config) and
+ flowFwdStore(node2, tc, ap, _, _, _, config) and
+ ap0 = push(tc, ap)
}
pragma[nomagic]
private predicate flowStore(
- Content f, NodeExt node, boolean toReturn, AccessPath ap, Configuration config
+ TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
) {
- exists(NodeExt mid, AccessPath ap0 |
- storeFwd(node, f, mid, ap, ap0, config) and
- flow(mid, toReturn, ap0, config)
+ exists(Node mid, AccessPath ap0 |
+ storeFlowFwd(node, tc, mid, ap, ap0, config) and
+ flow(mid, toReturn, returnAp, ap0, config)
)
}
pragma[nomagic]
-private predicate readFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate readFlowFwd(
+ Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- readExtCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, config) and
- ap0 = pop(f, ap) and
- flowConsCandFwd(f, _, ap0, unbind(config))
+ exists(AccessPathFrontHead apf |
+ readCandFwd(node1, tc, apf, node2, config) and
+ flowFwdRead(node2, apf, ap, _, _, _, config) and
+ ap0 = pop(tc, ap) and
+ flowFwdConsCand(tc, _, ap0, unbind(config))
+ )
}
pragma[nomagic]
-private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
- exists(NodeExt n, NodeExt mid |
- flow(mid, _, ap, config) and
- readFwd(n, f, mid, _, ap, config)
+private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
+ exists(Node n, Node mid |
+ flow(mid, _, _, ap, config) and
+ readFlowFwd(n, tc, mid, _, ap, config)
+ )
+}
+
+pragma[nomagic]
+private predicate flowOut(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(Node out, boolean allowsFieldFlow |
+ flow(out, toReturn, returnAp, ap, config) and
+ flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config)
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowIn(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ flow(p, toReturn, returnAp, ap, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowInToReturn(
+ DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config
+) {
+ flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `flow`.
+ */
+pragma[nomagic]
+private predicate flowIsReturned(
+ DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ flowOut(call, ret, toReturn, returnAp, ap, config) and
+ flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
)
}
bindingset[conf, result]
private Configuration unbind(Configuration conf) { result >= conf and result <= conf }
-private predicate flow(Node n, Configuration config) { flow(TNormalNode(n), _, _, config) }
+private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) }
+
+pragma[noinline]
+private predicate parameterFlow(
+ ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config
+) {
+ flow(p, true, _, ap, config) and
+ c = p.getEnclosingCallable()
+}
private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParameterNode p, AccessPath ap) {
- exists(ReturnNodeExt ret, Configuration config | flow(TNormalNode(p), true, ap, config) |
- exists(Summary summary |
- parameterFlowReturn(p, ret, _, _, _, summary, config) and
- flow(ret, unbind(config))
- |
- // taint through
- summary = TSummaryTaint() and
- ap instanceof AccessPathNil
- or
- // taint setter
- summary = TSummaryTaintStore(_) and
- ap instanceof AccessPathNil
- or
- // taint getter
- summary = TSummaryReadTaint(ap.(AccessPathConsNil).getHead())
- )
- or
- exists(ContentOption contentIn |
- parameterValueFlowReturn(p, ret, _, contentIn, _) and
- flow(ret, unbind(config))
- |
- // value through/setter
- contentIn = TContentNone()
- or
- // value getter (+ setter)
- contentIn = TContentSome(ap.getHead())
- )
+ exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 |
+ parameterFlow(p, ap, ret.getEnclosingCallable(), config) and
+ flow(ret, true, TAccessPathSome(_), ap0, config) and
+ flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config)
)
}
@@ -2113,7 +2071,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, cc, sc, ap) and
config = mid.getConfiguration() and
- flow(TNormalNode(node), _, ap, unbind(config))
+ flow(node, _, _, ap, unbind(config))
)
} or
TPathNodeSink(Node node, Configuration config) {
@@ -2164,14 +2122,31 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
+ private predicate isHidden() {
+ nodeIsHidden(this.getNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ }
+
+ private PathNode getASuccessorIfHidden() {
+ this.isHidden() and
+ result = this.(PathNodeImpl).getASuccessorImpl()
+ }
+
/** Gets a successor of this node, if any. */
- PathNode getASuccessor() { none() }
+ final PathNode getASuccessor() {
+ result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
+ not this.isHidden() and
+ not result.isHidden()
+ }
/** Holds if this node is a source. */
predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
+ abstract PathNode getASuccessorImpl();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2246,7 +2221,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
result.getConfiguration() = unbind(this.getConfiguration())
}
- override PathNodeImpl getASuccessor() {
+ override PathNodeImpl getASuccessorImpl() {
// an intermediate step to another intermediate node
result = getSuccMid()
or
@@ -2283,7 +2258,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNode getASuccessor() { none() }
+ override PathNode getASuccessorImpl() { none() }
override predicate isSource() { config.isSource(node) }
}
@@ -2293,17 +2268,18 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
* a callable is recorded by `cc`.
*/
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
- exists(
- AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
- LocalCallContext localCC
- |
- pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
- localCC = getLocalCallContext(cc, enclosing)
+ exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
+ midnode = mid.getNode() and
+ conf = mid.getConfiguration() and
+ cc = mid.getCallContext() and
+ sc = mid.getSummaryCtx() and
+ localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
ap = ap0
or
- localFlowBigStep(midnode, node, false, ap.(AccessPathNil).getType(), conf, localCC) and
+ localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -2318,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
mid.getAp() instanceof AccessPathNil and
ap = TNil(getErasedNodeTypeBound(node))
or
- exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
+ exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2332,44 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
}
pragma[nomagic]
-private predicate pathIntoLocalStep(
- PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
- AccessPath ap0, Configuration conf
-) {
- midnode = mid.getNode() and
- cc = mid.getCallContext() and
- conf = mid.getConfiguration() and
- localFlowBigStep(midnode, _, _, _, conf, _) and
- enclosing = midnode.getEnclosingCallable() and
- sc = mid.getSummaryCtx() and
- ap0 = mid.getAp()
-}
-
-pragma[nomagic]
-private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
- readDirect(node1, f, node2) and
+private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ readCandFwd(node1, tc, _, node2, config) and
flow(node2, config)
}
pragma[nomagic]
-private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
+private predicate pathReadStep(
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+) {
ap0 = mid.getAp() and
- readCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
-private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- storeDirect(node1, f, node2) and
+private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ storeCand2(node1, tc, node2, _, config) and
flow(node2, config)
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
@@ -2399,11 +2363,11 @@ private predicate pathOutOfCallable1(
}
pragma[noinline]
-private Node getAnOutNodeCand(
+private Node getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config
) {
result = kind.getAnOutNode(call) and
- flow(TNormalNode(result), _, ap, config)
+ flow(result, _, _, ap, config)
}
/**
@@ -2415,7 +2379,7 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config |
pathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = getAnOutNodeCand(kind, call, ap, config)
+ out = getAnOutNodeFlow(kind, call, ap, config)
)
}
@@ -2439,7 +2403,7 @@ private predicate parameterCand(
DataFlowCallable callable, int i, AccessPath ap, Configuration config
) {
exists(ParameterNode p |
- flow(TNormalNode(p), _, ap, config) and
+ flow(p, _, _, ap, config) and
p.isParameterOf(callable, i)
)
}
@@ -2514,7 +2478,7 @@ pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
exists(DataFlowCall call, ReturnKindExt kind |
pathThroughCallable0(call, mid, kind, cc, ap) and
- out = getAnOutNodeCand(kind, call, ap, mid.getConfiguration())
+ out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration())
)
}
@@ -2555,10 +2519,7 @@ private module FlowExploration {
viableParamArg(_, node2, node1)
or
// flow out of a callable
- exists(DataFlowCall call, ReturnKindExt kind |
- getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = kind.getAnOutNode(call)
- )
+ viableReturnPosOut(_, getReturnPosition(node1), node2)
|
c1 = node1.getEnclosingCallable() and
c2 = node2.getEnclosingCallable() and
@@ -2603,10 +2564,10 @@ private module FlowExploration {
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
- TPartialCons(Content f, int len) { len in [1 .. 5] }
+ TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
* element of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -2615,7 +2576,7 @@ private module FlowExploration {
private class PartialAccessPath extends TPartialAccessPath {
abstract string toString();
- Content getHead() { this = TPartialCons(result, _) }
+ TypedContent getHead() { this = TPartialCons(result, _) }
int len() {
this = TPartialNil(_) and result = 0
@@ -2626,7 +2587,7 @@ private module FlowExploration {
DataFlowType getType() {
this = TPartialNil(result)
or
- exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
+ exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
@@ -2644,15 +2605,15 @@ private module FlowExploration {
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
override string toString() {
- exists(Content f, int len | this = TPartialCons(f, len) |
+ exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
if len = 1
- then result = "[" + f.toString() + "]"
- else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc.toString() + "]"
+ else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
override AccessPathFront getFront() {
- exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
+ exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
}
}
@@ -2828,11 +2789,12 @@ private module FlowExploration {
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
- exists(PartialAccessPath ap0, Content f |
- partialPathReadStep(mid, ap0, f, node, cc, config) and
+ exists(PartialAccessPath ap0, TypedContent tc |
+ partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, f, ap0, config)
+ apConsFwd(ap, tc, ap0, config) and
+ compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -2851,35 +2813,42 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
+ PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
+ PartialAccessPath ap2
) {
- ap1 = mid.getAp() and
- storeDirect(mid.getNode(), f, node) and
- ap2.getHead() = f and
- ap2.len() = unbindInt(ap1.len() + 1) and
- compatibleTypes(ap1.getType(), f.getType())
+ exists(Node midNode, DataFlowType contentType |
+ midNode = mid.getNode() and
+ ap1 = mid.getAp() and
+ store(midNode, tc, node, contentType) and
+ ap2.getHead() = tc and
+ ap2.len() = unbindInt(ap1.len() + 1) and
+ compatibleTypes(ap1.getType(), contentType)
+ )
}
pragma[nomagic]
private predicate apConsFwd(
- PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
+ PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
- partialPathStoreStep(mid, ap1, f, _, ap2) and
+ partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
+ PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
- ap = mid.getAp() and
- readStep(mid.getNode(), f, node) and
- ap.getHead() = f and
- config = mid.getConfiguration() and
- cc = mid.getCallContext()
+ exists(Node midNode |
+ midNode = mid.getNode() and
+ ap = mid.getAp() and
+ read(midNode, tc.getContent(), node) and
+ ap.getHead() = tc and
+ config = mid.getConfiguration() and
+ cc = mid.getCallContext()
+ )
}
private predicate partialPathOutOfCallable0(
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
index a1daeb66411..1aeedf717f7 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
*/
predicate isBarrier(Node node) { none() }
- /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */
- deprecated predicate isBarrierEdge(Node node1, Node node2) { none() }
-
/** Holds if data flow into `node` is prohibited. */
predicate isBarrierIn(Node node) { none() }
@@ -251,15 +248,11 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
*/
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
-pragma[noinline]
-private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
- viableCallable(call) = result.getCallable() and
- kind = result.getKind()
-}
-
/**
- * Holds if `node` is reachable from a source in the given configuration
- * taking simple call contexts into consideration.
+ * Holds if `node` is reachable from a source in the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call.
*/
private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config) {
not fullBarrier(node, config) and
@@ -293,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
exists(Node mid |
useFieldFlow(config) and
nodeCandFwd1(mid, fromArg, config) and
- storeDirect(mid, _, node) and
+ store(mid, _, node, _) and
not outBarrier(mid, config)
)
or
// read
- exists(Content f |
- nodeCandFwd1Read(f, node, fromArg, config) and
- storeCandFwd1(f, config) and
+ exists(Content c |
+ nodeCandFwd1Read(c, node, fromArg, config) and
+ nodeCandFwd1IsStored(c, config) and
not inBarrier(node, config)
)
or
@@ -317,13 +310,35 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
fromArg = false
or
nodeCandFwd1OutFromArg(call, node, config) and
- flowOutCandFwd1(call, fromArg, config)
+ nodeCandFwd1IsEntered(call, fromArg, config)
)
)
}
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
+pragma[nomagic]
+private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
+ exists(Node mid |
+ nodeCandFwd1(mid, fromArg, config) and
+ read(mid, c, node)
+ )
+}
+
+/**
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
+ exists(Node mid, Node node, TypedContent tc |
+ not fullBarrier(node, config) and
+ useFieldFlow(config) and
+ nodeCandFwd1(mid, config) and
+ store(mid, tc, node, _) and
+ c = tc.getContent()
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1ReturnPosition(
ReturnPosition pos, boolean fromArg, Configuration config
@@ -335,43 +350,10 @@ private predicate nodeCandFwd1ReturnPosition(
}
pragma[nomagic]
-private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
- exists(Node mid |
- nodeCandFwd1(mid, fromArg, config) and
- readDirect(mid, f, node)
- )
-}
-
-/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
- */
-pragma[nomagic]
-private predicate storeCandFwd1(Content f, Configuration config) {
- exists(Node mid, Node node |
- not fullBarrier(node, config) and
- useFieldFlow(config) and
- nodeCandFwd1(mid, config) and
- storeDirect(mid, f, node)
- )
-}
-
-pragma[nomagic]
-private predicate nodeCandFwd1ReturnKind(
- DataFlowCall call, ReturnKindExt kind, boolean fromArg, Configuration config
-) {
+private predicate nodeCandFwd1Out(DataFlowCall call, Node out, boolean fromArg, Configuration config) {
exists(ReturnPosition pos |
nodeCandFwd1ReturnPosition(pos, fromArg, config) and
- pos = viableReturnPos(call, kind)
- )
-}
-
-pragma[nomagic]
-private predicate nodeCandFwd1Out(
- DataFlowCall call, Node node, boolean fromArg, Configuration config
-) {
- exists(ReturnKindExt kind |
- nodeCandFwd1ReturnKind(call, kind, fromArg, config) and
- node = kind.getAnOutNode(call)
+ viableReturnPosOut(call, pos, out)
)
}
@@ -384,7 +366,7 @@ private predicate nodeCandFwd1OutFromArg(DataFlowCall call, Node node, Configura
* Holds if an argument to `call` is reached in the flow covered by `nodeCandFwd1`.
*/
pragma[nomagic]
-private predicate flowOutCandFwd1(DataFlowCall call, boolean fromArg, Configuration config) {
+private predicate nodeCandFwd1IsEntered(DataFlowCall call, boolean fromArg, Configuration config) {
exists(ArgumentNode arg |
nodeCandFwd1(arg, fromArg, config) and
viableParamArg(call, _, arg)
@@ -395,8 +377,11 @@ bindingset[result, b]
private boolean unbindBool(boolean b) { result != b.booleanNot() }
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is part of a path from a source to a sink in the
+ * configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink.
*/
pragma[nomagic]
private predicate nodeCand1(Node node, boolean toReturn, Configuration config) {
@@ -433,81 +418,86 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
)
or
// store
- exists(Content f |
- nodeCand1Store(f, node, toReturn, config) and
- readCand1(f, config)
+ exists(Content c |
+ nodeCand1Store(c, node, toReturn, config) and
+ nodeCand1IsRead(c, config)
)
or
// read
- exists(Node mid, Content f |
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ exists(Node mid, Content c |
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, toReturn, config)
)
or
// flow into a callable
exists(DataFlowCall call |
- nodeCand1Arg(call, node, false, config) and
+ nodeCand1In(call, node, false, config) and
toReturn = false
or
- nodeCand1ArgToReturn(call, node, config) and
- flowInCand1(call, toReturn, config)
+ nodeCand1InToReturn(call, node, config) and
+ nodeCand1IsReturned(call, toReturn, config)
)
or
// flow out of a callable
exists(ReturnPosition pos |
- nodeCand1ReturnPosition(pos, config) and
+ nodeCand1Out(pos, config) and
getReturnPosition(node) = pos and
toReturn = true
)
}
-pragma[nomagic]
-private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _, config) }
-
-pragma[nomagic]
-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`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate readCand1(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content c, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd1(node, unbind(config)) and
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, _, config)
)
}
pragma[nomagic]
-private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
- exists(Node mid |
+private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
+ exists(Node mid, TypedContent tc |
nodeCand1(mid, toReturn, config) and
- storeCandFwd1(f, unbind(config)) and
- storeDirect(node, f, mid)
+ nodeCandFwd1IsStored(c, unbind(config)) and
+ store(node, tc, mid, _) and
+ c = tc.getContent()
)
}
/**
- * Holds if `f` is the target of both a read and a store in the flow covered
+ * Holds if `c` is the target of both a read and a store in the flow covered
* by `nodeCand1`.
*/
-private predicate readStoreCand1(Content f, Configuration conf) {
- readCand1(f, conf) and
- nodeCand1Store(f, _, _, conf)
+private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
+ nodeCand1IsRead(c, conf) and
+ nodeCand1Store(c, _, _, conf)
}
pragma[nomagic]
-private predicate viableParamArgCandFwd1(
+private predicate viableReturnPosOutNodeCandFwd1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+) {
+ nodeCandFwd1ReturnPosition(pos, _, config) and
+ viableReturnPosOut(call, pos, out)
+}
+
+pragma[nomagic]
+private predicate nodeCand1Out(ReturnPosition pos, Configuration config) {
+ exists(DataFlowCall call, Node out |
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, config)
+ )
+}
+
+pragma[nomagic]
+private predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
) {
viableParamArg(call, p, arg) and
@@ -515,32 +505,35 @@ private predicate viableParamArgCandFwd1(
}
pragma[nomagic]
-private predicate nodeCand1Arg(
+private predicate nodeCand1In(
DataFlowCall call, ArgumentNode arg, boolean toReturn, Configuration config
) {
exists(ParameterNode p |
nodeCand1(p, toReturn, config) and
- viableParamArgCandFwd1(call, p, arg, config)
+ viableParamArgNodeCandFwd1(call, p, arg, config)
)
}
pragma[nomagic]
-private predicate nodeCand1ArgToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
- nodeCand1Arg(call, arg, true, config)
+private predicate nodeCand1InToReturn(DataFlowCall call, ArgumentNode arg, Configuration config) {
+ nodeCand1In(call, arg, true, config)
}
/**
* Holds if an output from `call` is reached in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate flowInCand1(DataFlowCall call, boolean toReturn, Configuration config) {
+private predicate nodeCand1IsReturned(DataFlowCall call, boolean toReturn, Configuration config) {
exists(Node out |
nodeCand1(out, toReturn, config) and
nodeCandFwd1OutFromArg(call, out, config)
)
}
-private predicate throughFlowNodeCand(Node node, Configuration config) {
+pragma[nomagic]
+private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _, config) }
+
+private predicate throughFlowNodeCand1(Node node, Configuration config) {
nodeCand1(node, true, config) and
not fullBarrier(node, config) and
not inBarrier(node, config) and
@@ -549,11 +542,11 @@ private predicate throughFlowNodeCand(Node node, Configuration config) {
/** Holds if flow may return from `callable`. */
pragma[nomagic]
-private predicate returnFlowCallableCand(
+private predicate returnFlowCallableNodeCand1(
DataFlowCallable callable, ReturnKindExt kind, Configuration config
) {
exists(ReturnNodeExt ret |
- throughFlowNodeCand(ret, config) and
+ throughFlowNodeCand1(ret, config) and
callable = ret.getEnclosingCallable() and
kind = ret.getKind()
)
@@ -563,10 +556,10 @@ private predicate returnFlowCallableCand(
* Holds if flow may enter through `p` and reach a return node making `p` a
* candidate for the origin of a summary.
*/
-private predicate parameterThroughFlowCand(ParameterNode p, Configuration config) {
+private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration config) {
exists(ReturnKindExt kind |
- throughFlowNodeCand(p, config) and
- returnFlowCallableCand(p.getEnclosingCallable(), kind, config) and
+ throughFlowNodeCand1(p, config) and
+ returnFlowCallableNodeCand1(p.getEnclosingCallable(), kind, config) and
// we don't expect a parameter to return stored in itself
not exists(int pos |
kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos)
@@ -575,420 +568,77 @@ private predicate parameterThroughFlowCand(ParameterNode p, Configuration config
}
pragma[nomagic]
-private predicate store(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
- nodeCand1(n2, unbind(config)) and
- (
- storeDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentSome(f), n2)
- )
-}
-
-pragma[nomagic]
-private predicate read(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
- nodeCand1(n2, unbind(config)) and
- (
- readDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentSome(f), TContentNone(), n2)
- )
-}
-
-/**
- * Holds if `p` can flow to `node` in the same callable with `summary`
- * representing the flow path. The type of the tracked object is `t2`, and if
- * the summary includes a store step, `t1` is the tracked type just prior to the
- * store, that is, the type of the stored object, otherwise `t1` is equal to `t2`.
- */
-pragma[nomagic]
-private predicate parameterFlow(
- ParameterNode p, Node node, DataFlowType t1, DataFlowType t2, Summary summary,
- Configuration config
-) {
- parameterThroughFlowCand(p, config) and
- p = node and
- t1 = getErasedNodeTypeBound(node) and
- t1 = t2 and
- summary = TSummaryVal()
- or
- throughFlowNodeCand(node, unbind(config)) and
- (
- exists(Node mid |
- parameterFlow(p, mid, t1, t2, summary, config) and
- localFlowStep(mid, node, config) and
- compatibleTypes(t2, getErasedNodeTypeBound(node))
- )
- or
- exists(Node mid, Summary midsum |
- parameterFlow(p, mid, _, _, midsum, config) and
- additionalLocalFlowStep(mid, node, config) and
- t1 = getErasedNodeTypeBound(node) and
- t1 = t2 and
- summary = midsum.additionalStep()
- )
- or
- // read step
- exists(Node mid, Content f, Summary midsum |
- parameterFlow(p, mid, _, _, midsum, config) and
- read(mid, f, node, config) and
- summary = midsum.readStep(f) and
- t1 = f.getType() and
- t1 = t2
- )
- or
- // store step
- exists(Node mid, Content f, Summary midsum |
- parameterFlow(p, mid, t1, /* t1 */ _, midsum, config) and
- store(mid, f, node, config) and
- summary = midsum.storeStep(f) and
- compatibleTypes(t1, f.getType()) and
- t2 = f.getContainerType()
- )
- or
- // value flow through a callable
- exists(Node arg |
- parameterFlow(p, arg, t1, t2, summary, config) and
- argumentValueFlowsThrough(_, arg, TContentNone(), TContentNone(), node) and
- compatibleTypes(t2, getErasedNodeTypeBound(node))
- )
- or
- // flow through a callable
- exists(Node arg, Summary s1, Summary s2 |
- parameterFlow(p, arg, _, _, s1, config) and
- argumentFlowsThrough(arg, node, t1, t2, s2, config) and
- summary = s1.compose(s2)
- )
- )
-}
-
-private predicate viableParamArgCand(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
-) {
- viableParamArg(call, p, arg) and
- nodeCand1(arg, unbind(config)) and
- nodeCand1(p, config) and
- not outBarrier(arg, config) and
- not inBarrier(p, config)
-}
-
-pragma[nomagic]
-private predicate parameterFlowReturn(
- ParameterNode p, ReturnNodeExt ret, ReturnKindExt kind, DataFlowType t1, DataFlowType t2,
- Summary summary, Configuration config
-) {
- parameterFlow(p, ret, t1, t2, summary, config) and
- kind = ret.getKind() and
- not summary.isPartial() and
- not exists(int pos | kind.(ParamUpdateReturnKind).getPosition() = pos and p.isParameterOf(_, pos))
-}
-
-pragma[nomagic]
-private predicate argumentFlowsThrough0(
- DataFlowCall call, ArgumentNode arg, ReturnKindExt kind, DataFlowType t1, DataFlowType t2,
- Summary summary, Configuration config
-) {
- exists(ParameterNode p |
- viableParamArgCand(call, p, arg, config) and
- parameterFlowReturn(p, _, kind, t1, t2, summary, config)
- )
-}
-
-/**
- * Holds if data can flow from `arg` to `out` through a call with `summary`
- * representing the flow path. The type of the tracked object is `t2`, and if
- * the summary includes a store step, `t1` is the tracked type just prior to the
- * store, that is, the type of the stored object, otherwise `t1` is equal to `t2`.
- */
-private predicate argumentFlowsThrough(
- ArgumentNode arg, Node out, DataFlowType t1, DataFlowType t2, Summary summary,
- Configuration config
-) {
- nodeCand1(out, unbind(config)) and
- not inBarrier(out, config) and
- compatibleTypes(t2, getErasedNodeTypeBound(out)) and
- exists(DataFlowCall call, ReturnKindExt kind |
- argumentFlowsThrough0(call, arg, kind, t1, t2, summary, config) and
- out = kind.getAnOutNode(call)
- )
-}
-
-pragma[noinline]
-private predicate readStoreNode(
- DataFlowCall call, ArgumentNode arg, Content f1, Configuration config
-) {
- exists(Content f2, Node out |
- argumentValueFlowsThrough(call, arg, TContentSome(f1), TContentSome(f2), out) and
- nodeCand1(out, config) and
- readStoreCand1(f2, unbind(config))
- )
-}
-
-private newtype TNodeExt =
- TNormalNode(Node node) { nodeCand1(node, _) } or
- TReadStoreNode(DataFlowCall call, ArgumentNode arg, Content f1, Configuration config) {
- nodeCand1(arg, config) and
- readStoreNode(call, arg, f1, config) and
- readStoreCand1(f1, unbind(config))
- } or
- TReadTaintNode(ArgumentNode arg, Content f, Configuration config) {
- argumentFlowsThrough(arg, _, _, _, TSummaryReadTaint(f), config)
- } or
- TTaintStoreNode(ArgumentNode arg, DataFlowType t, Configuration config) {
- argumentFlowsThrough(arg, _, t, _, TSummaryTaintStore(_), config)
- }
-
-/**
- * An extended data flow node. Either a normal node, or an intermediate node
- * used to split up a summarized flow steps.
- *
- * This is purely an internal implementation detail.
- */
-abstract private class NodeExt extends TNodeExt {
- /** Gets the underlying (normal) node, if any. */
- abstract Node getNode();
-
- abstract DataFlowType getErasedNodeTypeBound();
-
- abstract DataFlowCallable getEnclosingCallable();
-
- abstract predicate isCand1(Configuration config);
-
- abstract string toString();
-
- abstract predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- );
-}
-
-/** A `Node` at which a cast can occur such that the type should be checked. */
-abstract private class CastingNodeExt extends NodeExt { }
-
-private class NormalNodeExt extends NodeExt, TNormalNode {
- override Node getNode() { this = TNormalNode(result) }
-
- override DataFlowType getErasedNodeTypeBound() {
- result = getErasedRepr(this.getNode().getTypeBound())
- }
-
- override DataFlowCallable getEnclosingCallable() {
- result = this.getNode().getEnclosingCallable()
- }
-
- override predicate isCand1(Configuration config) { nodeCand1(this.getNode(), config) }
-
- override string toString() { result = this.getNode().toString() }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- this.getNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class NormalCastingNodeExt extends CastingNodeExt, NormalNodeExt {
- NormalCastingNodeExt() { this.getNode() instanceof CastingNode }
-}
-
-private class ReadStoreNodeExt extends CastingNodeExt, TReadStoreNode {
- private DataFlowCall call;
- private ArgumentNode arg;
- private Content f1;
- private Configuration config0;
-
- ReadStoreNodeExt() { this = TReadStoreNode(call, arg, f1, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = f1.getType() }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = "(inside) " + call.toString() + " [read " + f1 + "]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class ReadTaintNode extends NodeExt, TReadTaintNode {
- private ArgumentNode arg;
- private Content f;
- private Configuration config0;
-
- ReadTaintNode() { this = TReadTaintNode(arg, f, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = f.getType() }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = arg.toString() + " [read taint " + f + "]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private class TaintStoreNode extends NodeExt, TTaintStoreNode {
- private ArgumentNode arg;
- private DataFlowType t;
- private Configuration config0;
-
- TaintStoreNode() { this = TTaintStoreNode(arg, t, config0) }
-
- override Node getNode() { none() }
-
- override DataFlowType getErasedNodeTypeBound() { result = t }
-
- override DataFlowCallable getEnclosingCallable() { result = arg.getEnclosingCallable() }
-
- override predicate isCand1(Configuration config) { config = config0 }
-
- override string toString() { result = arg.toString() + " [taint store]" }
-
- override predicate hasLocationInfo(
- string filepath, int startline, int startcolumn, int endline, int endcolumn
- ) {
- arg.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
- }
-}
-
-private predicate additionalLocalFlowStepExt(
- NodeExt node1, NodeExt node2, DataFlowType t, Configuration config
-) {
- exists(ArgumentNode arg, Content f |
- node1 = TReadTaintNode(arg, f, config) and
- argumentFlowsThrough(arg, node2.getNode(), _, t, TSummaryReadTaint(f), config)
- )
- or
- node2 = TTaintStoreNode(node1.getNode(), t, config)
-}
-
-pragma[nomagic]
-private predicate readExt(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- read(node1.getNode(), f, node2.getNode(), config)
- or
- node2 = TReadStoreNode(_, node1.getNode(), f, config)
- or
- node2 = TReadTaintNode(node1.getNode(), f, config)
-}
-
-pragma[nomagic]
-private predicate storeExt(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- store(node1.getNode(), f, node2.getNode(), config)
- or
- exists(DataFlowCall call, ArgumentNode arg, Content f1, Node n2 |
- node1 = TReadStoreNode(call, arg, f1, config) and
- n2 = node2.getNode() and
- argumentValueFlowsThrough(call, arg, TContentSome(f1), TContentSome(f), n2) and
+private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
+ exists(TypedContent tc |
+ nodeCand1IsReadAndStored(c, config) and
nodeCand1(n2, unbind(config)) and
- readStoreCand1(f, unbind(config))
- )
- or
- exists(ArgumentNode arg, DataFlowType t |
- node1 = TTaintStoreNode(arg, t, config) and
- argumentFlowsThrough(arg, node2.getNode(), t, _, TSummaryTaintStore(f), config)
+ store(n1, tc, n2, _) and
+ c = tc.getContent()
)
}
-private predicate jumpStepExt(NodeExt node1, NodeExt node2, Configuration config) {
- jumpStep(node1.getNode(), node2.getNode(), config)
-}
-
-private predicate additionalJumpStepExt(NodeExt node1, NodeExt node2, Configuration config) {
- additionalJumpStep(node1.getNode(), node2.getNode(), config)
-}
-
-private predicate argumentValueFlowsThrough(NodeExt node1, NodeExt node2) {
- argumentValueFlowsThrough(_, node1.getNode(), TContentNone(), TContentNone(), node2.getNode())
-}
-
-private predicate argumentFlowsThrough(
- NodeExt arg, NodeExt out, DataFlowType t, Configuration config
-) {
- argumentFlowsThrough(arg.getNode(), out.getNode(), _, t, TSummaryTaint(), config)
-}
-
-/**
- * Holds if data can flow from `node1` to `node2` in one local step or a step
- * through a callable.
- */
-pragma[noinline]
-private predicate localFlowStepOrFlowThroughCallable(
- NodeExt node1, NodeExt node2, Configuration config
-) {
- exists(Node n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode()
- |
- nodeCand1(n1, config) and
- localFlowStep(n1, n2, config)
- or
- nodeCand1(n1, config) and
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentNone(), n2)
- )
-}
-
-/**
- * Holds if data can flow from `node1` to `node2` in one local step or a step
- * through a callable, in both cases using an additional flow step from the
- * configuration.
- */
-pragma[noinline]
-private predicate additionalLocalFlowStepOrFlowThroughCallable(
- NodeExt node1, NodeExt node2, Configuration config
-) {
- exists(Node n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode()
- |
- nodeCand1(n1, config) and
- additionalLocalFlowStep(n1, n2, config)
- or
- argumentFlowsThrough(n1, n2, _, _, TSummaryTaint(), config)
- )
- or
- additionalLocalFlowStepExt(node1, node2, _, config)
+pragma[nomagic]
+private predicate read(Node n1, Content c, Node n2, Configuration config) {
+ nodeCand1IsReadAndStored(c, config) and
+ nodeCand1(n2, unbind(config)) and
+ read(n1, c, n2)
}
pragma[noinline]
-private ReturnPosition getReturnPosition1(ReturnNodeExt node, Configuration config) {
- result = getReturnPosition(node) and
- nodeCand1(node, config)
+private predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ localFlowStep(node1, node2, config)
+}
+
+pragma[noinline]
+private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ additionalLocalFlowStep(node1, node2, config)
+}
+
+pragma[nomagic]
+private predicate viableReturnPosOutNodeCand1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
+) {
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, config)
}
/**
- * Holds if data can flow out of a callable from `node1` to `node2`, either
+ * Holds if data can flow out of `call` from `ret` to `out`, 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 flowOutOfCallableNodeCand1(ReturnNodeExt node1, Node node2, Configuration config) {
- nodeCand1(node2, config) and
- not outBarrier(node1, config) and
- not inBarrier(node2, config) and
- exists(DataFlowCall call, ReturnKindExt kind |
- getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
- node2 = kind.getAnOutNode(call)
- )
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand1(
+ DataFlowCall call, ReturnNodeExt ret, Node out, Configuration config
+) {
+ viableReturnPosOutNodeCand1(call, getReturnPosition(ret), out, config) and
+ nodeCand1(ret, config) and
+ not outBarrier(ret, config) and
+ not inBarrier(out, config)
+}
+
+pragma[nomagic]
+private predicate viableParamArgNodeCand1(
+ DataFlowCall call, ParameterNode p, ArgumentNode arg, Configuration config
+) {
+ viableParamArgNodeCandFwd1(call, p, arg, config) and
+ nodeCand1(arg, config)
}
/**
- * Holds if data can flow into a callable and that this step is part of a
+ * Holds if data can flow into `call` and that this step is part of a
* path from a source to a sink.
*/
-private predicate flowIntoCallableNodeCand1(
- ArgumentNode node1, ParameterNode node2, Configuration config
+pragma[nomagic]
+private predicate flowIntoCallNodeCand1(
+ DataFlowCall call, ArgumentNode arg, ParameterNode p, Configuration config
) {
- viableParamArgCand(_, node2, node1, config)
+ viableParamArgNodeCand1(call, p, arg, config) and
+ nodeCand1(p, config) and
+ not outBarrier(arg, config) and
+ not inBarrier(p, config)
}
/**
@@ -999,7 +649,7 @@ private predicate flowIntoCallableNodeCand1(
private int branch(Node n1, Configuration conf) {
result =
strictcount(Node n |
- flowOutOfCallableNodeCand1(n1, n, conf) or flowIntoCallableNodeCand1(n1, n, conf)
+ flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1011,269 +661,391 @@ private int branch(Node n1, Configuration conf) {
private int join(Node n2, Configuration conf) {
result =
strictcount(Node n |
- flowOutOfCallableNodeCand1(n, n2, conf) or flowIntoCallableNodeCand1(n, n2, conf)
+ flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
/**
- * Holds if data can flow out of a callable from `node1` to `node2`, either
+ * Holds if data can flow out of `call` from `ret` to `out`, 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. The
* `allowsFieldFlow` flag indicates whether the branching is within the limit
* specified by the configuration.
*/
-private predicate flowOutOfCallableNodeCand1(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand1(
+ DataFlowCall call, ReturnNodeExt ret, Node out, boolean allowsFieldFlow, Configuration config
) {
- exists(ReturnNodeExt n1, Node n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode() and
- flowOutOfCallableNodeCand1(n1, n2, config) and
- exists(int b, int j |
- b = branch(n1, config) and
- j = join(n2, config) and
- if b.minimum(j) <= config.fieldFlowBranchLimit()
- then allowsFieldFlow = true
- else allowsFieldFlow = false
- )
+ flowOutOfCallNodeCand1(call, ret, out, config) and
+ exists(int b, int j |
+ b = branch(ret, config) and
+ j = join(out, config) and
+ if b.minimum(j) <= config.fieldFlowBranchLimit()
+ then allowsFieldFlow = true
+ else allowsFieldFlow = false
)
}
/**
- * Holds if data can flow into a callable and that this step is part of a
+ * Holds if data can flow into `call` and that this step is part of a
* path from a source to a sink. The `allowsFieldFlow` flag indicates whether
* the branching is within the limit specified by the configuration.
*/
-private predicate flowIntoCallableNodeCand1(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+pragma[nomagic]
+private predicate flowIntoCallNodeCand1(
+ DataFlowCall call, ArgumentNode arg, ParameterNode p, boolean allowsFieldFlow,
+ Configuration config
) {
- exists(ArgumentNode n1, ParameterNode n2 |
- n1 = node1.getNode() and
- n2 = node2.getNode() and
- flowIntoCallableNodeCand1(n1, n2, config) and
- exists(int b, int j |
- b = branch(n1, config) and
- j = join(n2, config) and
- if b.minimum(j) <= config.fieldFlowBranchLimit()
- then allowsFieldFlow = true
- else allowsFieldFlow = false
- )
+ flowIntoCallNodeCand1(call, arg, p, config) and
+ exists(int b, int j |
+ b = branch(arg, config) and
+ j = join(p, config) and
+ if b.minimum(j) <= config.fieldFlowBranchLimit()
+ then allowsFieldFlow = true
+ else allowsFieldFlow = false
)
}
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is reachable from a source in the configuration `config`.
+ * The Boolean `stored` records whether the tracked value is stored into a
+ * field of `node`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argStored` records whether the tracked
+ * value was stored into a field of the argument.
*/
-private predicate nodeCandFwd2(NodeExt node, boolean fromArg, boolean stored, Configuration config) {
- nodeCand1(node.getNode(), config) and
- config.isSource(node.getNode()) and
+private predicate nodeCandFwd2(
+ Node node, boolean fromArg, BooleanOption argStored, boolean stored, Configuration config
+) {
+ nodeCand1(node, config) and
+ config.isSource(node) and
fromArg = false and
+ argStored = TBooleanNone() and
stored = false
or
- node.isCand1(unbind(config)) and
+ nodeCand1(node, unbind(config)) and
(
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, stored, config) and
- localFlowStepOrFlowThroughCallable(mid, node, config)
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, stored, config) and
+ localFlowStepNodeCand1(mid, node, config)
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, stored, config) and
- additionalLocalFlowStepOrFlowThroughCallable(mid, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, stored, config) and
+ additionalLocalFlowStepNodeCand1(mid, node, config) and
stored = false
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, _, stored, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
+ exists(Node mid |
+ nodeCandFwd2(mid, _, _, stored, config) and
+ jumpStep(mid, node, config) and
+ fromArg = false and
+ argStored = TBooleanNone()
)
or
- exists(NodeExt mid |
- nodeCandFwd2(mid, _, stored, config) and
- additionalJumpStepExt(mid, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, _, _, stored, config) and
+ additionalJumpStep(mid, node, config) and
fromArg = false and
+ argStored = TBooleanNone() and
stored = false
)
or
// store
- exists(NodeExt mid, Content f |
- nodeCandFwd2(mid, fromArg, _, config) and
- storeExt(mid, f, node, config) and
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, _, config) and
+ storeCand1(mid, _, node, config) and
stored = true
)
or
// read
- exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, config) and
- storeCandFwd2(f, stored, config)
+ exists(Content c |
+ nodeCandFwd2Read(c, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(c, stored, config)
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- nodeCandFwd2(mid, _, stored, config) and
- flowIntoCallableNodeCand1(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (stored = false or allowsFieldFlow = true)
- )
+ // flow into a callable
+ nodeCandFwd2In(_, node, _, _, stored, config) and
+ fromArg = true and
+ if parameterThroughFlowNodeCand1(node, config)
+ then argStored = TBooleanSome(stored)
+ else argStored = TBooleanNone()
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- nodeCandFwd2(mid, false, stored, config) and
- flowOutOfCallableNodeCand1(mid, node, allowsFieldFlow, config) and
- fromArg = false and
- (stored = false or allowsFieldFlow = true)
+ // flow out of a callable
+ exists(DataFlowCall call |
+ nodeCandFwd2Out(call, node, fromArg, argStored, stored, config) and
+ fromArg = false
+ or
+ exists(boolean argStored0 |
+ nodeCandFwd2OutFromArg(call, node, argStored0, stored, config) and
+ nodeCandFwd2IsEntered(call, fromArg, argStored, argStored0, config)
+ )
)
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
*/
pragma[noinline]
-private predicate storeCandFwd2(Content f, boolean stored, Configuration config) {
- exists(NodeExt mid, NodeExt node |
+private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
+ exists(Node mid, Node node |
useFieldFlow(config) and
- node.isCand1(unbind(config)) and
- nodeCandFwd2(mid, _, stored, config) and
- storeExt(mid, f, node, config)
+ nodeCand1(node, unbind(config)) and
+ nodeCandFwd2(mid, _, _, stored, config) and
+ storeCand1(mid, c, node, config)
)
}
pragma[nomagic]
-private predicate nodeCandFwd2Read(Content f, NodeExt node, boolean fromArg, Configuration config) {
- exists(NodeExt mid |
- nodeCandFwd2(mid, fromArg, true, config) and
- readExt(mid, f, node, config)
+private predicate nodeCandFwd2Read(
+ Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+) {
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, true, config) and
+ read(mid, c, node, config)
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2In(
+ DataFlowCall call, ParameterNode p, boolean fromArg, BooleanOption argStored, boolean stored,
+ Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ nodeCandFwd2(arg, fromArg, argStored, stored, config) and
+ flowIntoCallNodeCand1(call, arg, p, allowsFieldFlow, config)
+ |
+ stored = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2Out(
+ DataFlowCall call, Node out, boolean fromArg, BooleanOption argStored, boolean stored,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ nodeCandFwd2(ret, fromArg, argStored, stored, config) and
+ flowOutOfCallNodeCand1(call, ret, out, allowsFieldFlow, config)
+ |
+ stored = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCandFwd2OutFromArg(
+ DataFlowCall call, Node out, boolean argStored, boolean stored, Configuration config
+) {
+ nodeCandFwd2Out(call, out, true, TBooleanSome(argStored), stored, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `nodeCandFwd2`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd2IsEntered(
+ DataFlowCall call, boolean fromArg, BooleanOption argStored, boolean stored, Configuration config
+) {
+ exists(ParameterNode p |
+ nodeCandFwd2In(call, p, fromArg, argStored, stored, config) and
+ parameterThroughFlowNodeCand1(p, config)
)
}
/**
- * Holds if `node` is part of a path from a source to a sink in the given
- * configuration taking simple call contexts into consideration.
+ * Holds if `node` is part of a path from a source to a sink in the
+ * configuration `config`. The Boolean `read` records whether the tracked
+ * value must be read from a field of `node` in order to reach a sink.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnRead`
+ * records whether a field must be read from the returned value.
*/
-private predicate nodeCand2(NodeExt node, boolean toReturn, boolean read, Configuration config) {
- nodeCandFwd2(node, _, false, config) and
- config.isSink(node.getNode()) and
+private predicate nodeCand2(
+ Node node, boolean toReturn, BooleanOption returnRead, boolean read, Configuration config
+) {
+ nodeCandFwd2(node, _, _, false, config) and
+ config.isSink(node) and
toReturn = false and
+ returnRead = TBooleanNone() and
read = false
or
- nodeCandFwd2(node, _, unbindBool(read), unbind(config)) and
+ nodeCandFwd2(node, _, _, unbindBool(read), unbind(config)) and
(
- exists(NodeExt mid |
- localFlowStepOrFlowThroughCallable(node, mid, config) and
- nodeCand2(mid, toReturn, read, config)
+ exists(Node mid |
+ localFlowStepNodeCand1(node, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, read, config)
)
or
- exists(NodeExt mid |
- additionalLocalFlowStepOrFlowThroughCallable(node, mid, config) and
- nodeCand2(mid, toReturn, read, config) and
+ exists(Node mid |
+ additionalLocalFlowStepNodeCand1(node, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, read, config) and
read = false
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- nodeCand2(mid, _, read, config) and
- toReturn = false
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ nodeCand2(mid, _, _, read, config) and
+ toReturn = false and
+ returnRead = TBooleanNone()
)
or
- exists(NodeExt mid |
- additionalJumpStepExt(node, mid, config) and
- nodeCand2(mid, _, read, config) and
+ exists(Node mid |
+ additionalJumpStep(node, mid, config) and
+ nodeCand2(mid, _, _, read, config) and
toReturn = false and
+ returnRead = TBooleanNone() and
read = false
)
or
// store
- exists(Content f |
- nodeCand2Store(f, node, toReturn, read, config) and
- readCand2(f, read, config)
+ exists(Content c |
+ nodeCand2Store(c, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(c, read, config)
)
or
// read
- exists(NodeExt mid, Content f, boolean read0 |
- readExt(node, f, mid, config) and
- storeCandFwd2(f, unbindBool(read0), unbind(config)) and
- nodeCand2(mid, toReturn, read0, config) and
+ exists(Node mid, Content c, boolean read0 |
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
+ nodeCand2(mid, toReturn, returnRead, read0, config) and
read = true
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand1(node, mid, allowsFieldFlow, config) and
- nodeCand2(mid, false, read, config) and
- toReturn = false and
- (read = false or allowsFieldFlow = true)
+ // flow into a callable
+ exists(DataFlowCall call |
+ nodeCand2In(call, node, toReturn, returnRead, read, config) and
+ toReturn = false
+ or
+ exists(boolean returnRead0 |
+ nodeCand2InToReturn(call, node, returnRead0, read, config) and
+ nodeCand2IsReturned(call, toReturn, returnRead, returnRead0, config)
+ )
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand1(node, mid, allowsFieldFlow, config) and
- nodeCand2(mid, _, read, config) and
- toReturn = true and
- (read = false or allowsFieldFlow = true)
- )
+ // flow out of a callable
+ nodeCand2Out(_, node, _, _, read, config) and
+ toReturn = true and
+ if nodeCandFwd2(node, true, TBooleanSome(_), unbindBool(read), config)
+ then returnRead = TBooleanSome(read)
+ else returnRead = TBooleanNone()
)
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate readCand2(Content f, boolean read, Configuration config) {
- exists(NodeExt mid, NodeExt node |
+private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
+ exists(Node mid, Node node |
useFieldFlow(config) and
- nodeCandFwd2(node, _, true, unbind(config)) and
- readExt(node, f, mid, config) and
- storeCandFwd2(f, unbindBool(read), unbind(config)) and
- nodeCand2(mid, _, read, config)
+ nodeCandFwd2(node, _, _, true, unbind(config)) and
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
+ nodeCand2(mid, _, _, read, config)
)
}
pragma[nomagic]
private predicate nodeCand2Store(
- Content f, NodeExt node, boolean toReturn, boolean stored, Configuration config
+ Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
+ Configuration config
) {
- exists(NodeExt mid |
- storeExt(node, f, mid, config) and
- nodeCand2(mid, toReturn, true, config) and
- nodeCandFwd2(node, _, stored, unbind(config))
- )
-}
-
-pragma[nomagic]
-private predicate storeCand2(Content f, boolean stored, Configuration conf) {
- exists(NodeExt node |
- nodeCand2Store(f, node, _, stored, conf) and
- nodeCand2(node, _, stored, conf)
+ exists(Node mid |
+ storeCand1(node, c, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, true, config) and
+ nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
/**
- * Holds if `f` is the target of both a store and a read in the path graph
- * covered by `nodeCand2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
*/
-pragma[noinline]
-private predicate readStoreCand(Content f, Configuration conf) {
- exists(boolean apNonEmpty |
- storeCand2(f, apNonEmpty, conf) and
- readCand2(f, apNonEmpty, conf)
+pragma[nomagic]
+private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
+ exists(Node node |
+ nodeCand2Store(c, node, _, _, stored, conf) and
+ nodeCand2(node, _, _, stored, conf)
)
}
-private predicate nodeCand2(NodeExt node, Configuration config) { nodeCand2(node, _, _, config) }
+/**
+ * Holds if `c` is the target of both a store and a read in the path graph
+ * covered by `nodeCand2`.
+ */
+pragma[noinline]
+private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
+ exists(boolean apNonEmpty |
+ nodeCand2IsStored(c, apNonEmpty, conf) and
+ nodeCand2IsRead(c, apNonEmpty, conf)
+ )
+}
pragma[nomagic]
-private predicate flowOutOfCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate nodeCand2Out(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
) {
- flowOutOfCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ exists(Node out, boolean allowsFieldFlow |
+ nodeCand2(out, toReturn, returnRead, read, config) and
+ flowOutOfCallNodeCand1(call, ret, out, allowsFieldFlow, config)
+ |
+ read = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCand2In(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ nodeCand2(p, toReturn, returnRead, read, config) and
+ flowIntoCallNodeCand1(call, arg, p, allowsFieldFlow, config)
+ |
+ read = false or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate nodeCand2InToReturn(
+ DataFlowCall call, ArgumentNode arg, boolean returnRead, boolean read, Configuration config
+) {
+ nodeCand2In(call, arg, true, TBooleanSome(returnRead), read, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `nodeCand2`.
+ */
+pragma[nomagic]
+private predicate nodeCand2IsReturned(
+ DataFlowCall call, boolean toReturn, BooleanOption returnRead, boolean read, Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ nodeCand2Out(call, ret, toReturn, returnRead, read, config) and
+ nodeCandFwd2(ret, true, TBooleanSome(_), read, config)
+ )
+}
+
+private predicate nodeCand2(Node node, Configuration config) { nodeCand2(node, _, _, _, config) }
+
+pragma[nomagic]
+private predicate flowOutOfCallNodeCand2(
+ DataFlowCall call, ReturnNodeExt node1, Node node2, boolean allowsFieldFlow, Configuration config
+) {
+ flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
nodeCand2(node2, config) and
nodeCand2(node1, unbind(config))
}
pragma[nomagic]
-private predicate flowIntoCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate flowIntoCallNodeCand2(
+ DataFlowCall call, ArgumentNode node1, ParameterNode node2, boolean allowsFieldFlow,
+ Configuration config
) {
- flowIntoCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
nodeCand2(node2, config) and
nodeCand2(node1, unbind(config))
}
@@ -1284,15 +1056,15 @@ private module LocalFlowBigStep {
* flow steps in a dataflow path.
*/
private predicate localFlowEntry(Node node, Configuration config) {
- nodeCand2(TNormalNode(node), config) and
+ nodeCand2(node, config) and
(
config.isSource(node) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
node instanceof ParameterNode or
- node instanceof OutNode or
- node instanceof PostUpdateNode or
- readDirect(_, _, node) or
+ node instanceof OutNodeExt or
+ store(_, _, node, _) or
+ read(_, _, node) or
node instanceof CastNode
)
}
@@ -1302,15 +1074,13 @@ private module LocalFlowBigStep {
* flow steps in a dataflow path.
*/
private predicate localFlowExit(Node node, Configuration config) {
- exists(Node next | nodeCand2(TNormalNode(next), config) |
+ exists(Node next | nodeCand2(next, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
- flowIntoCallableNodeCand1(node, next, config) or
- flowOutOfCallableNodeCand1(node, next, config) or
- argumentFlowsThrough(node, next, _, _, _, config) or
- argumentValueFlowsThrough(_, node, TContentNone(), TContentNone(), next) or
- storeDirect(node, _, next) or
- readDirect(node, _, next)
+ flowIntoCallNodeCand1(_, node, next, config) or
+ flowOutOfCallNodeCand1(_, node, next, config) or
+ store(node, _, next, _) or
+ read(node, _, next)
)
or
node instanceof CastNode
@@ -1318,6 +1088,13 @@ private module LocalFlowBigStep {
config.isSink(node)
}
+ pragma[noinline]
+ private predicate additionalLocalFlowStepNodeCand2(Node node1, Node node2, Configuration config) {
+ additionalLocalFlowStepNodeCand1(node1, node2, config) and
+ nodeCand2(node1, _, _, false, config) and
+ nodeCand2(node2, _, _, false, unbind(config))
+ }
+
/**
* Holds if the local path from `node1` to `node2` is a prefix of a maximal
* subsequence of local flow steps in a dataflow path.
@@ -1334,33 +1111,33 @@ private module LocalFlowBigStep {
(
localFlowEntry(node1, config) and
(
- localFlowStep(node1, node2, config) and
+ localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
t = getErasedNodeTypeBound(node1)
or
- additionalLocalFlowStep(node1, node2, config) and
+ additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
t = getErasedNodeTypeBound(node2)
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
or
exists(Node mid |
localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
- localFlowStep(mid, node2, config) and
+ localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof CastNode and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
)
or
exists(Node mid |
localFlowStepPlus(node1, mid, _, _, config, cc) and
- additionalLocalFlowStep(mid, node2, config) and
+ additionalLocalFlowStepNodeCand2(mid, node2, config) and
not mid instanceof CastNode and
preservesValue = false and
t = getErasedNodeTypeBound(node2) and
- nodeCand2(TNormalNode(node2), unbind(config))
+ nodeCand2(node2, unbind(config))
)
)
}
@@ -1371,311 +1148,380 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, DataFlowType t, Configuration config,
+ Node node1, Node node2, boolean preservesValue, AccessPathFrontNil apf, Configuration config,
LocalCallContext callContext
) {
- localFlowStepPlus(node1, node2, preservesValue, t, config, callContext) and
+ localFlowStepPlus(node1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, config)
}
-
- pragma[nomagic]
- predicate localFlowBigStepExt(
- NodeExt node1, NodeExt node2, boolean preservesValue, AccessPathFrontNil apf,
- Configuration config
- ) {
- localFlowBigStep(node1.getNode(), node2.getNode(), preservesValue, apf.getType(), config, _)
- or
- additionalLocalFlowStepExt(node1, node2, apf.getType(), config) and
- nodeCand2(node1, config) and
- nodeCand2(node2, unbind(config)) and
- preservesValue = false
- }
}
private import LocalFlowBigStep
pragma[nomagic]
-private predicate readExtCand2(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- readExt(node1, f, node2, config) and
- nodeCand2(node1, _, true, unbind(config)) and
+private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
+ read(node1, c, node2, config) and
+ nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- readStoreCand(f, unbind(config))
+ nodeCand2IsReadAndStored(c, unbind(config))
}
pragma[nomagic]
-private predicate storeExtCand2(NodeExt node1, Content f, NodeExt node2, Configuration config) {
- storeExt(node1, f, node2, config) and
+private predicate storeCand2(
+ Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+) {
+ store(node1, tc, node2, contentType) and
nodeCand2(node1, config) and
- nodeCand2(node2, _, true, unbind(config)) and
- readStoreCand(f, unbind(config))
-}
-
-private newtype TAccessPathFront =
- TFrontNil(DataFlowType t) or
- TFrontHead(Content f)
-
-/**
- * The front of an `AccessPath`. This is either a head or a nil.
- */
-abstract private class AccessPathFront extends TAccessPathFront {
- abstract string toString();
-
- abstract DataFlowType getType();
-
- abstract boolean toBoolNonEmpty();
-
- predicate headUsesContent(Content f) { this = TFrontHead(f) }
-}
-
-private class AccessPathFrontNil extends AccessPathFront, TFrontNil {
- override string toString() {
- exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
- }
-
- override DataFlowType getType() { this = TFrontNil(result) }
-
- override boolean toBoolNonEmpty() { result = false }
-}
-
-private class AccessPathFrontHead extends AccessPathFront, TFrontHead {
- override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) }
-
- override DataFlowType getType() {
- exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
- }
-
- override boolean toBoolNonEmpty() { result = true }
+ nodeCand2(node2, _, _, true, unbind(config)) and
+ nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
}
/**
- * Holds if data can flow from a source to `node` with the given `apf`.
+ * Holds if `node` is reachable with access path front `apf` from a
+ * source in the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argApf` records the front of the
+ * access path of that argument.
*/
pragma[nomagic]
private predicate flowCandFwd(
- NodeExt node, boolean fromArg, AccessPathFront apf, Configuration config
+ Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
) {
- flowCandFwd0(node, fromArg, apf, config) and
- if node instanceof CastingNodeExt
- then compatibleTypes(node.getErasedNodeTypeBound(), apf.getType())
+ flowCandFwd0(node, fromArg, argApf, apf, config) and
+ if node instanceof CastingNode
+ then compatibleTypes(getErasedNodeTypeBound(node), apf.getType())
else any()
}
pragma[nomagic]
private predicate flowCandFwd0(
- NodeExt node, boolean fromArg, AccessPathFront apf, Configuration config
+ Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
) {
- nodeCand2(node, _, false, config) and
- config.isSource(node.getNode()) and
+ nodeCand2(node, _, _, false, config) and
+ config.isSource(node) and
fromArg = false and
- apf = TFrontNil(node.getErasedNodeTypeBound())
+ argApf = TAccessPathFrontNone() and
+ apf = TFrontNil(getErasedNodeTypeBound(node))
or
- exists(NodeExt mid |
- flowCandFwd(mid, fromArg, apf, config) and
- localFlowBigStepExt(mid, node, true, _, config)
+ exists(Node mid |
+ flowCandFwd(mid, fromArg, argApf, apf, config) and
+ localFlowBigStep(mid, node, true, _, config, _)
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(mid, fromArg, nil, config) and
- localFlowBigStepExt(mid, node, false, apf, config)
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(mid, fromArg, argApf, nil, config) and
+ localFlowBigStep(mid, node, false, apf, config, _)
)
or
- nodeCand2(node, unbind(config)) and
- (
- exists(NodeExt mid |
- flowCandFwd(mid, _, apf, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(mid, _, nil, config) and
- additionalJumpStepExt(mid, node, config) and
- fromArg = false and
- apf = TFrontNil(node.getErasedNodeTypeBound())
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowCandFwd(mid, _, apf, config) and
- flowIntoCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowCandFwd(mid, false, apf, config) and
- flowOutOfCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = false and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- flowCandFwd(mid, fromArg, apf, config) and
- argumentValueFlowsThrough(mid, node)
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil, DataFlowType t |
- flowCandFwd(mid, fromArg, nil, config) and
- argumentFlowsThrough(mid, node, t, config) and
- apf = TFrontNil(t)
- )
+ exists(Node mid |
+ flowCandFwd(mid, _, _, apf, config) and
+ nodeCand2(node, unbind(config)) and
+ jumpStep(mid, node, config) and
+ fromArg = false and
+ argApf = TAccessPathFrontNone()
)
or
- exists(NodeExt mid, Content f |
- flowCandFwd(mid, fromArg, _, config) and
- storeExtCand2(mid, f, node, config) and
- nodeCand2(node, _, true, unbind(config)) and
- apf.headUsesContent(f)
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(mid, _, _, nil, config) and
+ nodeCand2(node, unbind(config)) and
+ additionalJumpStep(mid, node, config) and
+ fromArg = false and
+ argApf = TAccessPathFrontNone() and
+ apf = TFrontNil(getErasedNodeTypeBound(node))
)
or
- exists(Content f |
- flowCandFwdRead(f, node, fromArg, config) and
- consCandFwd(f, apf, config) and
- nodeCand2(node, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ // store
+ exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ storeCand2(mid, tc, node, contentType, config) and
+ nodeCand2(node, _, _, true, unbind(config)) and
+ apf.headUsesContent(tc) and
+ compatibleTypes(apf0.getType(), contentType)
+ )
+ or
+ // read
+ exists(TypedContent tc |
+ flowCandFwdRead(tc, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(tc, apf, config) and
+ nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ )
+ or
+ // flow into a callable
+ flowCandFwdIn(_, node, _, _, apf, config) and
+ fromArg = true and
+ if nodeCand2(node, true, _, unbindBool(apf.toBoolNonEmpty()), config)
+ then argApf = TAccessPathFrontSome(apf)
+ else argApf = TAccessPathFrontNone()
+ or
+ // flow out of a callable
+ exists(DataFlowCall call |
+ flowCandFwdOut(call, node, fromArg, argApf, apf, config) and
+ fromArg = false
+ or
+ exists(AccessPathFront argApf0 |
+ flowCandFwdOutFromArg(call, node, argApf0, apf, config) and
+ flowCandFwdIsEntered(call, fromArg, argApf, argApf0, config)
+ )
)
}
pragma[nomagic]
-private predicate consCandFwd(Content f, AccessPathFront apf, Configuration config) {
- exists(NodeExt mid, NodeExt n |
- flowCandFwd(mid, _, apf, config) and
- storeExtCand2(mid, f, n, config) and
- nodeCand2(n, _, true, unbind(config)) and
- compatibleTypes(apf.getType(), f.getType())
+private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n, DataFlowType contentType |
+ flowCandFwd(mid, _, _, apf, config) and
+ storeCand2(mid, tc, n, contentType, config) and
+ nodeCand2(n, _, _, true, unbind(config)) and
+ compatibleTypes(apf.getType(), contentType)
)
}
pragma[nomagic]
-private predicate flowCandFwdRead(Content f, NodeExt node, boolean fromArg, Configuration config) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowCandFwd(mid, fromArg, apf0, config) and
- readExtCand2(mid, f, node, config) and
- apf0.headUsesContent(f)
+private predicate flowCandFwdRead0(
+ Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFrontHead apf, Configuration config
+) {
+ flowCandFwd(node1, fromArg, argApf, apf, config) and
+ readCand2(node1, c, node2, config) and
+ apf.headUsesContent(tc)
+}
+
+pragma[nomagic]
+private predicate flowCandFwdRead(
+ TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+) {
+ flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
+}
+
+pragma[nomagic]
+private predicate flowCandFwdIn(
+ DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ flowCandFwd(arg, fromArg, argApf, apf, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ flowCandFwd(ret, fromArg, argApf, apf, config) and
+ flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandFwdOutFromArg(
+ DataFlowCall call, Node node, AccessPathFront argApf, AccessPathFront apf, Configuration config
+) {
+ flowCandFwdOut(call, node, true, TAccessPathFrontSome(argApf), apf, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `flowCandFwd`.
+ */
+pragma[nomagic]
+private predicate flowCandFwdIsEntered(
+ DataFlowCall call, boolean fromArg, AccessPathFrontOption argApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ParameterNode p |
+ flowCandFwdIn(call, p, fromArg, argApf, apf, config) and
+ nodeCand2(p, true, TBooleanSome(_), unbindBool(apf.toBoolNonEmpty()), config)
)
}
/**
- * Holds if data can flow from a source to `node` with the given `apf` and
- * from there flow to a sink.
+ * Holds if `node` with access path front `apf` is part of a path from a
+ * source to a sink in the configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnApf`
+ * records the front of the access path of the returned value.
*/
pragma[nomagic]
-private predicate flowCand(NodeExt node, boolean toReturn, AccessPathFront apf, Configuration config) {
- flowCand0(node, toReturn, apf, config) and
- flowCandFwd(node, _, apf, config)
+private predicate flowCand(
+ Node node, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ flowCand0(node, toReturn, returnApf, apf, config) and
+ flowCandFwd(node, _, _, apf, config)
}
pragma[nomagic]
private predicate flowCand0(
- NodeExt node, boolean toReturn, AccessPathFront apf, Configuration config
+ Node node, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
) {
- flowCandFwd(node, _, apf, config) and
- config.isSink(node.getNode()) and
+ flowCandFwd(node, _, _, apf, config) and
+ config.isSink(node) and
toReturn = false and
+ returnApf = TAccessPathFrontNone() and
apf instanceof AccessPathFrontNil
or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flowCand(mid, toReturn, apf, config)
+ exists(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flowCand(mid, toReturn, returnApf, apf, config)
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- localFlowBigStepExt(node, mid, false, _, config) and
- flowCand(mid, toReturn, nil, config) and
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(node, _, _, apf, config) and
+ localFlowBigStep(node, mid, false, _, config, _) and
+ flowCand(mid, toReturn, returnApf, nil, config) and
apf instanceof AccessPathFrontNil
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flowCand(mid, _, apf, config) and
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flowCand(mid, _, _, apf, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone()
+ )
+ or
+ exists(Node mid, AccessPathFrontNil nil |
+ flowCandFwd(node, _, _, apf, config) and
+ additionalJumpStep(node, mid, config) and
+ flowCand(mid, _, _, nil, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone() and
+ apf instanceof AccessPathFrontNil
+ )
+ or
+ // store
+ exists(TypedContent tc |
+ flowCandStore(node, tc, apf, toReturn, returnApf, config) and
+ flowCandConsCand(tc, apf, config)
+ )
+ or
+ // read
+ exists(TypedContent tc, AccessPathFront apf0 |
+ flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(tc, apf0, config)
+ )
+ or
+ // flow into a callable
+ exists(DataFlowCall call |
+ flowCandIn(call, node, toReturn, returnApf, apf, config) and
toReturn = false
+ or
+ exists(AccessPathFront returnApf0 |
+ flowCandInToReturn(call, node, returnApf0, apf, config) and
+ flowCandIsReturned(call, toReturn, returnApf, returnApf0, config)
+ )
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- additionalJumpStepExt(node, mid, config) and
- flowCand(mid, _, nil, config) and
- toReturn = false and
- apf instanceof AccessPathFrontNil
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flowCand(mid, false, apf, config) and
- toReturn = false and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flowCand(mid, _, apf, config) and
- toReturn = true and
- (apf instanceof AccessPathFrontNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- argumentValueFlowsThrough(node, mid) and
- flowCand(mid, toReturn, apf, config)
- )
- or
- exists(NodeExt mid, AccessPathFrontNil nil |
- argumentFlowsThrough(node, mid, _, config) and
- flowCand(mid, toReturn, nil, config) and
- apf instanceof AccessPathFrontNil and
- flowCandFwd(node, _, apf, config)
- )
- or
- exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, apf0, config) and
- apf0.headUsesContent(f) and
- consCand(f, apf, config)
- )
- or
- exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, config) and
- apf.headUsesContent(f)
- )
+ // flow out of a callable
+ flowCandOut(_, node, _, _, apf, config) and
+ toReturn = true and
+ if flowCandFwd(node, true, _, apf, config)
+ then returnApf = TAccessPathFrontSome(apf)
+ else returnApf = TAccessPathFrontNone()
+}
+
+pragma[nomagic]
+private predicate readCandFwd(
+ Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
+) {
+ flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
}
pragma[nomagic]
private predicate flowCandRead(
- NodeExt node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
) {
- exists(NodeExt mid |
- readExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ readCandFwd(node, tc, apf, mid, config) and
+ flowCand(mid, toReturn, returnApf, apf0, config)
)
}
pragma[nomagic]
private predicate flowCandStore(
- NodeExt node, Content f, boolean toReturn, AccessPathFrontHead apf0, Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, Configuration config
) {
- exists(NodeExt mid |
- storeExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ flowCandFwd(node, _, _, apf, config) and
+ storeCand2(node, tc, mid, _, unbind(config)) and
+ flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
)
}
pragma[nomagic]
-private predicate consCand(Content f, AccessPathFront apf, Configuration config) {
- consCandFwd(f, apf, config) and
- exists(NodeExt n, AccessPathFrontHead apf0 |
- flowCandFwd(n, _, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandRead(n, f, _, apf, config)
+private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(tc, apf, config) and
+ flowCandRead(_, tc, _, _, _, apf, config)
+}
+
+pragma[nomagic]
+private predicate flowCandOut(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathFrontOption returnApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(Node out, boolean allowsFieldFlow |
+ flowCand(out, toReturn, returnApf, apf, config) and
+ flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandIn(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathFrontOption returnApf,
+ AccessPathFront apf, Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ flowCand(p, toReturn, returnApf, apf, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ apf instanceof AccessPathFrontNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowCandInToReturn(
+ DataFlowCall call, ArgumentNode arg, AccessPathFront returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ flowCandIn(call, arg, true, TAccessPathFrontSome(returnApf), apf, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `flowCand`.
+ */
+pragma[nomagic]
+private predicate flowCandIsReturned(
+ DataFlowCall call, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ flowCandOut(call, ret, toReturn, returnApf, apf, config) and
+ flowCandFwd(ret, true, TAccessPathFrontSome(_), apf, config)
)
}
private newtype TAccessPath =
TNil(DataFlowType t) or
- TConsNil(Content f, DataFlowType t) { consCand(f, TFrontNil(t), _) } or
- TConsCons(Content f1, Content f2, int len) {
- consCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
+ TConsCons(TypedContent tc1, TypedContent tc2, int len) {
+ flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
}
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first two
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two
* elements of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -1684,7 +1530,7 @@ private newtype TAccessPath =
abstract private class AccessPath extends TAccessPath {
abstract string toString();
- abstract Content getHead();
+ abstract TypedContent getHead();
abstract int len();
@@ -1695,7 +1541,7 @@ abstract private class AccessPath extends TAccessPath {
/**
* Holds if this access path has `head` at the front and may be followed by `tail`.
*/
- abstract predicate pop(Content head, AccessPath tail);
+ abstract predicate pop(TypedContent head, AccessPath tail);
}
private class AccessPathNil extends AccessPath, TNil {
@@ -1705,7 +1551,7 @@ private class AccessPathNil extends AccessPath, TNil {
override string toString() { result = concat(": " + ppReprType(t)) }
- override Content getHead() { none() }
+ override TypedContent getHead() { none() }
override int len() { result = 0 }
@@ -1713,357 +1559,469 @@ private class AccessPathNil extends AccessPath, TNil {
override AccessPathFront getFront() { result = TFrontNil(t) }
- override predicate pop(Content head, AccessPath tail) { none() }
+ override predicate pop(TypedContent head, AccessPath tail) { none() }
}
abstract private class AccessPathCons extends AccessPath { }
private class AccessPathConsNil extends AccessPathCons, TConsNil {
- private Content f;
+ private TypedContent tc;
private DataFlowType t;
- AccessPathConsNil() { this = TConsNil(f, t) }
+ AccessPathConsNil() { this = TConsNil(tc, t) }
override string toString() {
// The `concat` becomes "" if `ppReprType` has no result.
- result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
+ result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
}
- override Content getHead() { result = f }
+ override TypedContent getHead() { result = tc }
override int len() { result = 1 }
- override DataFlowType getType() { result = f.getContainerType() }
+ override DataFlowType getType() { result = tc.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f) }
+ override AccessPathFront getFront() { result = TFrontHead(tc) }
- override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
+ override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) }
}
private class AccessPathConsCons extends AccessPathCons, TConsCons {
- private Content f1;
- private Content f2;
+ private TypedContent tc1;
+ private TypedContent tc2;
private int len;
- AccessPathConsCons() { this = TConsCons(f1, f2, len) }
+ AccessPathConsCons() { this = TConsCons(tc1, tc2, len) }
override string toString() {
if len = 2
- then result = "[" + f1.toString() + ", " + f2.toString() + "]"
- else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
+ else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
}
- override Content getHead() { result = f1 }
+ override TypedContent getHead() { result = tc1 }
override int len() { result = len }
- override DataFlowType getType() { result = f1.getContainerType() }
+ override DataFlowType getType() { result = tc1.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f1) }
+ override AccessPathFront getFront() { result = TFrontHead(tc1) }
- override predicate pop(Content head, AccessPath tail) {
- head = f1 and
+ override predicate pop(TypedContent head, AccessPath tail) {
+ head = tc1 and
(
- tail = TConsCons(f2, _, len - 1)
+ tail = TConsCons(tc2, _, len - 1)
or
len = 2 and
- tail = TConsNil(f2, _)
+ tail = TConsNil(tc2, _)
)
}
}
-/** Gets the access path obtained by popping `f` from `ap`, if any. */
-private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
+/** Gets the access path obtained by popping `tc` from `ap`, if any. */
+private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) }
-/** Gets the access path obtained by pushing `f` onto `ap`. */
-private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
+/** Gets the access path obtained by pushing `tc` onto `ap`. */
+private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) }
+
+private newtype TAccessPathOption =
+ TAccessPathNone() or
+ TAccessPathSome(AccessPath ap)
+
+private class AccessPathOption extends TAccessPathOption {
+ string toString() {
+ this = TAccessPathNone() and result = ""
+ or
+ this = TAccessPathSome(any(AccessPath ap | result = ap.toString()))
+ }
+}
/**
- * Holds if data can flow from a source to `node` with the given `ap`.
+ * Holds if `node` is reachable with access path `ap` from a source in
+ * the configuration `config`.
+ *
+ * The Boolean `fromArg` records whether the node is reached through an
+ * argument in a call, and if so, `argAp` records the access path of that
+ * argument.
*/
private predicate flowFwd(
- NodeExt node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config
+ Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
) {
- flowFwd0(node, fromArg, apf, ap, config) and
- flowCand(node, _, apf, config)
+ flowFwd0(node, fromArg, argAp, apf, ap, config) and
+ flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
- NodeExt node, boolean fromArg, AccessPathFront apf, AccessPath ap, Configuration config
+ Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
) {
- flowCand(node, _, _, config) and
- config.isSource(node.getNode()) and
+ flowCand(node, _, _, _, config) and
+ config.isSource(node) and
fromArg = false and
- ap = TNil(node.getErasedNodeTypeBound()) and
+ argAp = TAccessPathNone() and
+ ap = TNil(getErasedNodeTypeBound(node)) and
apf = ap.(AccessPathNil).getFront()
or
- flowCand(node, _, _, unbind(config)) and
+ flowCand(node, _, _, _, unbind(config)) and
(
- exists(NodeExt mid |
- flowFwd(mid, fromArg, apf, ap, config) and
- localFlowBigStepExt(mid, node, true, _, config)
+ exists(Node mid |
+ flowFwd(mid, fromArg, argAp, apf, ap, config) and
+ localFlowBigStep(mid, node, true, _, config, _)
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(mid, fromArg, _, nil, config) and
- localFlowBigStepExt(mid, node, false, apf, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(mid, fromArg, argAp, _, nil, config) and
+ localFlowBigStep(mid, node, false, apf, config, _) and
apf = ap.(AccessPathNil).getFront()
)
or
- exists(NodeExt mid |
- flowFwd(mid, _, apf, ap, config) and
- jumpStepExt(mid, node, config) and
- fromArg = false
- )
- or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(mid, _, _, nil, config) and
- additionalJumpStepExt(mid, node, config) and
+ exists(Node mid |
+ flowFwd(mid, _, _, apf, ap, config) and
+ jumpStep(mid, node, config) and
fromArg = false and
- ap = TNil(node.getErasedNodeTypeBound()) and
- apf = ap.(AccessPathNil).getFront()
+ argAp = TAccessPathNone()
)
or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowFwd(mid, _, apf, ap, config) and
- flowIntoCallableNodeCand2(mid, node, allowsFieldFlow, config) and
- fromArg = true and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowFwd(mid, false, apf, ap, config) and
- flowOutOfCallableNodeCand2(mid, node, allowsFieldFlow, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(mid, _, _, _, nil, config) and
+ additionalJumpStep(mid, node, config) and
fromArg = false and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- flowFwd(mid, fromArg, apf, ap, config) and
- argumentValueFlowsThrough(mid, node)
- )
- or
- exists(NodeExt mid, AccessPathNil nil, DataFlowType t |
- flowFwd(mid, fromArg, _, nil, config) and
- argumentFlowsThrough(mid, node, t, config) and
- ap = TNil(t) and
+ argAp = TAccessPathNone() and
+ ap = TNil(getErasedNodeTypeBound(node)) and
apf = ap.(AccessPathNil).getFront()
)
)
or
- exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, config) and
- ap = push(f, ap0)
+ // store
+ exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
+ or
+ // read
+ exists(TypedContent tc |
+ flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
+ flowFwdConsCand(tc, apf, ap, config)
)
or
- exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, config) and
- flowConsCandFwd(f, apf, ap, config)
+ // flow into a callable
+ flowFwdIn(_, node, _, _, apf, ap, config) and
+ fromArg = true and
+ if flowCand(node, true, _, apf, config)
+ then argAp = TAccessPathSome(ap)
+ else argAp = TAccessPathNone()
+ or
+ // flow out of a callable
+ exists(DataFlowCall call |
+ flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
+ fromArg = false
+ or
+ exists(AccessPath argAp0 |
+ flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and
+ flowFwdIsEntered(call, fromArg, argAp, argAp0, config)
+ )
)
}
pragma[nomagic]
private predicate flowFwdStore(
- NodeExt node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
- Configuration config
+ Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFront apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- flowFwdStore1(mid, f, node, apf0, apf, config)
+ exists(Node mid, AccessPathFront apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
+ flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
pragma[nomagic]
-private predicate flowFwdStore0(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, Configuration config
+private predicate storeCand(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
+ Configuration config
) {
- storeExtCand2(mid, f, node, config) and
- flowCand(mid, _, apf0, config)
+ storeCand2(mid, tc, node, _, config) and
+ flowCand(mid, _, _, apf0, config) and
+ apf.headUsesContent(tc)
}
pragma[noinline]
-private predicate flowFwdStore1(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, AccessPathFrontHead apf,
+private predicate flowFwdStore0(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
- flowFwdStore0(mid, f, node, apf0, config) and
- consCand(f, apf0, config) and
- apf.headUsesContent(f) and
- flowCand(node, _, apf, unbind(config))
+ storeCand(mid, tc, node, apf0, apf, config) and
+ flowCandConsCand(tc, apf0, config) and
+ flowCand(node, _, _, apf, unbind(config))
+}
+
+pragma[nomagic]
+private predicate flowFwdRead0(
+ Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
+ boolean fromArg, AccessPathOption argAp, Configuration config
+) {
+ flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
+ readCandFwd(node1, tc, apf0, node2, config)
}
pragma[nomagic]
private predicate flowFwdRead(
- NodeExt node, Content f, AccessPath ap0, boolean fromArg, Configuration config
+ Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- readExtCand2(mid, f, node, config) and
- apf0.headUsesContent(f) and
- flowCand(node, _, _, unbind(config))
+ exists(Node mid, TypedContent tc |
+ flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
+ flowCand(node, _, _, apf, unbind(config)) and
+ flowCandConsCand(tc, apf, unbind(config))
)
}
pragma[nomagic]
-private predicate flowConsCandFwd(
- Content f, AccessPathFront apf, AccessPath ap, Configuration config
+private predicate flowFwdConsCand(
+ TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config
) {
- exists(NodeExt n |
- flowFwd(n, _, apf, ap, config) and
- flowFwdStore1(n, f, _, apf, _, config)
+ exists(Node n |
+ flowFwd(n, _, _, apf, ap, config) and
+ flowFwdStore0(n, tc, _, apf, _, config)
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdIn(
+ DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
+) {
+ exists(ArgumentNode arg, boolean allowsFieldFlow |
+ flowFwd(arg, fromArg, argAp, apf, ap, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
+ flowCand(p, _, _, _, unbind(config))
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
+) {
+ exists(ReturnNodeExt ret, boolean allowsFieldFlow |
+ flowFwd(ret, fromArg, argAp, apf, ap, config) and
+ flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
+ flowCand(node, _, _, _, unbind(config))
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowFwdOutFromArg(
+ DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap,
+ Configuration config
+) {
+ flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config)
+}
+
+/**
+ * Holds if an argument to `call` is reached in the flow covered by `flowFwd`.
+ */
+pragma[nomagic]
+private predicate flowFwdIsEntered(
+ DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
+) {
+ exists(ParameterNode p, AccessPathFront apf |
+ flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and
+ flowCand(p, true, TAccessPathFrontSome(_), apf, config)
)
}
/**
- * Holds if data can flow from a source to `node` with the given `ap` and
- * from there flow to a sink.
+ * Holds if `node` with access path `ap` is part of a path from a source to
+ * a sink in the configuration `config`.
+ *
+ * The Boolean `toReturn` records whether the node must be returned from
+ * the enclosing callable in order to reach a sink, and if so, `returnAp`
+ * records the access path of the returned value.
*/
-private predicate flow(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flow0(node, toReturn, ap, config) and
- flowFwd(node, _, _, ap, config)
+private predicate flow(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flow0(node, toReturn, returnAp, ap, config) and
+ flowFwd(node, _, _, _, ap, config)
}
-private predicate flow0(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flowFwd(node, _, _, ap, config) and
- config.isSink(node.getNode()) and
+private predicate flow0(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flowFwd(node, _, _, _, ap, config) and
+ config.isSink(node) and
toReturn = false and
+ returnAp = TAccessPathNone() and
ap instanceof AccessPathNil
or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flow(mid, toReturn, ap, config)
+ exists(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flow(mid, toReturn, returnAp, ap, config)
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(node, _, _, ap, config) and
- localFlowBigStepExt(node, mid, false, _, config) and
- flow(mid, toReturn, nil, config) and
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(node, _, _, _, ap, config) and
+ localFlowBigStep(node, mid, false, _, config, _) and
+ flow(mid, toReturn, returnAp, nil, config) and
ap instanceof AccessPathNil
)
or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flow(mid, _, ap, config) and
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flow(mid, _, _, ap, config) and
+ toReturn = false and
+ returnAp = TAccessPathNone()
+ )
+ or
+ exists(Node mid, AccessPathNil nil |
+ flowFwd(node, _, _, _, ap, config) and
+ additionalJumpStep(node, mid, config) and
+ flow(mid, _, _, nil, config) and
+ toReturn = false and
+ returnAp = TAccessPathNone() and
+ ap instanceof AccessPathNil
+ )
+ or
+ // store
+ exists(TypedContent tc |
+ flowStore(tc, node, toReturn, returnAp, ap, config) and
+ flowConsCand(tc, ap, config)
+ )
+ or
+ // read
+ exists(Node mid, AccessPath ap0 |
+ readFlowFwd(node, _, mid, ap, ap0, config) and
+ flow(mid, toReturn, returnAp, ap0, config)
+ )
+ or
+ // flow into a callable
+ exists(DataFlowCall call |
+ flowIn(call, node, toReturn, returnAp, ap, config) and
toReturn = false
+ or
+ exists(AccessPath returnAp0 |
+ flowInToReturn(call, node, returnAp0, ap, config) and
+ flowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
)
or
- exists(NodeExt mid, AccessPathNil nil |
- flowFwd(node, _, _, ap, config) and
- additionalJumpStepExt(node, mid, config) and
- flow(mid, _, nil, config) and
- toReturn = false and
- ap instanceof AccessPathNil
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowIntoCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flow(mid, false, ap, config) and
- toReturn = false and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid, boolean allowsFieldFlow |
- flowOutOfCallableNodeCand2(node, mid, allowsFieldFlow, config) and
- flow(mid, _, ap, config) and
- toReturn = true and
- (ap instanceof AccessPathNil or allowsFieldFlow = true)
- )
- or
- exists(NodeExt mid |
- argumentValueFlowsThrough(node, mid) and
- flow(mid, toReturn, ap, config)
- )
- or
- exists(NodeExt mid, AccessPathNil nil |
- argumentFlowsThrough(node, mid, _, config) and
- flow(mid, toReturn, nil, config) and
- ap instanceof AccessPathNil and
- flowFwd(node, _, _, ap, config)
- )
- or
- exists(Content f |
- flowStore(f, node, toReturn, ap, config) and
- flowConsCand(f, ap, config)
- )
- or
- exists(NodeExt mid, AccessPath ap0 |
- readFwd(node, _, mid, ap, ap0, config) and
- flow(mid, toReturn, ap0, config)
- )
+ // flow out of a callable
+ flowOut(_, node, _, _, ap, config) and
+ toReturn = true and
+ if flowFwd(node, true, TAccessPathSome(_), _, ap, config)
+ then returnAp = TAccessPathSome(ap)
+ else returnAp = TAccessPathNone()
}
pragma[nomagic]
-private predicate storeFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate storeFlowFwd(
+ Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- storeExtCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, config) and
- ap0 = push(f, ap)
+ storeCand2(node1, tc, node2, _, config) and
+ flowFwdStore(node2, tc, ap, _, _, _, config) and
+ ap0 = push(tc, ap)
}
pragma[nomagic]
private predicate flowStore(
- Content f, NodeExt node, boolean toReturn, AccessPath ap, Configuration config
+ TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
) {
- exists(NodeExt mid, AccessPath ap0 |
- storeFwd(node, f, mid, ap, ap0, config) and
- flow(mid, toReturn, ap0, config)
+ exists(Node mid, AccessPath ap0 |
+ storeFlowFwd(node, tc, mid, ap, ap0, config) and
+ flow(mid, toReturn, returnAp, ap0, config)
)
}
pragma[nomagic]
-private predicate readFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate readFlowFwd(
+ Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- readExtCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, config) and
- ap0 = pop(f, ap) and
- flowConsCandFwd(f, _, ap0, unbind(config))
+ exists(AccessPathFrontHead apf |
+ readCandFwd(node1, tc, apf, node2, config) and
+ flowFwdRead(node2, apf, ap, _, _, _, config) and
+ ap0 = pop(tc, ap) and
+ flowFwdConsCand(tc, _, ap0, unbind(config))
+ )
}
pragma[nomagic]
-private predicate flowConsCand(Content f, AccessPath ap, Configuration config) {
- exists(NodeExt n, NodeExt mid |
- flow(mid, _, ap, config) and
- readFwd(n, f, mid, _, ap, config)
+private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) {
+ exists(Node n, Node mid |
+ flow(mid, _, _, ap, config) and
+ readFlowFwd(n, tc, mid, _, ap, config)
+ )
+}
+
+pragma[nomagic]
+private predicate flowOut(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(Node out, boolean allowsFieldFlow |
+ flow(out, toReturn, returnAp, ap, config) and
+ flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config)
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowIn(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(ParameterNode p, boolean allowsFieldFlow |
+ flow(p, toReturn, returnAp, ap, config) and
+ flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config)
+ |
+ ap instanceof AccessPathNil or allowsFieldFlow = true
+ )
+}
+
+pragma[nomagic]
+private predicate flowInToReturn(
+ DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config
+) {
+ flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config)
+}
+
+/**
+ * Holds if an output from `call` is reached in the flow covered by `flow`.
+ */
+pragma[nomagic]
+private predicate flowIsReturned(
+ DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
+ Configuration config
+) {
+ exists(ReturnNodeExt ret |
+ flowOut(call, ret, toReturn, returnAp, ap, config) and
+ flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
)
}
bindingset[conf, result]
private Configuration unbind(Configuration conf) { result >= conf and result <= conf }
-private predicate flow(Node n, Configuration config) { flow(TNormalNode(n), _, _, config) }
+private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) }
+
+pragma[noinline]
+private predicate parameterFlow(
+ ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config
+) {
+ flow(p, true, _, ap, config) and
+ c = p.getEnclosingCallable()
+}
private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParameterNode p, AccessPath ap) {
- exists(ReturnNodeExt ret, Configuration config | flow(TNormalNode(p), true, ap, config) |
- exists(Summary summary |
- parameterFlowReturn(p, ret, _, _, _, summary, config) and
- flow(ret, unbind(config))
- |
- // taint through
- summary = TSummaryTaint() and
- ap instanceof AccessPathNil
- or
- // taint setter
- summary = TSummaryTaintStore(_) and
- ap instanceof AccessPathNil
- or
- // taint getter
- summary = TSummaryReadTaint(ap.(AccessPathConsNil).getHead())
- )
- or
- exists(ContentOption contentIn |
- parameterValueFlowReturn(p, ret, _, contentIn, _) and
- flow(ret, unbind(config))
- |
- // value through/setter
- contentIn = TContentNone()
- or
- // value getter (+ setter)
- contentIn = TContentSome(ap.getHead())
- )
+ exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 |
+ parameterFlow(p, ap, ret.getEnclosingCallable(), config) and
+ flow(ret, true, TAccessPathSome(_), ap0, config) and
+ flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config)
)
}
@@ -2113,7 +2071,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, cc, sc, ap) and
config = mid.getConfiguration() and
- flow(TNormalNode(node), _, ap, unbind(config))
+ flow(node, _, _, ap, unbind(config))
)
} or
TPathNodeSink(Node node, Configuration config) {
@@ -2164,14 +2122,31 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
+ private predicate isHidden() {
+ nodeIsHidden(this.getNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ }
+
+ private PathNode getASuccessorIfHidden() {
+ this.isHidden() and
+ result = this.(PathNodeImpl).getASuccessorImpl()
+ }
+
/** Gets a successor of this node, if any. */
- PathNode getASuccessor() { none() }
+ final PathNode getASuccessor() {
+ result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
+ not this.isHidden() and
+ not result.isHidden()
+ }
/** Holds if this node is a source. */
predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
+ abstract PathNode getASuccessorImpl();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2246,7 +2221,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
result.getConfiguration() = unbind(this.getConfiguration())
}
- override PathNodeImpl getASuccessor() {
+ override PathNodeImpl getASuccessorImpl() {
// an intermediate step to another intermediate node
result = getSuccMid()
or
@@ -2283,7 +2258,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNode getASuccessor() { none() }
+ override PathNode getASuccessorImpl() { none() }
override predicate isSource() { config.isSource(node) }
}
@@ -2293,17 +2268,18 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
* a callable is recorded by `cc`.
*/
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) {
- exists(
- AccessPath ap0, Node midnode, Configuration conf, DataFlowCallable enclosing,
- LocalCallContext localCC
- |
- pathIntoLocalStep(mid, midnode, cc, enclosing, sc, ap0, conf) and
- localCC = getLocalCallContext(cc, enclosing)
+ exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC |
+ midnode = mid.getNode() and
+ conf = mid.getConfiguration() and
+ cc = mid.getCallContext() and
+ sc = mid.getSummaryCtx() and
+ localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ ap0 = mid.getAp()
|
localFlowBigStep(midnode, node, true, _, conf, localCC) and
ap = ap0
or
- localFlowBigStep(midnode, node, false, ap.(AccessPathNil).getType(), conf, localCC) and
+ localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and
ap0 instanceof AccessPathNil
)
or
@@ -2318,10 +2294,10 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
mid.getAp() instanceof AccessPathNil and
ap = TNil(getErasedNodeTypeBound(node))
or
- exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
+ exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2332,44 +2308,32 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
}
pragma[nomagic]
-private predicate pathIntoLocalStep(
- PathNodeMid mid, Node midnode, CallContext cc, DataFlowCallable enclosing, SummaryCtx sc,
- AccessPath ap0, Configuration conf
-) {
- midnode = mid.getNode() and
- cc = mid.getCallContext() and
- conf = mid.getConfiguration() and
- localFlowBigStep(midnode, _, _, _, conf, _) and
- enclosing = midnode.getEnclosingCallable() and
- sc = mid.getSummaryCtx() and
- ap0 = mid.getAp()
-}
-
-pragma[nomagic]
-private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
- readDirect(node1, f, node2) and
+private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ readCandFwd(node1, tc, _, node2, config) and
flow(node2, config)
}
pragma[nomagic]
-private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
+private predicate pathReadStep(
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+) {
ap0 = mid.getAp() and
- readCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
-private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- storeDirect(node1, f, node2) and
+private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ storeCand2(node1, tc, node2, _, config) and
flow(node2, config)
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
@@ -2399,11 +2363,11 @@ private predicate pathOutOfCallable1(
}
pragma[noinline]
-private Node getAnOutNodeCand(
+private Node getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config
) {
result = kind.getAnOutNode(call) and
- flow(TNormalNode(result), _, ap, config)
+ flow(result, _, _, ap, config)
}
/**
@@ -2415,7 +2379,7 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config |
pathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = getAnOutNodeCand(kind, call, ap, config)
+ out = getAnOutNodeFlow(kind, call, ap, config)
)
}
@@ -2439,7 +2403,7 @@ private predicate parameterCand(
DataFlowCallable callable, int i, AccessPath ap, Configuration config
) {
exists(ParameterNode p |
- flow(TNormalNode(p), _, ap, config) and
+ flow(p, _, _, ap, config) and
p.isParameterOf(callable, i)
)
}
@@ -2514,7 +2478,7 @@ pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
exists(DataFlowCall call, ReturnKindExt kind |
pathThroughCallable0(call, mid, kind, cc, ap) and
- out = getAnOutNodeCand(kind, call, ap, mid.getConfiguration())
+ out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration())
)
}
@@ -2555,10 +2519,7 @@ private module FlowExploration {
viableParamArg(_, node2, node1)
or
// flow out of a callable
- exists(DataFlowCall call, ReturnKindExt kind |
- getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = kind.getAnOutNode(call)
- )
+ viableReturnPosOut(_, getReturnPosition(node1), node2)
|
c1 = node1.getEnclosingCallable() and
c2 = node2.getEnclosingCallable() and
@@ -2603,10 +2564,10 @@ private module FlowExploration {
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
- TPartialCons(Content f, int len) { len in [1 .. 5] }
+ TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
* element of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -2615,7 +2576,7 @@ private module FlowExploration {
private class PartialAccessPath extends TPartialAccessPath {
abstract string toString();
- Content getHead() { this = TPartialCons(result, _) }
+ TypedContent getHead() { this = TPartialCons(result, _) }
int len() {
this = TPartialNil(_) and result = 0
@@ -2626,7 +2587,7 @@ private module FlowExploration {
DataFlowType getType() {
this = TPartialNil(result)
or
- exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
+ exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
@@ -2644,15 +2605,15 @@ private module FlowExploration {
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
override string toString() {
- exists(Content f, int len | this = TPartialCons(f, len) |
+ exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
if len = 1
- then result = "[" + f.toString() + "]"
- else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc.toString() + "]"
+ else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
override AccessPathFront getFront() {
- exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
+ exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
}
}
@@ -2828,11 +2789,12 @@ private module FlowExploration {
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
- exists(PartialAccessPath ap0, Content f |
- partialPathReadStep(mid, ap0, f, node, cc, config) and
+ exists(PartialAccessPath ap0, TypedContent tc |
+ partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, f, ap0, config)
+ apConsFwd(ap, tc, ap0, config) and
+ compatibleTypes(ap.getType(), getErasedNodeTypeBound(node))
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -2851,35 +2813,42 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
+ PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
+ PartialAccessPath ap2
) {
- ap1 = mid.getAp() and
- storeDirect(mid.getNode(), f, node) and
- ap2.getHead() = f and
- ap2.len() = unbindInt(ap1.len() + 1) and
- compatibleTypes(ap1.getType(), f.getType())
+ exists(Node midNode, DataFlowType contentType |
+ midNode = mid.getNode() and
+ ap1 = mid.getAp() and
+ store(midNode, tc, node, contentType) and
+ ap2.getHead() = tc and
+ ap2.len() = unbindInt(ap1.len() + 1) and
+ compatibleTypes(ap1.getType(), contentType)
+ )
}
pragma[nomagic]
private predicate apConsFwd(
- PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
+ PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
- partialPathStoreStep(mid, ap1, f, _, ap2) and
+ partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
+ PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
- ap = mid.getAp() and
- readStep(mid.getNode(), f, node) and
- ap.getHead() = f and
- config = mid.getConfiguration() and
- cc = mid.getCallContext()
+ exists(Node midNode |
+ midNode = mid.getNode() and
+ ap = mid.getAp() and
+ read(midNode, tc.getContent(), node) and
+ ap.getHead() = tc and
+ config = mid.getConfiguration() and
+ cc = mid.getCallContext()
+ )
}
private predicate partialPathOutOfCallable0(
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
index 783ac641e6e..1f42c21d5a7 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
@@ -26,13 +26,30 @@ private module Cached {
)
}
- /** Provides predicates for calculating flow-through summaries. */
+ pragma[nomagic]
+ private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
+ viableCallable(call) = result.getCallable() and
+ kind = result.getKind()
+ }
+
+ /**
+ * Holds if a value at return position `pos` can be returned to `out` via `call`,
+ * taking virtual dispatch into account.
+ */
cached
+ predicate viableReturnPosOut(DataFlowCall call, ReturnPosition pos, Node out) {
+ exists(ReturnKindExt kind |
+ pos = viableReturnPos(call, kind) and
+ out = kind.getAnOutNode(call)
+ )
+ }
+
+ /** Provides predicates for calculating flow-through summaries. */
private module FlowThrough {
/**
* The first flow-through approximation:
*
- * - Input/output access paths are abstracted with a Boolean parameter
+ * - Input access paths are abstracted with a Boolean parameter
* that indicates (non-)emptiness.
*/
private module Cand {
@@ -40,83 +57,47 @@ private module Cached {
* Holds if `p` can flow to `node` in the same callable using only
* value-preserving steps.
*
- * `read` indicates whether it is contents of `p` that can flow to `node`,
- * and `stored` indicates whether it flows to contents of `node`.
+ * `read` indicates whether it is contents of `p` that can flow to `node`.
*/
pragma[nomagic]
- private predicate parameterValueFlowCand(
- ParameterNode p, Node node, boolean read, boolean stored
- ) {
+ private predicate parameterValueFlowCand(ParameterNode p, Node node, boolean read) {
p = node and
- read = false and
- stored = false
+ read = false
or
// local flow
exists(Node mid |
- parameterValueFlowCand(p, mid, read, stored) and
+ parameterValueFlowCand(p, mid, read) and
simpleLocalFlowStep(mid, node)
)
or
// read
- exists(Node mid, boolean readMid, boolean storedMid |
- parameterValueFlowCand(p, mid, readMid, storedMid) and
- readStep(mid, _, node) and
- stored = false
- |
- // value neither read nor stored prior to read
- readMid = false and
- storedMid = false and
- read = true
- or
- // value (possibly read and then) stored prior to read (same content)
- read = readMid and
- storedMid = true
- )
- or
- // store
exists(Node mid |
- parameterValueFlowCand(p, mid, read, false) and
- storeStep(mid, _, node) and
- stored = true
+ parameterValueFlowCand(p, mid, false) and
+ readStep(mid, _, node) and
+ read = true
)
or
- // flow through: no prior read or store
+ // flow through: no prior read
exists(ArgumentNode arg |
- parameterValueFlowArgCand(p, arg, false, false) and
- argumentValueFlowsThroughCand(arg, node, read, stored)
+ parameterValueFlowArgCand(p, arg, false) and
+ argumentValueFlowsThroughCand(arg, node, read)
)
or
- // flow through: no read or store inside method
+ // flow through: no read inside method
exists(ArgumentNode arg |
- parameterValueFlowArgCand(p, arg, read, stored) and
- argumentValueFlowsThroughCand(arg, node, false, false)
- )
- or
- // flow through: possible prior read and prior store with compatible
- // flow-through method
- exists(ArgumentNode arg, boolean mid |
- parameterValueFlowArgCand(p, arg, read, mid) and
- argumentValueFlowsThroughCand(arg, node, mid, stored)
+ parameterValueFlowArgCand(p, arg, read) and
+ argumentValueFlowsThroughCand(arg, node, false)
)
}
pragma[nomagic]
- private predicate parameterValueFlowArgCand(
- ParameterNode p, ArgumentNode arg, boolean read, boolean stored
- ) {
- parameterValueFlowCand(p, arg, read, stored)
+ private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) {
+ parameterValueFlowCand(p, arg, read)
}
pragma[nomagic]
predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) {
- parameterValueFlowCand(p, n.getPreUpdateNode(), false, false)
- }
-
- pragma[nomagic]
- private predicate parameterValueFlowsToPostUpdateCand(
- ParameterNode p, PostUpdateNode n, boolean read
- ) {
- parameterValueFlowCand(p, n, read, true)
+ parameterValueFlowCand(p, n.getPreUpdateNode(), false)
}
/**
@@ -125,33 +106,21 @@ private module Cached {
* into account.
*
* `read` indicates whether it is contents of `p` that can flow to the return
- * node, and `stored` indicates whether it flows to contents of the return
* node.
*/
- predicate parameterValueFlowReturnCand(
- ParameterNode p, ReturnKindExt kind, boolean read, boolean stored
- ) {
+ predicate parameterValueFlowReturnCand(ParameterNode p, ReturnKind kind, boolean read) {
exists(ReturnNode ret |
- parameterValueFlowCand(p, ret, read, stored) and
- kind = TValueReturn(ret.getKind())
- )
- or
- exists(ParameterNode p2, int pos2, PostUpdateNode n |
- parameterValueFlowsToPostUpdateCand(p, n, read) and
- parameterValueFlowsToPreUpdateCand(p2, n) and
- p2.isParameterOf(_, pos2) and
- kind = TParamUpdate(pos2) and
- p != p2 and
- stored = true
+ parameterValueFlowCand(p, ret, read) and
+ kind = ret.getKind()
)
}
pragma[nomagic]
private predicate argumentValueFlowsThroughCand0(
- DataFlowCall call, ArgumentNode arg, ReturnKindExt kind, boolean read, boolean stored
+ DataFlowCall call, ArgumentNode arg, ReturnKind kind, boolean read
) {
exists(ParameterNode param | viableParamArg(call, param, arg) |
- parameterValueFlowReturnCand(param, kind, read, stored)
+ parameterValueFlowReturnCand(param, kind, read)
)
}
@@ -159,22 +128,19 @@ private module Cached {
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
* not taking call contexts into account.
*
- * `read` indicates whether it is contents of `arg` that can flow to `out`, and
- * `stored` indicates whether it flows to contents of `out`.
+ * `read` indicates whether it is contents of `arg` that can flow to `out`.
*/
- predicate argumentValueFlowsThroughCand(
- ArgumentNode arg, Node out, boolean read, boolean stored
- ) {
- exists(DataFlowCall call, ReturnKindExt kind |
- argumentValueFlowsThroughCand0(call, arg, kind, read, stored) and
- out = kind.getAnOutNode(call)
+ predicate argumentValueFlowsThroughCand(ArgumentNode arg, Node out, boolean read) {
+ exists(DataFlowCall call, ReturnKind kind |
+ argumentValueFlowsThroughCand0(call, arg, kind, read) and
+ out = getAnOutNode(call, kind)
)
}
predicate cand(ParameterNode p, Node n) {
- parameterValueFlowCand(p, n, _, _) and
+ parameterValueFlowCand(p, n, _) and
(
- parameterValueFlowReturnCand(p, _, _, _)
+ parameterValueFlowReturnCand(p, _, _)
or
parameterValueFlowsToPreUpdateCand(p, _)
)
@@ -187,7 +153,6 @@ private module Cached {
(
n instanceof ParameterNode or
n instanceof OutNode or
- n instanceof PostUpdateNode or
readStep(_, _, n) or
n instanceof CastNode
)
@@ -200,10 +165,6 @@ private module Cached {
or
n instanceof ReturnNode
or
- Cand::parameterValueFlowsToPreUpdateCand(_, n)
- or
- storeStep(n, _, _)
- or
readStep(n, _, _)
or
n instanceof CastNode
@@ -237,230 +198,167 @@ private module Cached {
/**
* The final flow-through calculation:
*
- * - Input/output access paths are abstracted with a `ContentOption` parameter
- * that represents the head of the access path. `TContentNone()` means that
- * the access path is unrestricted.
+ * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`)
+ * or summarized as a single read step with before and after types recorded
+ * in the `ReadStepTypesOption` parameter.
* - Types are checked using the `compatibleTypes()` relation.
*/
- cached
private module Final {
/**
* Holds if `p` can flow to `node` in the same callable using only
- * value-preserving steps, not taking call contexts into account.
+ * value-preserving steps and possibly a single read step, not taking
+ * call contexts into account.
*
- * `contentIn` describes the content of `p` that can flow to `node`
- * (if any), and `contentOut` describes the content of `node` that
- * it flows to (if any).
+ * If a read step was taken, then `read` captures the `Content`, the
+ * container type, and the content type.
*/
- private predicate parameterValueFlow(
- ParameterNode p, Node node, ContentOption contentIn, ContentOption contentOut
- ) {
- parameterValueFlow0(p, node, contentIn, contentOut) and
+ predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) {
+ parameterValueFlow0(p, node, read) and
if node instanceof CastingNode
then
// normal flow through
- contentIn = TContentNone() and
- contentOut = TContentNone() and
+ read = TReadStepTypesNone() and
compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node))
or
// getter
- exists(Content fIn |
- contentIn.getContent() = fIn and
- contentOut = TContentNone() and
- compatibleTypes(fIn.getType(), getErasedNodeTypeBound(node))
- )
- or
- // (getter+)setter
- exists(Content fOut |
- contentOut.getContent() = fOut and
- compatibleTypes(fOut.getContainerType(), getErasedNodeTypeBound(node))
- )
+ compatibleTypes(read.getContentType(), getErasedNodeTypeBound(node))
else any()
}
pragma[nomagic]
- private predicate parameterValueFlow0(
- ParameterNode p, Node node, ContentOption contentIn, ContentOption contentOut
- ) {
+ private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) {
p = node and
Cand::cand(p, _) and
- contentIn = TContentNone() and
- contentOut = TContentNone()
+ read = TReadStepTypesNone()
or
// local flow
exists(Node mid |
- parameterValueFlow(p, mid, contentIn, contentOut) and
+ parameterValueFlow(p, mid, read) and
LocalFlowBigStep::localFlowBigStep(mid, node)
)
or
// read
- exists(Node mid, Content f, ContentOption contentInMid, ContentOption contentOutMid |
- parameterValueFlow(p, mid, contentInMid, contentOutMid) and
- readStep(mid, f, node)
- |
- // value neither read nor stored prior to read
- contentInMid = TContentNone() and
- contentOutMid = TContentNone() and
- contentIn.getContent() = f and
- contentOut = TContentNone() and
- Cand::parameterValueFlowReturnCand(p, _, true, _) and
- compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType())
- or
- // value (possibly read and then) stored prior to read (same content)
- contentIn = contentInMid and
- contentOutMid.getContent() = f and
- contentOut = TContentNone()
+ exists(Node mid |
+ parameterValueFlow(p, mid, TReadStepTypesNone()) and
+ readStepWithTypes(mid, read.getContainerType(), read.getContent(), node,
+ read.getContentType()) and
+ Cand::parameterValueFlowReturnCand(p, _, true) and
+ compatibleTypes(getErasedNodeTypeBound(p), read.getContainerType())
)
or
- // store
- exists(Node mid, Content f |
- parameterValueFlow(p, mid, contentIn, TContentNone()) and
- storeStep(mid, f, node) and
- contentOut.getContent() = f
- |
- contentIn = TContentNone() and
- compatibleTypes(getErasedNodeTypeBound(p), f.getType())
- or
- compatibleTypes(contentIn.getContent().getType(), f.getType())
- )
- or
- // flow through: no prior read or store
+ // flow through: no prior read
exists(ArgumentNode arg |
- parameterValueFlowArg(p, arg, TContentNone(), TContentNone()) and
- argumentValueFlowsThrough(_, arg, contentIn, contentOut, node)
+ parameterValueFlowArg(p, arg, TReadStepTypesNone()) and
+ argumentValueFlowsThrough(arg, read, node)
)
or
- // flow through: no read or store inside method
+ // flow through: no read inside method
exists(ArgumentNode arg |
- parameterValueFlowArg(p, arg, contentIn, contentOut) and
- argumentValueFlowsThrough(_, arg, TContentNone(), TContentNone(), node)
- )
- or
- // flow through: possible prior read and prior store with compatible
- // flow-through method
- exists(ArgumentNode arg, ContentOption contentMid |
- parameterValueFlowArg(p, arg, contentIn, contentMid) and
- argumentValueFlowsThrough(_, arg, contentMid, contentOut, node)
+ parameterValueFlowArg(p, arg, read) and
+ argumentValueFlowsThrough(arg, TReadStepTypesNone(), node)
)
}
pragma[nomagic]
private predicate parameterValueFlowArg(
- ParameterNode p, ArgumentNode arg, ContentOption contentIn, ContentOption contentOut
+ ParameterNode p, ArgumentNode arg, ReadStepTypesOption read
) {
- parameterValueFlow(p, arg, contentIn, contentOut) and
- Cand::argumentValueFlowsThroughCand(arg, _, _, _)
+ parameterValueFlow(p, arg, read) and
+ Cand::argumentValueFlowsThroughCand(arg, _, _)
}
pragma[nomagic]
private predicate argumentValueFlowsThrough0(
- DataFlowCall call, ArgumentNode arg, ReturnKindExt kind, ContentOption contentIn,
- ContentOption contentOut
+ DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read
) {
exists(ParameterNode param | viableParamArg(call, param, arg) |
- parameterValueFlowReturn(param, _, kind, contentIn, contentOut)
+ parameterValueFlowReturn(param, kind, read)
)
}
/**
- * Holds if `arg` flows to `out` through `call` using only value-preserving steps,
- * not taking call contexts into account.
+ * Holds if `arg` flows to `out` through a call using only
+ * value-preserving steps and possibly a single read step, not taking
+ * call contexts into account.
*
- * `contentIn` describes the content of `arg` that can flow to `out` (if any), and
- * `contentOut` describes the content of `out` that it flows to (if any).
+ * If a read step was taken, then `read` captures the `Content`, the
+ * container type, and the content type.
*/
- cached
- predicate argumentValueFlowsThrough(
- DataFlowCall call, ArgumentNode arg, ContentOption contentIn, ContentOption contentOut,
- Node out
- ) {
- exists(ReturnKindExt kind |
- argumentValueFlowsThrough0(call, arg, kind, contentIn, contentOut) and
- out = kind.getAnOutNode(call)
+ pragma[nomagic]
+ predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) {
+ exists(DataFlowCall call, ReturnKind kind |
+ argumentValueFlowsThrough0(call, arg, kind, read) and
+ out = getAnOutNode(call, kind)
|
// normal flow through
- contentIn = TContentNone() and
- contentOut = TContentNone() and
+ read = TReadStepTypesNone() and
compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out))
or
// getter
- exists(Content fIn |
- contentIn.getContent() = fIn and
- contentOut = TContentNone() and
- compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and
- compatibleTypes(fIn.getType(), getErasedNodeTypeBound(out))
- )
- or
- // setter
- exists(Content fOut |
- contentIn = TContentNone() and
- contentOut.getContent() = fOut and
- compatibleTypes(getErasedNodeTypeBound(arg), fOut.getType()) and
- compatibleTypes(fOut.getContainerType(), getErasedNodeTypeBound(out))
- )
- or
- // getter+setter
- exists(Content fIn, Content fOut |
- contentIn.getContent() = fIn and
- contentOut.getContent() = fOut and
- compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and
- compatibleTypes(fOut.getContainerType(), getErasedNodeTypeBound(out))
- )
+ compatibleTypes(getErasedNodeTypeBound(arg), read.getContainerType()) and
+ compatibleTypes(read.getContentType(), getErasedNodeTypeBound(out))
)
}
/**
- * Holds if `p` can flow to the pre-update node associated with post-update
- * node `n`, in the same callable, using only value-preserving steps.
+ * Holds if `arg` flows to `out` through a call using only
+ * value-preserving steps and a single read step, not taking call
+ * contexts into account, thus representing a getter-step.
*/
- cached
- predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) {
- parameterValueFlow(p, n.getPreUpdateNode(), TContentNone(), TContentNone())
- }
-
- pragma[nomagic]
- private predicate parameterValueFlowsToPostUpdate(
- ParameterNode p, PostUpdateNode n, ContentOption contentIn, ContentOption contentOut
- ) {
- parameterValueFlow(p, n, contentIn, contentOut) and
- contentOut.hasContent()
+ predicate getterStep(ArgumentNode arg, Content c, Node out) {
+ argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out)
}
/**
* Holds if `p` can flow to a return node of kind `kind` in the same
- * callable using only value-preserving steps.
+ * callable using only value-preserving steps and possibly a single read
+ * step.
*
- * `contentIn` describes the content of `p` that can flow to the return
- * node (if any), and `contentOut` describes the content of the return
- * node that it flows to (if any).
+ * If a read step was taken, then `read` captures the `Content`, the
+ * container type, and the content type.
*/
- cached
- predicate parameterValueFlowReturn(
- ParameterNode p, Node ret, ReturnKindExt kind, ContentOption contentIn,
- ContentOption contentOut
+ private predicate parameterValueFlowReturn(
+ ParameterNode p, ReturnKind kind, ReadStepTypesOption read
) {
- ret =
- any(ReturnNode n |
- parameterValueFlow(p, n, contentIn, contentOut) and
- kind = TValueReturn(n.getKind())
- )
- or
- ret =
- any(PostUpdateNode n |
- exists(ParameterNode p2, int pos2 |
- parameterValueFlowsToPostUpdate(p, n, contentIn, contentOut) and
- parameterValueFlowsToPreUpdate(p2, n) and
- p2.isParameterOf(_, pos2) and
- kind = TParamUpdate(pos2) and
- p != p2
- )
- )
+ exists(ReturnNode ret |
+ parameterValueFlow(p, ret, read) and
+ kind = ret.getKind()
+ )
}
}
import Final
}
+ /**
+ * Holds if `p` can flow to the pre-update node associated with post-update
+ * node `n`, in the same callable, using only value-preserving steps.
+ */
+ cached
+ predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) {
+ parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone())
+ }
+
+ private predicate store(
+ Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
+ ) {
+ storeStep(node1, c, node2) and
+ readStep(_, c, _) and
+ contentType = getErasedNodeTypeBound(node1) and
+ containerType = getErasedNodeTypeBound(node2)
+ or
+ exists(Node n1, Node n2 |
+ n1 = node1.(PostUpdateNode).getPreUpdateNode() and
+ n2 = node2.(PostUpdateNode).getPreUpdateNode()
+ |
+ argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1)
+ or
+ readStep(n2, c, n1) and
+ contentType = getErasedNodeTypeBound(n1) and
+ containerType = getErasedNodeTypeBound(n2)
+ )
+ }
+
/**
* Holds if data can flow from `node1` to `node2` via a direct assignment to
* `f`.
@@ -469,17 +367,8 @@ private module Cached {
* been stored into, in order to handle cases like `x.f1.f2 = y`.
*/
cached
- predicate storeDirect(Node node1, Content f, Node node2) {
- storeStep(node1, f, node2) and readStep(_, f, _)
- or
- exists(Node n1, Node n2 |
- n1 = node1.(PostUpdateNode).getPreUpdateNode() and
- n2 = node2.(PostUpdateNode).getPreUpdateNode()
- |
- argumentValueFlowsThrough(_, n2, TContentSome(f), TContentNone(), n1)
- or
- readStep(n2, f, n1)
- )
+ predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) {
+ store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
}
import FlowThrough
@@ -520,6 +409,24 @@ private module Cached {
newtype TReturnKindExt =
TValueReturn(ReturnKind kind) or
TParamUpdate(int pos) { exists(ParameterNode p | p.isParameterOf(_, pos)) }
+
+ cached
+ newtype TBooleanOption =
+ TBooleanNone() or
+ TBooleanSome(boolean b) { b = true or b = false }
+
+ cached
+ newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
+
+ cached
+ newtype TAccessPathFront =
+ TFrontNil(DataFlowType t) or
+ TFrontHead(TypedContent tc)
+
+ cached
+ newtype TAccessPathFrontOption =
+ TAccessPathFrontNone() or
+ TAccessPathFrontSome(AccessPathFront apf)
}
/**
@@ -529,26 +436,38 @@ class CastingNode extends Node {
CastingNode() {
this instanceof ParameterNode or
this instanceof CastNode or
- this instanceof OutNode or
- this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode
+ this instanceof OutNodeExt or
+ // For reads, `x.f`, we want to check that the tracked type after the read (which
+ // is obtained by popping the head of the access path stack) is compatible with
+ // the type of `x.f`.
+ readStep(_, _, this)
}
}
-newtype TContentOption =
- TContentNone() or
- TContentSome(Content f)
+private predicate readStepWithTypes(
+ Node n1, DataFlowType container, Content c, Node n2, DataFlowType content
+) {
+ readStep(n1, c, n2) and
+ container = getErasedNodeTypeBound(n1) and
+ content = getErasedNodeTypeBound(n2)
+}
-class ContentOption extends TContentOption {
- Content getContent() { this = TContentSome(result) }
-
- predicate hasContent() { exists(this.getContent()) }
-
- string toString() {
- result = this.getContent().toString()
- or
- not this.hasContent() and
- result = ""
+private newtype TReadStepTypesOption =
+ TReadStepTypesNone() or
+ TReadStepTypesSome(DataFlowType container, Content c, DataFlowType content) {
+ readStepWithTypes(_, container, c, _, content)
}
+
+private class ReadStepTypesOption extends TReadStepTypesOption {
+ predicate isSome() { this instanceof TReadStepTypesSome }
+
+ DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) }
+
+ Content getContent() { this = TReadStepTypesSome(_, result, _) }
+
+ DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) }
+
+ string toString() { if this.isSome() then result = "Some(..)" else result = "None()" }
}
/**
@@ -678,6 +597,18 @@ class ReturnNodeExt extends Node {
}
}
+/**
+ * A node to which data can flow from a call. Either an ordinary out node
+ * or a post-update node associated with a call argument.
+ */
+class OutNodeExt extends Node {
+ OutNodeExt() {
+ this instanceof OutNode
+ or
+ this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode
+ }
+}
+
/**
* 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
@@ -688,7 +619,7 @@ abstract class ReturnKindExt extends TReturnKindExt {
abstract string toString();
/** Gets a node corresponding to data flow out of `call`. */
- abstract Node getAnOutNode(DataFlowCall call);
+ abstract OutNodeExt getAnOutNode(DataFlowCall call);
}
class ValueReturnKind extends ReturnKindExt, TValueReturn {
@@ -700,7 +631,9 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn {
override string toString() { result = kind.toString() }
- override Node getAnOutNode(DataFlowCall call) { result = getAnOutNode(call, this.getKind()) }
+ override OutNodeExt getAnOutNode(DataFlowCall call) {
+ result = getAnOutNode(call, this.getKind())
+ }
}
class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
@@ -712,9 +645,9 @@ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
override string toString() { result = "param update " + pos }
- override PostUpdateNode getAnOutNode(DataFlowCall call) {
+ override OutNodeExt getAnOutNode(DataFlowCall call) {
exists(ArgumentNode arg |
- result.getPreUpdateNode() = arg and
+ result.(PostUpdateNode).getPreUpdateNode() = arg and
arg.argumentOf(call, this.getPosition())
)
}
@@ -779,77 +712,79 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
result = viableCallable(call) and cc instanceof CallContextReturn
}
-newtype TSummary =
- TSummaryVal() or
- TSummaryTaint() or
- TSummaryReadVal(Content f) or
- TSummaryReadTaint(Content f) or
- TSummaryTaintStore(Content f)
-
-/**
- * A summary of flow through a callable. This can either be value-preserving
- * if no additional steps are used, taint-flow if at least one additional step
- * is used, or any one of those combined with a store or a read. Summaries
- * recorded at a return node are restricted to include at least one additional
- * step, as the value-based summaries are calculated independent of the
- * configuration.
- */
-class Summary extends TSummary {
- string toString() {
- result = "Val" and this = TSummaryVal()
- or
- result = "Taint" and this = TSummaryTaint()
- or
- exists(Content f |
- result = "ReadVal " + f.toString() and this = TSummaryReadVal(f)
- or
- result = "ReadTaint " + f.toString() and this = TSummaryReadTaint(f)
- or
- result = "TaintStore " + f.toString() and this = TSummaryTaintStore(f)
- )
- }
-
- /** Gets the summary that results from extending this with an additional step. */
- Summary additionalStep() {
- this = TSummaryVal() and result = TSummaryTaint()
- or
- this = TSummaryTaint() and result = TSummaryTaint()
- or
- exists(Content f | this = TSummaryReadVal(f) and result = TSummaryReadTaint(f))
- or
- exists(Content f | this = TSummaryReadTaint(f) and result = TSummaryReadTaint(f))
- }
-
- /** Gets the summary that results from extending this with a read. */
- Summary readStep(Content f) { this = TSummaryVal() and result = TSummaryReadVal(f) }
-
- /** Gets the summary that results from extending this with a store. */
- Summary storeStep(Content f) { this = TSummaryTaint() and result = TSummaryTaintStore(f) }
-
- /** Gets the summary that results from extending this with `step`. */
- bindingset[this, step]
- Summary compose(Summary step) {
- this = TSummaryVal() and result = step
- or
- this = TSummaryTaint() and
- (step = TSummaryTaint() or step = TSummaryTaintStore(_)) and
- result = step
- or
- exists(Content f |
- this = TSummaryReadVal(f) and step = TSummaryTaint() and result = TSummaryReadTaint(f)
- )
- or
- this = TSummaryReadTaint(_) and step = TSummaryTaint() and result = this
- }
-
- /** Holds if this summary does not include any taint steps. */
- predicate isPartial() {
- this = TSummaryVal() or
- this = TSummaryReadVal(_)
- }
-}
-
pragma[noinline]
DataFlowType getErasedNodeTypeBound(Node n) { result = getErasedRepr(n.getTypeBound()) }
-predicate readDirect = readStep/3;
+predicate read = readStep/3;
+
+/** An optional Boolean value. */
+class BooleanOption extends TBooleanOption {
+ string toString() {
+ this = TBooleanNone() and result = ""
+ or
+ this = TBooleanSome(any(boolean b | result = b.toString()))
+ }
+}
+
+/** Content tagged with the type of a containing object. */
+class TypedContent extends MkTypedContent {
+ private Content c;
+ private DataFlowType t;
+
+ TypedContent() { this = MkTypedContent(c, t) }
+
+ /** Gets the content. */
+ Content getContent() { result = c }
+
+ /** Gets the container type. */
+ DataFlowType getContainerType() { result = t }
+
+ /** Gets a textual representation of this content. */
+ string toString() { result = c.toString() }
+}
+
+/**
+ * The front of an access path. This is either a head or a nil.
+ */
+abstract class AccessPathFront extends TAccessPathFront {
+ abstract string toString();
+
+ abstract DataFlowType getType();
+
+ abstract boolean toBoolNonEmpty();
+
+ predicate headUsesContent(TypedContent tc) { this = TFrontHead(tc) }
+}
+
+class AccessPathFrontNil extends AccessPathFront, TFrontNil {
+ private DataFlowType t;
+
+ AccessPathFrontNil() { this = TFrontNil(t) }
+
+ override string toString() { result = ppReprType(t) }
+
+ override DataFlowType getType() { result = t }
+
+ override boolean toBoolNonEmpty() { result = false }
+}
+
+class AccessPathFrontHead extends AccessPathFront, TFrontHead {
+ private TypedContent tc;
+
+ AccessPathFrontHead() { this = TFrontHead(tc) }
+
+ override string toString() { result = tc.toString() }
+
+ override DataFlowType getType() { result = tc.getContainerType() }
+
+ override boolean toBoolNonEmpty() { result = true }
+}
+
+/** An optional access path front. */
+class AccessPathFrontOption extends TAccessPathFrontOption {
+ string toString() {
+ this = TAccessPathFrontNone() and result = ""
+ or
+ this = TAccessPathFrontSome(any(AccessPathFront apf | result = apf.toString()))
+ }
+}
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 c5cf4180765..ff809e0d3eb 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
@@ -8,16 +8,28 @@ private import DataFlowDispatch
* to the callable. Instance arguments (`this` pointer) are also included.
*/
class ArgumentNode extends InstructionNode {
- ArgumentNode() { exists(CallInstruction call | this.getInstruction() = call.getAnArgument()) }
+ ArgumentNode() {
+ exists(CallInstruction call |
+ instr = call.getAnArgument()
+ or
+ instr.(ReadSideEffectInstruction).getPrimaryInstruction() = call
+ )
+ }
/**
* Holds if this argument occurs at the given position in the given call.
* The instance argument is considered to have index `-1`.
*/
predicate argumentOf(DataFlowCall call, int pos) {
- this.getInstruction() = call.getPositionalArgument(pos)
+ instr = call.getPositionalArgument(pos)
or
- this.getInstruction() = call.getThisArgument() and pos = -1
+ instr = call.getThisArgument() and pos = -1
+ or
+ exists(ReadSideEffectInstruction read |
+ read = instr and
+ read.getPrimaryInstruction() = call and
+ pos = getArgumentPosOfSideEffect(read.getIndex())
+ )
}
/** Gets the call in which this node is an argument. */
@@ -74,7 +86,12 @@ class ReturnValueNode extends ReturnNode {
class ReturnIndirectionNode extends ReturnNode {
override ReturnIndirectionInstruction primary;
- override ReturnKind getKind() { result = TIndirectReturnKind(primary.getParameter().getIndex()) }
+ override ReturnKind getKind() {
+ result = TIndirectReturnKind(-1) and
+ primary.isThisIndirection()
+ or
+ result = TIndirectReturnKind(primary.getParameter().getIndex())
+ }
}
/** A data flow node that represents the output of a call. */
@@ -111,8 +128,13 @@ private class SideEffectOutNode extends OutNode {
* `kind`.
*/
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
- result.getCall() = call and
- result.getReturnKind() = kind
+ // There should be only one `OutNode` for a given `(call, kind)` pair. Showing the optimizer that
+ // this is true helps it make better decisions downstream, especially in virtual dispatch.
+ result =
+ unique(OutNode outNode |
+ outNode.getCall() = call and
+ outNode.getReturnKind() = kind
+ )
}
/**
@@ -138,12 +160,6 @@ class Content extends TContent {
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
}
-
- /** Gets the type of the object containing this content. */
- abstract Type getContainerType();
-
- /** Gets the type of this content. */
- abstract Type getType();
}
private class FieldContent extends Content, TFieldContent {
@@ -158,26 +174,32 @@ private class FieldContent extends Content, TFieldContent {
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
f.getLocation().hasLocationInfo(path, sl, sc, el, ec)
}
-
- override Type getContainerType() { result = f.getDeclaringType() }
-
- override Type getType() { result = f.getType() }
}
private class CollectionContent extends Content, TCollectionContent {
override string toString() { result = "collection" }
-
- override Type getContainerType() { none() }
-
- override Type getType() { none() }
}
private class ArrayContent extends Content, TArrayContent {
override string toString() { result = "array" }
+}
- override Type getContainerType() { none() }
+private predicate storeStepNoChi(Node node1, Content f, PostUpdateNode node2) {
+ exists(FieldAddressInstruction fa, StoreInstruction store |
+ store = node2.asInstruction() and
+ store.getDestinationAddress() = fa and
+ store.getSourceValue() = node1.asInstruction() and
+ f.(FieldContent).getField() = fa.getField()
+ )
+}
- override Type getType() { none() }
+private predicate storeStepChi(Node node1, Content f, PostUpdateNode node2) {
+ exists(FieldAddressInstruction fa, StoreInstruction store |
+ node1.asInstruction() = store and
+ store.getDestinationAddress() = fa and
+ node2.asInstruction().(ChiInstruction).getPartial() = store and
+ f.(FieldContent).getField() = fa.getField()
+ )
}
/**
@@ -186,7 +208,8 @@ private class ArrayContent extends Content, TArrayContent {
* value of `node1`.
*/
predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
- none() // stub implementation
+ storeStepNoChi(node1, f, node2) or
+ storeStepChi(node1, f, node2)
}
/**
@@ -195,7 +218,12 @@ predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
* `node2`.
*/
predicate readStep(Node node1, Content f, Node node2) {
- none() // stub implementation
+ exists(FieldAddressInstruction fa, LoadInstruction load |
+ load.getSourceAddress() = fa and
+ node1.asInstruction() = load.getSourceValueOperand().getAnyDef() and
+ fa.getField() = f.(FieldContent).getField() and
+ load = node2.asInstruction()
+ )
}
/**
@@ -270,3 +298,6 @@ predicate isImmutableOrUnobservable(Node n) {
// complex to model here.
any()
}
+
+/** Holds if `n` should be hidden from path explanations. */
+predicate nodeIsHidden(Node n) { none() }
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 193173da442..bee21b93cb0 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
@@ -54,8 +54,8 @@ class Node extends TIRDataFlowNode {
/** Gets the argument that defines this `DefinitionByReferenceNode`, if any. */
Expr asDefiningArgument() { result = this.(DefinitionByReferenceNode).getArgument() }
- /** Gets the parameter corresponding to this node, if any. */
- Parameter asParameter() { result = this.(ParameterNode).getParameter() }
+ /** Gets the positional parameter corresponding to this node, if any. */
+ Parameter asParameter() { result = this.(ExplicitParameterNode).getParameter() }
/**
* Gets the variable corresponding to this node, if any. This can be used for
@@ -63,6 +63,16 @@ class Node extends TIRDataFlowNode {
*/
Variable asVariable() { result = this.(VariableNode).getVariable() }
+ /**
+ * Gets the expression that is partially defined by this node, if any.
+ *
+ * Partial definitions are created for field stores (`x.y = taint();` is a partial
+ * definition of `x`), and for calls that may change the value of an object (so
+ * `x.set(taint())` is a partial definition of `x`, and `transfer(&x, taint())` is
+ * a partial definition of `&x`).
+ */
+ Expr asPartialDefinition() { result = this.(PartialDefinitionNode).getDefinedExpr() }
+
/**
* DEPRECATED: See UninitializedNode.
*
@@ -96,6 +106,9 @@ class Node extends TIRDataFlowNode {
string toString() { none() } // overridden by subclasses
}
+/**
+ * An instruction, viewed as a node in a data flow graph.
+ */
class InstructionNode extends Node, TInstructionNode {
Instruction instr;
@@ -142,30 +155,91 @@ class ExprNode extends InstructionNode {
override string toString() { result = this.asConvertedExpr().toString() }
}
+/**
+ * INTERNAL: do not use. Translates a parameter/argument index into a negative
+ * number that denotes the index of its side effect (pointer indirection).
+ */
+bindingset[index]
+int getArgumentPosOfSideEffect(int index) {
+ // -1 -> -2
+ // 0 -> -3
+ // 1 -> -4
+ // ...
+ result = -3 - index
+}
+
/**
* The value of a parameter at function entry, viewed as a node in a data
- * flow graph.
+ * flow graph. This includes both explicit parameters such as `x` in `f(x)`
+ * and implicit parameters such as `this` in `x.f()`.
+ *
+ * To match a specific kind of parameter, consider using one of the subclasses
+ * `ExplicitParameterNode`, `ThisParameterNode`, or
+ * `ParameterIndirectionNode`.
*/
class ParameterNode extends InstructionNode {
- override InitializeParameterInstruction instr;
+ ParameterNode() {
+ // To avoid making this class abstract, we enumerate its values here
+ instr instanceof InitializeParameterInstruction
+ or
+ instr instanceof InitializeIndirectionInstruction
+ }
/**
- * Holds if this node is the parameter of `c` at the specified (zero-based)
- * position. The implicit `this` parameter is considered to have index `-1`.
+ * Holds if this node is the parameter of `f` at the specified position. The
+ * implicit `this` parameter is considered to have position `-1`, and
+ * pointer-indirection parameters are at further negative positions.
*/
- predicate isParameterOf(Function f, int i) { f.getParameter(i) = instr.getParameter() }
+ predicate isParameterOf(Function f, int pos) { none() } // overridden by subclasses
+}
+/** An explicit positional parameter, not including `this` or `...`. */
+private class ExplicitParameterNode extends ParameterNode {
+ override InitializeParameterInstruction instr;
+
+ ExplicitParameterNode() { exists(instr.getParameter()) }
+
+ override predicate isParameterOf(Function f, int pos) {
+ f.getParameter(pos) = instr.getParameter()
+ }
+
+ /** Gets the `Parameter` associated with this node. */
Parameter getParameter() { result = instr.getParameter() }
override string toString() { result = instr.getParameter().toString() }
}
-private class ThisParameterNode extends InstructionNode {
- override InitializeThisInstruction instr;
+/** An implicit `this` parameter. */
+class ThisParameterNode extends ParameterNode {
+ override InitializeParameterInstruction instr;
+
+ ThisParameterNode() { instr.getIRVariable() instanceof IRThisVariable }
+
+ override predicate isParameterOf(Function f, int pos) {
+ pos = -1 and instr.getEnclosingFunction() = f
+ }
override string toString() { result = "this" }
}
+/** A synthetic parameter to model the pointed-to object of a pointer parameter. */
+class ParameterIndirectionNode extends ParameterNode {
+ override InitializeIndirectionInstruction instr;
+
+ override predicate isParameterOf(Function f, int pos) {
+ exists(int index |
+ f.getParameter(index) = instr.getParameter()
+ or
+ index = -1 and
+ instr.getIRVariable().(IRThisVariable).getEnclosingFunction() = f
+ |
+ pos = getArgumentPosOfSideEffect(index)
+ )
+ }
+
+ override string toString() { result = "*" + instr.getIRVariable().toString() }
+}
+
/**
* DEPRECATED: Data flow was never an accurate way to determine what
* expressions might be uninitialized. It errs on the side of saying that
@@ -204,6 +278,67 @@ abstract class PostUpdateNode extends InstructionNode {
abstract Node getPreUpdateNode();
}
+/**
+ * The base class for nodes that perform "partial definitions".
+ *
+ * In contrast to a normal "definition", which provides a new value for
+ * something, a partial definition is an expression that may affect a
+ * value, but does not necessarily replace it entirely. For example:
+ * ```
+ * x.y = 1; // a partial definition of the object `x`.
+ * x.y.z = 1; // a partial definition of the object `x.y`.
+ * x.setY(1); // a partial definition of the object `x`.
+ * setY(&x); // a partial definition of the object `x`.
+ * ```
+ */
+abstract private class PartialDefinitionNode extends PostUpdateNode, TInstructionNode {
+ abstract Expr getDefinedExpr();
+}
+
+private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode {
+ override ChiInstruction instr;
+ FieldAddressInstruction field;
+
+ ExplicitFieldStoreQualifierNode() {
+ not instr.isResultConflated() and
+ exists(StoreInstruction store |
+ instr.getPartial() = store and field = store.getDestinationAddress()
+ )
+ }
+
+ // There might be multiple `ChiInstructions` that has a particular instruction as
+ // the total operand - so this definition gives consistency errors in
+ // DataFlowImplConsistency::Consistency. However, it's not clear what (if any) implications
+ // this consistency failure has.
+ override Node getPreUpdateNode() { result.asInstruction() = instr.getTotal() }
+
+ override Expr getDefinedExpr() {
+ result = field.getObjectAddress().getUnconvertedResultExpression()
+ }
+}
+
+/**
+ * Not every store instruction generates a chi instruction that we can attach a PostUpdateNode to.
+ * For instance, an update to a field of a struct containing only one field. For these cases we
+ * attach the PostUpdateNode to the store instruction. There's no obvious pre update node for this case
+ * (as the entire memory is updated), so `getPreUpdateNode` is implemented as `none()`.
+ */
+private class ExplicitSingleFieldStoreQualifierNode extends PartialDefinitionNode {
+ override StoreInstruction instr;
+ FieldAddressInstruction field;
+
+ ExplicitSingleFieldStoreQualifierNode() {
+ field = instr.getDestinationAddress() and
+ not exists(ChiInstruction chi | chi.getPartial() = instr)
+ }
+
+ override Node getPreUpdateNode() { none() }
+
+ override Expr getDefinedExpr() {
+ result = field.getObjectAddress().getUnconvertedResultExpression()
+ }
+}
+
/**
* A node that represents the value of a variable after a function call that
* may have changed the variable because it's passed by reference.
@@ -252,6 +387,18 @@ class DefinitionByReferenceNode extends InstructionNode {
}
}
+/**
+ * A node representing the memory pointed to by a function argument.
+ *
+ * This class exists only in order to override `toString`, which would
+ * otherwise be the default implementation inherited from `InstructionNode`.
+ */
+private class ArgumentIndirectionNode extends InstructionNode {
+ override ReadSideEffectInstruction instr;
+
+ override string toString() { result = "Argument " + instr.getIndex() + " indirection" }
+}
+
/**
* A `Node` corresponding to a variable in the program, as opposed to the
* value of that variable at some particular point. This can be used for
@@ -288,6 +435,10 @@ class VariableNode extends Node, TVariableNode {
*/
InstructionNode instructionNode(Instruction instr) { result.getInstruction() = instr }
+/**
+ * Gets the `Node` corresponding to a definition by reference of the variable
+ * that is passed as `argument` of a call.
+ */
DefinitionByReferenceNode definitionByReferenceNode(Expr e) { result.getArgument() = e }
/**
@@ -305,7 +456,7 @@ ExprNode convertedExprNode(Expr e) { result.getConvertedExpr() = e }
/**
* Gets the `Node` corresponding to the value of `p` at function entry.
*/
-ParameterNode parameterNode(Parameter p) { result.getParameter() = p }
+ExplicitParameterNode parameterNode(Parameter p) { result.getParameter() = p }
/** Gets the `VariableNode` corresponding to the variable `v`. */
VariableNode variableNode(Variable v) { result.getVariable() = v }
@@ -334,12 +485,46 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
simpleInstructionLocalFlowStep(nodeFrom.asInstruction(), nodeTo.asInstruction())
}
+pragma[noinline]
+private predicate getFieldSizeOfClass(Class c, Type type, int size) {
+ exists(Field f |
+ f.getDeclaringType() = c and
+ f.getType() = type and
+ type.getSize() = size
+ )
+}
+
cached
private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction iTo) {
iTo.(CopyInstruction).getSourceValue() = iFrom
or
iTo.(PhiInstruction).getAnOperand().getDef() = iFrom
or
+ // A read side effect is almost never exact since we don't know exactly how
+ // much memory the callee will read.
+ iTo.(ReadSideEffectInstruction).getSideEffectOperand().getAnyDef() = iFrom and
+ not iFrom.isResultConflated()
+ or
+ // Loading a single `int` from an `int *` parameter is not an exact load since
+ // the parameter may point to an entire array rather than a single `int`. The
+ // following rule ensures that any flow going into the
+ // `InitializeIndirectionInstruction`, even if it's for a different array
+ // element, will propagate to a load of the first element.
+ //
+ // Since we're linking `InitializeIndirectionInstruction` and
+ // `LoadInstruction` together directly, this rule will break if there's any
+ // reassignment of the parameter indirection, including a conditional one that
+ // leads to a phi node.
+ exists(InitializeIndirectionInstruction init |
+ iFrom = init and
+ iTo.(LoadInstruction).getSourceValueOperand().getAnyDef() = init and
+ // Check that the types match. Otherwise we can get flow from an object to
+ // its fields, which leads to field conflation when there's flow from other
+ // fields to the object elsewhere.
+ init.getParameter().getType().getUnspecifiedType().(DerivedType).getBaseType() =
+ iTo.getResultType().getUnspecifiedType()
+ )
+ or
// Treat all conversions as flow, even conversions between different numeric types.
iTo.(ConvertInstruction).getUnary() = iFrom
or
@@ -360,6 +545,32 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
// for now.
iTo.getAnOperand().(ChiTotalOperand).getDef() = iFrom
or
+ // Add flow from write side-effects to non-conflated chi instructions through their
+ // partial operands. From there, a `readStep` will find subsequent reads of that field.
+ // Consider the following example:
+ // ```
+ // void setX(Point* p, int new_x) {
+ // p->x = new_x;
+ // }
+ // ...
+ // setX(&p, taint());
+ // ```
+ // Here, a `WriteSideEffectInstruction` will provide a new definition for `p->x` after the call to
+ // `setX`, which will be melded into `p` through a chi instruction.
+ exists(ChiInstruction chi | chi = iTo |
+ chi.getPartialOperand().getDef() = iFrom.(WriteSideEffectInstruction) and
+ not chi.isResultConflated()
+ )
+ or
+ // Flow from stores to structs with a single field to a load of that field.
+ iTo.(LoadInstruction).getSourceValueOperand().getAnyDef() = iFrom and
+ exists(int size, Type type, Class cTo |
+ type = iFrom.getResultType() and
+ cTo = iTo.getResultType() and
+ cTo.getSize() = size and
+ getFieldSizeOfClass(cTo, type, size)
+ )
+ or
// Flow through modeled functions
modelFlow(iFrom, iTo)
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll
new file mode 100644
index 00000000000..d1cafb28f1c
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll
@@ -0,0 +1,47 @@
+/**
+ * Provides predicates for mapping the `FunctionInput` and `FunctionOutput`
+ * classes used in function models to the corresponding instructions.
+ */
+
+private import semmle.code.cpp.ir.IR
+private import semmle.code.cpp.ir.dataflow.DataFlow
+
+/**
+ * Gets the instruction that goes into `input` for `call`.
+ */
+Instruction callInput(CallInstruction call, FunctionInput input) {
+ // A positional argument
+ exists(int index |
+ result = call.getPositionalArgument(index) and
+ input.isParameter(index)
+ )
+ or
+ // A value pointed to by a positional argument
+ exists(ReadSideEffectInstruction read |
+ result = read and
+ read.getPrimaryInstruction() = call and
+ input.isParameterDeref(read.getIndex())
+ )
+ or
+ // The qualifier pointer
+ result = call.getThisArgument() and
+ input.isQualifierAddress()
+ //TODO: qualifier deref
+}
+
+/**
+ * Gets the instruction that holds the `output` for `call`.
+ */
+Instruction callOutput(CallInstruction call, FunctionOutput output) {
+ // The return value
+ result = call and
+ output.isReturnValue()
+ or
+ // The side effect of a call on the value pointed to by a positional argument
+ exists(WriteSideEffectInstruction effect |
+ result = effect and
+ effect.getPrimaryInstruction() = call and
+ output.isParameterDeref(effect.getIndex())
+ )
+ // TODO: qualifiers, return value dereference
+}
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 8d7c9194f4f..2290bab0571 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
@@ -1,5 +1,8 @@
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.DataFlow
+private import ModelUtil
+private import semmle.code.cpp.models.interfaces.DataFlow
+private import semmle.code.cpp.models.interfaces.SideEffect
/**
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
@@ -45,6 +48,25 @@ private predicate localInstructionTaintStep(Instruction nodeFrom, Instruction no
)
or
nodeTo.(LoadInstruction).getSourceAddress() = nodeFrom
+ or
+ modeledInstructionTaintStep(nodeFrom, nodeTo)
+ or
+ // Flow through partial reads of arrays and unions
+ nodeTo.(LoadInstruction).getSourceValueOperand().getAnyDef() = nodeFrom and
+ not nodeFrom.isResultConflated() and
+ (
+ nodeFrom.getResultType() instanceof ArrayType or
+ nodeFrom.getResultType() instanceof Union
+ )
+ or
+ // Flow from an element to an array or union that contains it.
+ nodeTo.(ChiInstruction).getPartial() = nodeFrom and
+ not nodeTo.isResultConflated() and
+ exists(Type t | nodeTo.getResultLanguageType().hasType(t, false) |
+ t instanceof Union
+ or
+ t instanceof ArrayType
+ )
}
/**
@@ -82,3 +104,34 @@ predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
* but not in local taint.
*/
predicate defaultTaintBarrier(DataFlow::Node node) { none() }
+
+/**
+ * Holds if taint can flow from `instrIn` to `instrOut` through a call to a
+ * modeled function.
+ */
+predicate modeledInstructionTaintStep(Instruction instrIn, Instruction instrOut) {
+ exists(CallInstruction call, TaintFunction func, FunctionInput modelIn, FunctionOutput modelOut |
+ instrIn = callInput(call, modelIn) and
+ instrOut = callOutput(call, modelOut) and
+ call.getStaticCallTarget() = func and
+ func.hasTaintFlow(modelIn, modelOut)
+ )
+ or
+ // Taint flow from one argument to another and data flow from an argument to a
+ // return value. This happens in functions like `strcat` and `memcpy`. We
+ // could model this flow in two separate steps, but that would add reverse
+ // flow from the write side-effect to the call instruction, which may not be
+ // desirable.
+ exists(
+ CallInstruction call, Function func, FunctionInput modelIn, OutParameterDeref modelMidOut,
+ int indexMid, InParameter modelMidIn, OutReturnValue modelOut
+ |
+ instrIn = callInput(call, modelIn) and
+ instrOut = callOutput(call, modelOut) and
+ call.getStaticCallTarget() = func and
+ func.(TaintFunction).hasTaintFlow(modelIn, modelMidOut) and
+ func.(DataFlowFunction).hasDataFlow(modelMidIn, modelOut) and
+ modelMidOut.isParameterDeref(indexMid) and
+ modelMidIn.isParameter(indexMid)
+ )
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
index e8b828f5b3e..0f0607662e9 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
@@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration {
defaultTaintBarrier(node)
}
- /** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */
- deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() }
-
- deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
- isSanitizerEdge(node1, node2)
- }
-
/** Holds if data flow into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
index e8b828f5b3e..0f0607662e9 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
@@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration {
defaultTaintBarrier(node)
}
- /** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */
- deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() }
-
- deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
- isSanitizerEdge(node1, node2)
- }
-
/** Holds if data flow into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll
index 650c15f189a..54059fb5b82 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll
@@ -17,6 +17,7 @@ private newtype TEdgeKind =
* `EdgeKind`.
*/
abstract class EdgeKind extends TEdgeKind {
+ /** Gets a textual representation of this edge kind. */
abstract string toString();
}
@@ -28,8 +29,6 @@ class GotoEdge extends EdgeKind, TGotoEdge {
final override string toString() { result = "Goto" }
}
-GotoEdge gotoEdge() { result = TGotoEdge() }
-
/**
* A "true" edge, representing the successor of a conditional branch when the
* condition is non-zero.
@@ -38,8 +37,6 @@ class TrueEdge extends EdgeKind, TTrueEdge {
final override string toString() { result = "True" }
}
-TrueEdge trueEdge() { result = TTrueEdge() }
-
/**
* A "false" edge, representing the successor of a conditional branch when the
* condition is zero.
@@ -48,8 +45,6 @@ class FalseEdge extends EdgeKind, TFalseEdge {
final override string toString() { result = "False" }
}
-FalseEdge falseEdge() { result = TFalseEdge() }
-
/**
* An "exception" edge, representing the successor of an instruction when that
* instruction's evaluation throws an exception.
@@ -58,8 +53,6 @@ class ExceptionEdge extends EdgeKind, TExceptionEdge {
final override string toString() { result = "Exception" }
}
-ExceptionEdge exceptionEdge() { result = TExceptionEdge() }
-
/**
* A "default" edge, representing the successor of a `Switch` instruction when
* none of the case values matches the condition value.
@@ -68,8 +61,6 @@ class DefaultEdge extends EdgeKind, TDefaultEdge {
final override string toString() { result = "Default" }
}
-DefaultEdge defaultEdge() { result = TDefaultEdge() }
-
/**
* A "case" edge, representing the successor of a `Switch` instruction when the
* the condition value matches a correponding `case` label.
@@ -91,4 +82,48 @@ class CaseEdge extends EdgeKind, TCaseEdge {
string getMaxValue() { result = maxValue }
}
-CaseEdge caseEdge(string minValue, string maxValue) { result = TCaseEdge(minValue, maxValue) }
+/**
+ * Predicates to access the single instance of each `EdgeKind` class.
+ */
+module EdgeKind {
+ /**
+ * Gets the single instance of the `GotoEdge` class.
+ */
+ GotoEdge gotoEdge() { result = TGotoEdge() }
+
+ /**
+ * Gets the single instance of the `TrueEdge` class.
+ */
+ TrueEdge trueEdge() { result = TTrueEdge() }
+
+ /**
+ * Gets the single instance of the `FalseEdge` class.
+ */
+ FalseEdge falseEdge() { result = TFalseEdge() }
+
+ /**
+ * Gets the single instance of the `ExceptionEdge` class.
+ */
+ ExceptionEdge exceptionEdge() { result = TExceptionEdge() }
+
+ /**
+ * Gets the single instance of the `DefaultEdge` class.
+ */
+ DefaultEdge defaultEdge() { result = TDefaultEdge() }
+
+ /**
+ * Gets the `CaseEdge` representing a `case` label with the specified lower and upper bounds.
+ * For example:
+ * ```
+ * switch (x) {
+ * case 1: // Edge kind is `caseEdge("1", "1")`
+ * return x;
+ * case 2...8: // Edge kind is `caseEdge("2", "8")`
+ * return x - 1;
+ * default: // Edge kind is `defaultEdge()`
+ * return 0;
+ * }
+ * ```
+ */
+ CaseEdge caseEdge(string minValue, string maxValue) { result = TCaseEdge(minValue, maxValue) }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll
index 833c929ecc5..dec78b413b3 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll
@@ -12,7 +12,9 @@ private newtype TIRType =
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
+ TIRFloatingPointType(int byteSize, int base, Language::TypeDomain domain) {
+ Language::hasFloatingPointType(byteSize, base, domain)
+ } or
TIRAddressType(int byteSize) { Language::hasAddressType(byteSize) } or
TIRFunctionAddressType(int byteSize) { Language::hasFunctionAddressType(byteSize) } or
TIROpaqueType(Language::OpaqueTypeTag tag, int byteSize) {
@@ -104,11 +106,13 @@ private class IRSizedType extends IRType {
this = TIRBooleanType(byteSize) or
this = TIRSignedIntegerType(byteSize) or
this = TIRUnsignedIntegerType(byteSize) or
- this = TIRFloatingPointType(byteSize) or
+ this = TIRFloatingPointType(byteSize, _, _) or
this = TIRAddressType(byteSize) or
this = TIRFunctionAddressType(byteSize) or
this = TIROpaqueType(_, byteSize)
}
+ // Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
+ // overridden only in the leaf classes.
}
/**
@@ -126,22 +130,36 @@ class IRBooleanType extends IRSizedType, TIRBooleanType {
}
/**
- * A numberic type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and
+ * A numeric type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and
* `IRFloatingPointType`.
*/
class IRNumericType extends IRSizedType {
IRNumericType() {
this = TIRSignedIntegerType(byteSize) or
this = TIRUnsignedIntegerType(byteSize) or
- this = TIRFloatingPointType(byteSize)
+ this = TIRFloatingPointType(byteSize, _, _)
}
+ // Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
+ // overridden only in the leaf classes.
+}
+
+/**
+ * An integer type. This includes `IRSignedIntegerType` and `IRUnsignedIntegerType`.
+ */
+class IRIntegerType extends IRNumericType {
+ IRIntegerType() {
+ this = TIRSignedIntegerType(byteSize) or
+ this = TIRUnsignedIntegerType(byteSize)
+ }
+ // Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
+ // overridden only in the leaf classes.
}
/**
* A signed two's-complement integer. Also used to represent enums whose underlying type is a signed
* integer, as well as character types whose representation is signed.
*/
-class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType {
+class IRSignedIntegerType extends IRIntegerType, TIRSignedIntegerType {
final override string toString() { result = "int" + byteSize.toString() }
final override Language::LanguageType getCanonicalLanguageType() {
@@ -156,7 +174,7 @@ class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType {
* An unsigned two's-complement integer. Also used to represent enums whose underlying type is an
* unsigned integer, as well as character types whose representation is unsigned.
*/
-class IRUnsignedIntegerType extends IRNumericType, TIRUnsignedIntegerType {
+class IRUnsignedIntegerType extends IRIntegerType, TIRUnsignedIntegerType {
final override string toString() { result = "uint" + byteSize.toString() }
final override Language::LanguageType getCanonicalLanguageType() {
@@ -171,14 +189,43 @@ class IRUnsignedIntegerType extends IRNumericType, TIRUnsignedIntegerType {
* A floating-point type.
*/
class IRFloatingPointType extends IRNumericType, TIRFloatingPointType {
- final override string toString() { result = "float" + byteSize.toString() }
+ final private int base;
+ final private Language::TypeDomain domain;
+
+ IRFloatingPointType() { this = TIRFloatingPointType(_, base, domain) }
+
+ final override string toString() {
+ result = getDomainPrefix() + getBaseString() + byteSize.toString()
+ }
final override Language::LanguageType getCanonicalLanguageType() {
- result = Language::getCanonicalFloatingPointType(byteSize)
+ result = Language::getCanonicalFloatingPointType(byteSize, base, domain)
}
pragma[noinline]
final override int getByteSize() { result = byteSize }
+
+ /** Gets the numeric base of the type. Can be either 2 (binary) or 10 (decimal). */
+ final int getBase() { result = base }
+
+ /**
+ * Gets the type domain of the type. Can be `RealDomain`, `ComplexDomain`, or `ImaginaryDomain`.
+ */
+ final Language::TypeDomain getDomain() { result = domain }
+
+ private string getBaseString() {
+ base = 2 and result = "float"
+ or
+ base = 10 and result = "decimal"
+ }
+
+ private string getDomainPrefix() {
+ domain instanceof Language::RealDomain and result = ""
+ or
+ domain instanceof Language::ComplexDomain and result = "c"
+ or
+ domain instanceof Language::ImaginaryDomain and result = "i"
+ }
}
/**
@@ -244,12 +291,24 @@ class IROpaqueType extends IRSizedType, TIROpaqueType {
final override int getByteSize() { result = byteSize }
}
-module IRTypeSanity {
+/**
+ * INTERNAL: Do not use.
+ * Query predicates used to check invariants that should hold for all `IRType` objects. To run all
+ * consistency queries for the IR, including the ones below, run
+ * "semmle/code/cpp/IR/IRConsistency.ql".
+ */
+module IRTypeConsistency {
+ /**
+ * Holds if the type has no result for `IRType.getCanonicalLanguageType()`.
+ */
query predicate missingCanonicalLanguageType(IRType type, string message) {
not exists(type.getCanonicalLanguageType()) and
message = "Type does not have a canonical `LanguageType`"
}
+ /**
+ * Holds if the type has more than one result for `IRType.getCanonicalLanguageType()`.
+ */
query predicate multipleCanonicalLanguageTypes(IRType type, string message) {
strictcount(type.getCanonicalLanguageType()) > 1 and
message =
@@ -257,11 +316,17 @@ module IRTypeSanity {
concat(type.getCanonicalLanguageType().toString(), ", ")
}
+ /**
+ * Holds if the type has no result for `LanguageType.getIRType()`.
+ */
query predicate missingIRType(Language::LanguageType type, string message) {
not exists(type.getIRType()) and
message = "`LanguageType` does not have a corresponding `IRType`."
}
+ /**
+ * Holds if the type has more than one result for `LanguageType.getIRType()`.
+ */
query predicate multipleIRTypes(Language::LanguageType type, string message) {
strictcount(type.getIRType()) > 1 and
message =
@@ -269,5 +334,5 @@ module IRTypeSanity {
concat(type.getIRType().toString(), ", ")
}
- import Language::LanguageTypeSanity
+ import Language::LanguageTypeConsistency
}
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 eac4d333afc..6852a965401 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll
@@ -92,11 +92,3 @@ class ChiTotalMemoryAccess extends MemoryAccessKind, TChiTotalMemoryAccess {
class ChiPartialMemoryAccess extends MemoryAccessKind, TChiPartialMemoryAccess {
override string toString() { result = "chi(partial)" }
}
-
-/**
- * The operand accesses memory not modeled in SSA. Used only on the result of
- * `UnmodeledDefinition` and on the operands of `UnmodeledUse`.
- */
-class UnmodeledMemoryAccess extends MemoryAccessKind, TUnmodeledMemoryAccess {
- override string toString() { result = "unmodeled" }
-}
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 e7a0b6f1b4f..c0b8adbe56b 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll
@@ -60,8 +60,6 @@ private newtype TOpcode =
TThrowValue() or
TReThrow() or
TUnwind() or
- TUnmodeledDefinition() or
- TUnmodeledUse() or
TAliasedDefinition() or
TInitializeNonLocal() or
TAliasedUse() or
@@ -579,22 +577,6 @@ module Opcode {
final override string toString() { result = "Unwind" }
}
- class UnmodeledDefinition extends Opcode, TUnmodeledDefinition {
- final override string toString() { result = "UnmodeledDefinition" }
-
- final override MemoryAccessKind getWriteMemoryAccess() {
- result instanceof UnmodeledMemoryAccess
- }
- }
-
- class UnmodeledUse extends Opcode, TUnmodeledUse {
- final override string toString() { result = "UnmodeledUse" }
-
- final override predicate hasOperandInternal(OperandTag tag) {
- tag instanceof UnmodeledUseOperandTag
- }
- }
-
class AliasedDefinition extends Opcode, TAliasedDefinition {
final override string toString() { result = "AliasedDefinition" }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll
index ec6de78cfa4..a0c0ca67530 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll
@@ -1,6 +1,16 @@
+/**
+ * Defines the public interface to temporary variable tags, which describe the reason a particular
+ * `IRTempVariable` was generated.
+ */
+
private import internal.TempVariableTagInternal
private import Imports::TempVariableTag
+/**
+ * A reason that a particular IR temporary variable was generated. For example, it could be
+ * generated to hold the return value of a function, or to hold the result of a `?:` operator
+ * computed on each branch. The set of possible `TempVariableTag`s is language-dependent.
+ */
class TempVariableTag extends TTempVariableTag {
string toString() { result = getTempVariableTagId(this) }
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll
index 49cb4dd6dc4..94ef73b2769 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll
@@ -31,10 +31,14 @@ class IRBlockBase extends TIRBlock {
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
) and
this =
- rank[result + 1](IRBlock funcBlock |
- funcBlock.getEnclosingFunction() = getEnclosingFunction()
+ rank[result + 1](IRBlock funcBlock, int sortOverride |
+ funcBlock.getEnclosingFunction() = getEnclosingFunction() and
+ // Ensure that the block containing `EnterFunction` always comes first.
+ if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction
+ then sortOverride = 0
+ else sortOverride = 1
|
- funcBlock order by funcBlock.getUniqueId()
+ funcBlock order by sortOverride, funcBlock.getUniqueId()
)
}
@@ -101,23 +105,24 @@ class IRBlock extends IRBlockBase {
private predicate startsBasicBlock(Instruction instr) {
not instr instanceof PhiInstruction and
- (
- count(Instruction predecessor | instr = predecessor.getASuccessor()) != 1 // Multiple predecessors or no predecessor
- or
- exists(Instruction predecessor |
- instr = predecessor.getASuccessor() and
- strictcount(Instruction other | other = predecessor.getASuccessor()) > 1
- ) // Predecessor has multiple successors
- or
- exists(Instruction predecessor, EdgeKind kind |
- instr = predecessor.getSuccessor(kind) and
- not kind instanceof GotoEdge
- ) // Incoming edge is not a GotoEdge
- or
- exists(Instruction predecessor |
- instr = Construction::getInstructionBackEdgeSuccessor(predecessor, _)
- ) // A back edge enters this instruction
- )
+ not adjacentInBlock(_, instr)
+}
+
+/** Holds if `i2` follows `i1` in a `IRBlock`. */
+private predicate adjacentInBlock(Instruction i1, Instruction i2) {
+ // - i2 must be the only successor of i1
+ i2 = unique(Instruction i | i = i1.getASuccessor()) and
+ // - i1 must be the only predecessor of i2
+ i1 = unique(Instruction i | i.getASuccessor() = i2) and
+ // - The edge between the two must be a GotoEdge. We just check that one
+ // exists since we've already checked that it's unique.
+ exists(GotoEdge edgeKind | exists(i1.getSuccessor(edgeKind))) and
+ // - The edge must not be a back edge. This means we get the same back edges
+ // in the basic-block graph as we do in the raw CFG.
+ not exists(Construction::getInstructionBackEdgeSuccessor(i1, _))
+ // This predicate could be simplified to remove one of the `unique`s if we
+ // were willing to rely on the CFG being well-formed and thus never having
+ // more than one successor to an instruction that has a `GotoEdge` out of it.
}
private predicate isEntryBlock(TIRBlock block) {
@@ -129,12 +134,6 @@ private module Cached {
cached
newtype TIRBlock = MkIRBlock(Instruction firstInstr) { startsBasicBlock(firstInstr) }
- /** Holds if `i2` follows `i1` in a `IRBlock`. */
- private predicate adjacentInBlock(Instruction i1, Instruction i2) {
- exists(GotoEdge edgeKind | i2 = i1.getSuccessor(edgeKind)) and
- not startsBasicBlock(i2)
- }
-
/** Holds if `i` is the `index`th instruction the block starting with `first`. */
private Instruction getInstructionFromFirst(Instruction first, int index) =
shortestDistances(startsBasicBlock/1, adjacentInBlock/2)(first, result, index)
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.ql b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.ql
new file mode 100644
index 00000000000..0b49f422bab
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.ql
@@ -0,0 +1,8 @@
+/**
+ * @name Aliased SSA IR Consistency Check
+ * @description Performs consistency checks on the Intermediate Representation. This query should have no results.
+ * @kind table
+ * @id cpp/aliased-ssa-ir-consistency-check
+ */
+
+import IRConsistency
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll
new file mode 100644
index 00000000000..6a87b9b4b5f
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll
@@ -0,0 +1,497 @@
+private import IR
+import InstructionConsistency // module is below
+import IRTypeConsistency // module is in IRType.qll
+
+module InstructionConsistency {
+ private import internal.InstructionImports as Imports
+ private import Imports::OperandTag
+ private import Imports::Overlap
+ private import internal.IRInternal
+
+ private newtype TOptionalIRFunction =
+ TPresentIRFunction(IRFunction irFunc) or
+ TMissingIRFunction()
+
+ /**
+ * An `IRFunction` that might not exist. This is used so that we can produce consistency failures
+ * for IR that also incorrectly lacks a `getEnclosingIRFunction()`.
+ */
+ abstract private class OptionalIRFunction extends TOptionalIRFunction {
+ abstract string toString();
+
+ abstract Language::Location getLocation();
+ }
+
+ private class PresentIRFunction extends OptionalIRFunction, TPresentIRFunction {
+ private IRFunction irFunc;
+
+ PresentIRFunction() { this = TPresentIRFunction(irFunc) }
+
+ override string toString() {
+ result = concat(Language::getIdentityString(irFunc.getFunction()), "; ")
+ }
+
+ override Language::Location getLocation() {
+ // To avoid an overwhelming number of results when the extractor merges functions with the
+ // same name, just pick a single location.
+ result =
+ rank[1](Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString())
+ }
+ }
+
+ private class MissingIRFunction extends OptionalIRFunction, TMissingIRFunction {
+ override string toString() { result = "" }
+
+ override Language::Location getLocation() { result instanceof Language::UnknownDefaultLocation }
+ }
+
+ private OptionalIRFunction getInstructionIRFunction(Instruction instr) {
+ result = TPresentIRFunction(instr.getEnclosingIRFunction())
+ or
+ not exists(instr.getEnclosingIRFunction()) and result = TMissingIRFunction()
+ }
+
+ pragma[inline]
+ private OptionalIRFunction getInstructionIRFunction(Instruction instr, string irFuncText) {
+ result = getInstructionIRFunction(instr) and
+ irFuncText = result.toString()
+ }
+
+ private OptionalIRFunction getOperandIRFunction(Operand operand) {
+ result = TPresentIRFunction(operand.getEnclosingIRFunction())
+ or
+ not exists(operand.getEnclosingIRFunction()) and result = TMissingIRFunction()
+ }
+
+ pragma[inline]
+ private OptionalIRFunction getOperandIRFunction(Operand operand, string irFuncText) {
+ result = getOperandIRFunction(operand) and
+ irFuncText = result.toString()
+ }
+
+ private OptionalIRFunction getBlockIRFunction(IRBlock block) {
+ result = TPresentIRFunction(block.getEnclosingIRFunction())
+ or
+ not exists(block.getEnclosingIRFunction()) and result = TMissingIRFunction()
+ }
+
+ /**
+ * Holds if instruction `instr` is missing an expected operand with tag `tag`.
+ */
+ query predicate missingOperand(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(OperandTag tag |
+ instr.getOpcode().hasOperand(tag) and
+ not exists(NonPhiOperand operand |
+ operand = instr.getAnOperand() and
+ operand.getOperandTag() = tag
+ ) and
+ message =
+ "Instruction '" + instr.getOpcode().toString() +
+ "' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ /**
+ * Holds if instruction `instr` has an unexpected operand with tag `tag`.
+ */
+ query predicate unexpectedOperand(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(OperandTag tag |
+ exists(NonPhiOperand operand |
+ operand = instr.getAnOperand() and
+ operand.getOperandTag() = tag
+ ) and
+ not instr.getOpcode().hasOperand(tag) and
+ not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
+ not (
+ instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
+ ) and
+ not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) and
+ message =
+ "Instruction '" + instr.toString() + "' has unexpected operand '" + tag.toString() +
+ "' in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ /**
+ * Holds if instruction `instr` has multiple operands with tag `tag`.
+ */
+ query predicate duplicateOperand(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(OperandTag tag, int operandCount |
+ operandCount =
+ strictcount(NonPhiOperand operand |
+ operand = instr.getAnOperand() and
+ operand.getOperandTag() = tag
+ ) and
+ operandCount > 1 and
+ message =
+ "Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
+ " in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ /**
+ * Holds if `Phi` instruction `instr` is missing an operand corresponding to
+ * the predecessor block `pred`.
+ */
+ query predicate missingPhiOperand(
+ PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(IRBlock pred |
+ pred = instr.getBlock().getAPredecessor() and
+ not exists(PhiInputOperand operand |
+ operand = instr.getAnOperand() and
+ operand.getPredecessorBlock() = pred
+ ) and
+ message =
+ "Instruction '" + instr.toString() + "' is missing an operand for predecessor block '" +
+ pred.toString() + "' in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ query predicate missingOperandType(
+ Operand operand, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(Instruction use |
+ not exists(operand.getType()) and
+ use = operand.getUse() and
+ message =
+ "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
+ "' is missing a type in function '$@'." and
+ irFunc = getOperandIRFunction(operand, irFuncText)
+ )
+ }
+
+ query predicate duplicateChiOperand(
+ ChiInstruction chi, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ chi.getTotal() = chi.getPartial() and
+ message =
+ "Chi instruction for " + chi.getPartial().toString() +
+ " has duplicate operands in function '$@'." and
+ irFunc = getInstructionIRFunction(chi, irFuncText)
+ }
+
+ query predicate sideEffectWithoutPrimary(
+ SideEffectInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ not exists(instr.getPrimaryInstruction()) and
+ message =
+ "Side effect instruction '" + instr + "' is missing a primary instruction in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ }
+
+ /**
+ * Holds if an instruction, other than `ExitFunction`, has no successors.
+ */
+ query predicate instructionWithoutSuccessor(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ not exists(instr.getASuccessor()) and
+ not instr instanceof ExitFunctionInstruction and
+ // Phi instructions aren't linked into the instruction-level flow graph.
+ not instr instanceof PhiInstruction and
+ not instr instanceof UnreachedInstruction and
+ message = "Instruction '" + instr.toString() + "' has no successors in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ }
+
+ /**
+ * Holds if there are multiple edges of the same kind from `source`.
+ */
+ query predicate ambiguousSuccessors(
+ Instruction source, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(EdgeKind kind, int n |
+ n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
+ n > 1 and
+ message =
+ "Instruction '" + source.toString() + "' has " + n.toString() + " successors of kind '" +
+ kind.toString() + "' in function '$@'." and
+ irFunc = getInstructionIRFunction(source, irFuncText)
+ )
+ }
+
+ /**
+ * Holds if `instr` is part of a loop even though the AST of `instr`'s enclosing function
+ * contains no element that can cause loops.
+ */
+ query predicate unexplainedLoop(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(Language::Function f |
+ exists(IRBlock block |
+ instr.getBlock() = block and
+ block.getEnclosingFunction() = f and
+ block.getASuccessor+() = block
+ ) and
+ not Language::hasPotentialLoop(f) and
+ message =
+ "Instruction '" + instr.toString() + "' is part of an unexplained loop in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ /**
+ * Holds if a `Phi` instruction is present in a block with fewer than two
+ * predecessors.
+ */
+ query predicate unnecessaryPhiInstruction(
+ PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(int n |
+ n = count(instr.getBlock().getAPredecessor()) and
+ n < 2 and
+ message =
+ "Instruction '" + instr.toString() + "' is in a block with only " + n.toString() +
+ " predecessors in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ /**
+ * Holds if a memory operand is connected to a definition with an unmodeled result.
+ */
+ query predicate memoryOperandDefinitionIsUnmodeled(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(MemoryOperand operand, Instruction def |
+ operand = instr.getAnOperand() and
+ def = operand.getAnyDef() and
+ not def.isResultModeled() and
+ message =
+ "Memory operand definition on instruction '" + instr.toString() +
+ "' has unmodeled result in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ /**
+ * Holds if operand `operand` consumes a value that was defined in
+ * a different function.
+ */
+ query predicate operandAcrossFunctions(
+ Operand operand, string message, OptionalIRFunction useIRFunc, string useIRFuncText,
+ OptionalIRFunction defIRFunc, string defIRFuncText
+ ) {
+ exists(Instruction useInstr, Instruction defInstr |
+ operand.getUse() = useInstr and
+ operand.getAnyDef() = defInstr and
+ useIRFunc = getInstructionIRFunction(useInstr, useIRFuncText) and
+ defIRFunc = getInstructionIRFunction(defInstr, defIRFuncText) and
+ useIRFunc != defIRFunc and
+ message =
+ "Operand '" + operand.toString() + "' is used on instruction '" + useInstr.toString() +
+ "' in function '$@', but is defined on instruction '" + defInstr.toString() +
+ "' in function '$@'."
+ )
+ }
+
+ /**
+ * Holds if instruction `instr` is not in exactly one block.
+ */
+ query predicate instructionWithoutUniqueBlock(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(int blockCount |
+ blockCount = count(instr.getBlock()) and
+ blockCount != 1 and
+ message =
+ "Instruction '" + instr.toString() + "' is a member of " + blockCount.toString() +
+ " blocks in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ private predicate forwardEdge(IRBlock b1, IRBlock b2) {
+ b1.getASuccessor() = b2 and
+ not b1.getBackEdgeSuccessor(_) = b2
+ }
+
+ /**
+ * Holds if `f` contains a loop in which no edge is a back edge.
+ *
+ * This check ensures we don't have too _few_ back edges.
+ */
+ query predicate containsLoopOfForwardEdges(IRFunction f, string message) {
+ exists(IRBlock block |
+ forwardEdge+(block, block) and
+ block.getEnclosingIRFunction() = f and
+ message = "Function contains a loop consisting of only forward edges."
+ )
+ }
+
+ /**
+ * Holds if `block` is reachable from its function entry point but would not
+ * be reachable by traversing only forward edges. This check is skipped for
+ * functions containing `goto` statements as the property does not generally
+ * hold there.
+ *
+ * This check ensures we don't have too _many_ back edges.
+ */
+ query predicate lostReachability(
+ IRBlock block, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(IRFunction f, IRBlock entry |
+ entry = f.getEntryBlock() and
+ entry.getASuccessor+() = block and
+ not forwardEdge+(entry, block) and
+ not Language::hasGoto(f.getFunction()) and
+ message =
+ "Block '" + block.toString() +
+ "' is not reachable by traversing only forward edges in function '$@'." and
+ irFunc = TPresentIRFunction(f) and
+ irFuncText = irFunc.toString()
+ )
+ }
+
+ /**
+ * Holds if the number of back edges differs between the `Instruction` graph
+ * and the `IRBlock` graph.
+ */
+ query predicate backEdgeCountMismatch(OptionalIRFunction irFunc, string message) {
+ exists(int fromInstr, int fromBlock |
+ fromInstr =
+ count(Instruction i1, Instruction i2 |
+ getInstructionIRFunction(i1) = irFunc and i1.getBackEdgeSuccessor(_) = i2
+ ) and
+ fromBlock =
+ count(IRBlock b1, IRBlock b2 |
+ getBlockIRFunction(b1) = irFunc and b1.getBackEdgeSuccessor(_) = b2
+ ) and
+ fromInstr != fromBlock and
+ message =
+ "The instruction graph for function '" + irFunc.toString() + "' contains " +
+ fromInstr.toString() + " back edges, but the block graph contains " + fromBlock.toString()
+ + " back edges."
+ )
+ }
+
+ /**
+ * Gets the point in the function at which the specified operand is evaluated. For most operands,
+ * this is at the instruction that consumes the use. For a `PhiInputOperand`, the effective point
+ * of evaluation is at the end of the corresponding predecessor block.
+ */
+ private predicate pointOfEvaluation(Operand operand, IRBlock block, int index) {
+ block = operand.(PhiInputOperand).getPredecessorBlock() and
+ index = block.getInstructionCount()
+ or
+ exists(Instruction use |
+ use = operand.(NonPhiOperand).getUse() and
+ block.getInstruction(index) = use
+ )
+ }
+
+ /**
+ * Holds if `useOperand` has a definition that does not dominate the use.
+ */
+ query predicate useNotDominatedByDefinition(
+ Operand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
+ pointOfEvaluation(useOperand, useBlock, useIndex) and
+ defInstr = useOperand.getAnyDef() and
+ (
+ defInstr instanceof PhiInstruction and
+ defBlock = defInstr.getBlock() and
+ defIndex = -1
+ or
+ defBlock.getInstruction(defIndex) = defInstr
+ ) and
+ not (
+ defBlock.strictlyDominates(useBlock)
+ or
+ defBlock = useBlock and
+ defIndex < useIndex
+ ) and
+ message =
+ "Operand '" + useOperand.toString() +
+ "' is not dominated by its definition in function '$@'." and
+ irFunc = getOperandIRFunction(useOperand, irFuncText)
+ )
+ }
+
+ query predicate switchInstructionWithoutDefaultEdge(
+ SwitchInstruction switchInstr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ not exists(switchInstr.getDefaultSuccessor()) and
+ message =
+ "SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and
+ irFunc = getInstructionIRFunction(switchInstr, irFuncText)
+ }
+
+ /**
+ * Holds if `instr` is on the chain of chi/phi instructions for all aliased
+ * memory.
+ */
+ private predicate isOnAliasedDefinitionChain(Instruction instr) {
+ instr instanceof AliasedDefinitionInstruction
+ or
+ isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal())
+ or
+ isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
+ }
+
+ private predicate shouldBeConflated(Instruction instr) {
+ isOnAliasedDefinitionChain(instr)
+ or
+ instr.getOpcode() instanceof Opcode::InitializeNonLocal
+ }
+
+ query predicate notMarkedAsConflated(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ shouldBeConflated(instr) and
+ not instr.isResultConflated() and
+ message =
+ "Instruction '" + instr.toString() +
+ "' should be marked as having a conflated result in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ }
+
+ query predicate wronglyMarkedAsConflated(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ instr.isResultConflated() and
+ not shouldBeConflated(instr) and
+ message =
+ "Instruction '" + instr.toString() +
+ "' should not be marked as having a conflated result in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ }
+
+ query predicate invalidOverlap(
+ MemoryOperand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(Overlap overlap |
+ overlap = useOperand.getDefinitionOverlap() and
+ overlap instanceof MayPartiallyOverlap and
+ message =
+ "MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
+ overlap.toString() + "'." and
+ irFunc = getOperandIRFunction(useOperand, irFuncText)
+ )
+ }
+
+ query predicate nonUniqueEnclosingIRFunction(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(int irFuncCount |
+ irFuncCount = count(instr.getEnclosingIRFunction()) and
+ irFuncCount != 1 and
+ message =
+ "Instruction '" + instr.toString() + "' has " + irFuncCount.toString() +
+ " results for `getEnclosingIRFunction()` in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll
index 1e9c2d1d913..6b2d32af48c 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll
@@ -1,29 +1,12 @@
private import internal.IRInternal
+private import internal.IRFunctionImports as Imports
+import Imports::IRFunctionBase
import Instruction
-private newtype TIRFunction =
- MkIRFunction(Language::Function func) { Construction::functionHasIR(func) }
-
/**
- * Represents the IR for a function.
+ * The IR for a function.
*/
-class IRFunction extends TIRFunction {
- Language::Function func;
-
- IRFunction() { this = MkIRFunction(func) }
-
- final string toString() { result = "IR: " + func.toString() }
-
- /**
- * Gets the function whose IR is represented.
- */
- final Language::Function getFunction() { result = func }
-
- /**
- * Gets the location of the function.
- */
- final Language::Location getLocation() { result = func.getLocation() }
-
+class IRFunction extends IRFunctionBase {
/**
* Gets the entry point for this function.
*/
@@ -40,16 +23,6 @@ class IRFunction extends TIRFunction {
result.getEnclosingIRFunction() = this
}
- pragma[noinline]
- final UnmodeledDefinitionInstruction getUnmodeledDefinitionInstruction() {
- result.getEnclosingIRFunction() = this
- }
-
- pragma[noinline]
- final UnmodeledUseInstruction getUnmodeledUseInstruction() {
- result.getEnclosingIRFunction() = this
- }
-
/**
* Gets the single return instruction for this function.
*/
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.ql b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.ql
deleted file mode 100644
index b5d3ae4633e..00000000000
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.ql
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * @name Aliased SSA IR Sanity Check
- * @description Performs sanity checks on the Intermediate Representation. This query should have no results.
- * @kind table
- * @id cpp/aliased-ssa-ir-sanity-check
- */
-
-import IRSanity
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
deleted file mode 100644
index 94d0192fe18..00000000000
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll
+++ /dev/null
@@ -1,320 +0,0 @@
-private import IR
-import InstructionSanity // module is below
-import IRTypeSanity // module is in IRType.qll
-
-module InstructionSanity {
- private import internal.InstructionImports as Imports
- private import Imports::OperandTag
- private import Imports::Overlap
- private import internal.IRInternal
-
- /**
- * Holds if instruction `instr` is missing an expected operand with tag `tag`.
- */
- query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) {
- exists(OperandTag tag |
- instr.getOpcode().hasOperand(tag) and
- not exists(NonPhiOperand operand |
- operand = instr.getAnOperand() and
- operand.getOperandTag() = tag
- ) and
- message =
- "Instruction '" + instr.getOpcode().toString() +
- "' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
- func = instr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
- )
- }
-
- /**
- * Holds if instruction `instr` has an unexpected operand with tag `tag`.
- */
- query predicate unexpectedOperand(Instruction instr, OperandTag tag) {
- exists(NonPhiOperand operand |
- operand = instr.getAnOperand() and
- operand.getOperandTag() = tag
- ) and
- not instr.getOpcode().hasOperand(tag) and
- not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
- not (
- instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
- ) and
- not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag)
- }
-
- /**
- * Holds if instruction `instr` has multiple operands with tag `tag`.
- */
- query predicate duplicateOperand(
- Instruction instr, string message, IRFunction func, string funcText
- ) {
- exists(OperandTag tag, int operandCount |
- operandCount =
- strictcount(NonPhiOperand operand |
- operand = instr.getAnOperand() and
- operand.getOperandTag() = tag
- ) and
- operandCount > 1 and
- not tag instanceof UnmodeledUseOperandTag and
- message =
- "Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
- " in function '$@'." and
- func = instr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
- )
- }
-
- /**
- * Holds if `Phi` instruction `instr` is missing an operand corresponding to
- * the predecessor block `pred`.
- */
- query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
- pred = instr.getBlock().getAPredecessor() and
- not exists(PhiInputOperand operand |
- operand = instr.getAnOperand() and
- operand.getPredecessorBlock() = pred
- )
- }
-
- query predicate missingOperandType(Operand operand, string message) {
- exists(Language::Function func, Instruction use |
- not exists(operand.getType()) and
- use = operand.getUse() and
- func = use.getEnclosingFunction() and
- message =
- "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
- "' missing type in function '" + Language::getIdentityString(func) + "'."
- )
- }
-
- 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.
- */
- query predicate instructionWithoutSuccessor(Instruction instr) {
- not exists(instr.getASuccessor()) and
- not instr instanceof ExitFunctionInstruction and
- // Phi instructions aren't linked into the instruction-level flow graph.
- not instr instanceof PhiInstruction and
- not instr instanceof UnreachedInstruction
- }
-
- /**
- * Holds if there are multiple (`n`) edges of kind `kind` from `source`,
- * where `target` is among the targets of those edges.
- */
- query predicate ambiguousSuccessors(Instruction source, EdgeKind kind, int n, Instruction target) {
- n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
- n > 1 and
- source.getSuccessor(kind) = target
- }
-
- /**
- * Holds if `instr` in `f` is part of a loop even though the AST of `f`
- * contains no element that can cause loops.
- */
- query predicate unexplainedLoop(Language::Function f, Instruction instr) {
- exists(IRBlock block |
- instr.getBlock() = block and
- block.getEnclosingFunction() = f and
- block.getASuccessor+() = block
- ) and
- not Language::hasPotentialLoop(f)
- }
-
- /**
- * Holds if a `Phi` instruction is present in a block with fewer than two
- * predecessors.
- */
- query predicate unnecessaryPhiInstruction(PhiInstruction instr) {
- count(instr.getBlock().getAPredecessor()) < 2
- }
-
- /**
- * Holds if operand `operand` consumes a value that was defined in
- * a different function.
- */
- query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) {
- operand.getUse() = instr and
- operand.getAnyDef() = defInstr and
- instr.getEnclosingIRFunction() != defInstr.getEnclosingIRFunction()
- }
-
- /**
- * Holds if instruction `instr` is not in exactly one block.
- */
- query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) {
- blockCount = count(instr.getBlock()) and
- blockCount != 1
- }
-
- private predicate forwardEdge(IRBlock b1, IRBlock b2) {
- b1.getASuccessor() = b2 and
- not b1.getBackEdgeSuccessor(_) = b2
- }
-
- /**
- * Holds if `f` contains a loop in which no edge is a back edge.
- *
- * This check ensures we don't have too _few_ back edges.
- */
- query predicate containsLoopOfForwardEdges(IRFunction f) {
- exists(IRBlock block |
- forwardEdge+(block, block) and
- block.getEnclosingIRFunction() = f
- )
- }
-
- /**
- * Holds if `block` is reachable from its function entry point but would not
- * be reachable by traversing only forward edges. This check is skipped for
- * functions containing `goto` statements as the property does not generally
- * hold there.
- *
- * This check ensures we don't have too _many_ back edges.
- */
- query predicate lostReachability(IRBlock block) {
- exists(IRFunction f, IRBlock entry |
- entry = f.getEntryBlock() and
- entry.getASuccessor+() = block and
- not forwardEdge+(entry, block) and
- not Language::hasGoto(f.getFunction())
- )
- }
-
- /**
- * Holds if the number of back edges differs between the `Instruction` graph
- * and the `IRBlock` graph.
- */
- query predicate backEdgeCountMismatch(Language::Function f, int fromInstr, int fromBlock) {
- fromInstr =
- count(Instruction i1, Instruction i2 |
- i1.getEnclosingFunction() = f and i1.getBackEdgeSuccessor(_) = i2
- ) and
- fromBlock =
- count(IRBlock b1, IRBlock b2 |
- b1.getEnclosingFunction() = f and b1.getBackEdgeSuccessor(_) = b2
- ) and
- fromInstr != fromBlock
- }
-
- /**
- * Gets the point in the function at which the specified operand is evaluated. For most operands,
- * this is at the instruction that consumes the use. For a `PhiInputOperand`, the effective point
- * of evaluation is at the end of the corresponding predecessor block.
- */
- private predicate pointOfEvaluation(Operand operand, IRBlock block, int index) {
- block = operand.(PhiInputOperand).getPredecessorBlock() and
- index = block.getInstructionCount()
- or
- exists(Instruction use |
- use = operand.(NonPhiOperand).getUse() and
- block.getInstruction(index) = use
- )
- }
-
- /**
- * Holds if `useOperand` has a definition that does not dominate the use.
- */
- query predicate useNotDominatedByDefinition(
- Operand useOperand, string message, IRFunction func, string funcText
- ) {
- 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
- (
- defInstr instanceof PhiInstruction and
- defBlock = defInstr.getBlock() and
- defIndex = -1
- or
- defBlock.getInstruction(defIndex) = defInstr
- ) and
- not (
- defBlock.strictlyDominates(useBlock)
- or
- defBlock = useBlock and
- defIndex < useIndex
- ) and
- message =
- "Operand '" + useOperand.toString() +
- "' is not dominated by its definition in function '$@'." and
- func = useOperand.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
- )
- }
-
- query predicate switchInstructionWithoutDefaultEdge(
- SwitchInstruction switchInstr, string message, IRFunction func, string funcText
- ) {
- not exists(switchInstr.getDefaultSuccessor()) and
- message =
- "SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and
- func = switchInstr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
- }
-
- /**
- * Holds if `instr` is on the chain of chi/phi instructions for all aliased
- * memory.
- */
- private predicate isOnAliasedDefinitionChain(Instruction instr) {
- instr instanceof AliasedDefinitionInstruction
- or
- isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal())
- or
- isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
- }
-
- private predicate shouldBeConflated(Instruction instr) {
- isOnAliasedDefinitionChain(instr)
- or
- instr instanceof UnmodeledDefinitionInstruction
- or
- instr.getOpcode() instanceof Opcode::InitializeNonLocal
- }
-
- query predicate notMarkedAsConflated(Instruction instr) {
- shouldBeConflated(instr) and
- not instr.isResultConflated()
- }
-
- query predicate wronglyMarkedAsConflated(Instruction instr) {
- instr.isResultConflated() and
- not shouldBeConflated(instr)
- }
-
- query predicate invalidOverlap(
- MemoryOperand useOperand, string message, IRFunction func, string funcText
- ) {
- exists(Overlap overlap |
- overlap = useOperand.getDefinitionOverlap() and
- overlap instanceof MayPartiallyOverlap and
- message =
- "MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
- overlap.toString() + "'." and
- func = useOperand.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
- )
- }
-}
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 0d5e7fe595c..a01bd2dc79a 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll
@@ -217,10 +217,23 @@ class IRThrowVariable extends IRTempVariable {
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
* function that accepts a variable number of arguments.
*/
-class IREllipsisVariable extends IRTempVariable {
+class IREllipsisVariable extends IRTempVariable, IRParameter {
IREllipsisVariable() { tag = EllipsisTempVar() }
final override string toString() { result = "#ellipsis" }
+
+ final override int getIndex() { result = func.getNumberOfParameters() }
+}
+
+/**
+ * A temporary variable generated to hold the `this` pointer.
+ */
+class IRThisVariable extends IRTempVariable, IRParameter {
+ IRThisVariable() { tag = ThisTempVar() }
+
+ final override string toString() { result = "#this" }
+
+ final override int getIndex() { result = -1 }
}
/**
@@ -265,3 +278,29 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
}
+
+/**
+ * An IR variable which acts like a function parameter, including positional parameters and the
+ * temporary variables generated for `this` and ellipsis parameters.
+ */
+class IRParameter extends IRAutomaticVariable {
+ IRParameter() {
+ this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter
+ or
+ this = TIRTempVariable(_, _, ThisTempVar(), _)
+ or
+ this = TIRTempVariable(_, _, EllipsisTempVar(), _)
+ }
+
+ /**
+ * Gets the zero-based index of this parameter. The `this` parameter has index -1.
+ */
+ int getIndex() { none() }
+}
+
+/**
+ * An IR variable representing a positional parameter.
+ */
+class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable {
+ final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll
index 38216872f2b..409577d3e46 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll
@@ -29,7 +29,13 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil
/**
* Represents a single operation in the IR.
*/
-class Instruction extends Construction::TInstruction {
+class Instruction extends Construction::TStageInstruction {
+ Instruction() {
+ // The base `TStageInstruction` type is a superset of the actual instructions appearing in this
+ // stage. This call lets the stage filter out the ones that are not reused from raw IR.
+ Construction::hasInstruction(this)
+ }
+
final string toString() { result = getOpcode().toString() + ": " + getAST().toString() }
/**
@@ -190,17 +196,18 @@ class Instruction extends Construction::TInstruction {
final Language::Location getLocation() { result = getAST().getLocation() }
/**
- * Gets the `Expr` whose result is computed by this instruction, if any.
+ * Gets the `Expr` whose result is computed by this instruction, if any. The `Expr` may be a
+ * conversion.
*/
final Language::Expr getConvertedResultExpression() {
- result = Construction::getInstructionConvertedResultExpression(this)
+ result = Raw::getInstructionConvertedResultExpression(this)
}
/**
- * Gets the unconverted `Expr` whose result is computed by this instruction, if any.
+ * Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any.
*/
final Language::Expr getUnconvertedResultExpression() {
- result = Construction::getInstructionUnconvertedResultExpression(this)
+ result = Raw::getInstructionUnconvertedResultExpression(this)
}
final Language::LanguageType getResultLanguageType() {
@@ -211,6 +218,7 @@ class Instruction extends Construction::TInstruction {
* Gets the type of the result produced by this instruction. If the instruction does not produce
* a result, its result type will be `IRVoidType`.
*/
+ cached
final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
/**
@@ -249,7 +257,7 @@ class Instruction extends Construction::TInstruction {
* result of the `Load` instruction is a prvalue of type `int`, representing
* the integer value loaded from variable `x`.
*/
- final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
+ final predicate isGLValue() { getResultLanguageType().hasType(_, true) }
/**
* Gets the size of the result produced by this instruction, in bytes. If the
@@ -258,7 +266,7 @@ class Instruction extends Construction::TInstruction {
* If `this.isGLValue()` holds for this instruction, the value of
* `getResultSize()` will always be the size of a pointer.
*/
- final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
+ final int getResultSize() { result = getResultLanguageType().getByteSize() }
/**
* Gets the opcode that specifies the operation performed by this instruction.
@@ -319,8 +327,7 @@ class Instruction extends Construction::TInstruction {
/**
* Holds if the result of this instruction is precisely modeled in SSA. Always
* holds for a register result. For a memory result, a modeled result is
- * connected to its actual uses. An unmodeled result is connected to the
- * `UnmodeledUse` instruction.
+ * connected to its actual uses. An unmodeled result has no uses.
*
* For example:
* ```
@@ -395,7 +402,7 @@ class Instruction extends Construction::TInstruction {
class VariableInstruction extends Instruction {
IRVariable var;
- VariableInstruction() { var = Construction::getInstructionVariable(this) }
+ VariableInstruction() { var = Raw::getInstructionVariable(this) }
override string getImmediateString() { result = var.toString() }
@@ -410,7 +417,7 @@ class VariableInstruction extends Instruction {
class FieldInstruction extends Instruction {
Language::Field field;
- FieldInstruction() { field = Construction::getInstructionField(this) }
+ FieldInstruction() { field = Raw::getInstructionField(this) }
final override string getImmediateString() { result = field.toString() }
@@ -420,7 +427,7 @@ class FieldInstruction extends Instruction {
class FunctionInstruction extends Instruction {
Language::Function funcSymbol;
- FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) }
+ FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) }
final override string getImmediateString() { result = funcSymbol.toString() }
@@ -430,7 +437,7 @@ class FunctionInstruction extends Instruction {
class ConstantValueInstruction extends Instruction {
string value;
- ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) }
+ ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) }
final override string getImmediateString() { result = value }
@@ -440,7 +447,7 @@ class ConstantValueInstruction extends Instruction {
class IndexedInstruction extends Instruction {
int index;
- IndexedInstruction() { index = Construction::getInstructionIndex(this) }
+ IndexedInstruction() { index = Raw::getInstructionIndex(this) }
final override string getImmediateString() { result = index.toString() }
@@ -541,6 +548,11 @@ class ReturnIndirectionInstruction extends VariableInstruction {
* function.
*/
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
+
+ /**
+ * Holds if this instruction is the return indirection for `this`.
+ */
+ final predicate isThisIndirection() { var instanceof IRThisVariable }
}
class CopyInstruction extends Instruction {
@@ -584,9 +596,9 @@ class ConditionalBranchInstruction extends Instruction {
final Instruction getCondition() { result = getConditionOperand().getDef() }
- final Instruction getTrueSuccessor() { result = getSuccessor(trueEdge()) }
+ final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) }
- final Instruction getFalseSuccessor() { result = getSuccessor(falseEdge()) }
+ final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) }
}
class ExitFunctionInstruction extends Instruction {
@@ -598,11 +610,16 @@ class ConstantInstruction extends ConstantValueInstruction {
}
class IntegerConstantInstruction extends ConstantInstruction {
- IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType }
+ IntegerConstantInstruction() {
+ exists(IRType resultType |
+ resultType = getResultIRType() and
+ (resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
+ )
+ }
}
class FloatConstantInstruction extends ConstantInstruction {
- FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
+ FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType }
}
class StringConstantInstruction extends VariableInstruction {
@@ -699,7 +716,7 @@ class PointerArithmeticInstruction extends BinaryInstruction {
PointerArithmeticInstruction() {
getOpcode() instanceof PointerArithmeticOpcode and
- elementSize = Construction::getInstructionElementSize(this)
+ elementSize = Raw::getInstructionElementSize(this)
}
final override string getImmediateString() { result = elementSize.toString() }
@@ -748,7 +765,7 @@ class InheritanceConversionInstruction extends UnaryInstruction {
Language::Class derivedClass;
InheritanceConversionInstruction() {
- Construction::getInstructionInheritance(this, baseClass, derivedClass)
+ Raw::getInstructionInheritance(this, baseClass, derivedClass)
}
final override string getImmediateString() {
@@ -906,7 +923,7 @@ class SwitchInstruction extends Instruction {
final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) }
- final Instruction getDefaultSuccessor() { result = getSuccessor(defaultEdge()) }
+ final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) }
}
/**
@@ -1211,7 +1228,7 @@ class CatchByTypeInstruction extends CatchInstruction {
CatchByTypeInstruction() {
getOpcode() instanceof Opcode::CatchByType and
- exceptionType = Construction::getInstructionExceptionType(this)
+ exceptionType = Raw::getInstructionExceptionType(this)
}
final override string getImmediateString() { result = exceptionType.toString() }
@@ -1229,10 +1246,6 @@ class CatchAnyInstruction extends CatchInstruction {
CatchAnyInstruction() { getOpcode() instanceof Opcode::CatchAny }
}
-class UnmodeledDefinitionInstruction extends Instruction {
- UnmodeledDefinitionInstruction() { getOpcode() instanceof Opcode::UnmodeledDefinition }
-}
-
/**
* An instruction that initializes all escaped memory.
*/
@@ -1247,12 +1260,6 @@ class AliasedUseInstruction extends Instruction {
AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse }
}
-class UnmodeledUseInstruction extends Instruction {
- UnmodeledUseInstruction() { getOpcode() instanceof Opcode::UnmodeledUse }
-
- override string getOperandsString() { result = "mu*" }
-}
-
/**
* An instruction representing the choice of one of multiple input values based on control flow.
*
@@ -1367,7 +1374,7 @@ class BuiltInOperationInstruction extends Instruction {
BuiltInOperationInstruction() {
getOpcode() instanceof BuiltInOperationOpcode and
- operation = Construction::getInstructionBuiltInOperation(this)
+ operation = Raw::getInstructionBuiltInOperation(this)
}
final Language::BuiltInOperation getBuiltInOperation() { result = operation }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll
index 1836f4c4b2f..f82704094c8 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
@@ -14,16 +14,8 @@ private newtype TOperand =
not Construction::isInCycle(useInstr) and
strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1
} or
- TNonPhiMemoryOperand(
- Instruction useInstr, MemoryOperandTag tag, Instruction defInstr, Overlap overlap
- ) {
- defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and
- not Construction::isInCycle(useInstr) and
- (
- strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
- or
- tag instanceof UnmodeledUseOperandTag
- )
+ TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
+ useInstr.getOpcode().hasOperand(tag)
} or
TPhiOperand(
PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
@@ -31,6 +23,57 @@ private newtype TOperand =
defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
}
+/**
+ * Base class for all register operands. This is a placeholder for the IPA union type that we will
+ * eventually use for this purpose.
+ */
+private class RegisterOperandBase extends TRegisterOperand {
+ /** Gets a textual representation of this element. */
+ abstract string toString();
+}
+
+/**
+ * Returns the register operand with the specified parameters.
+ */
+private RegisterOperandBase registerOperand(
+ Instruction useInstr, RegisterOperandTag tag, Instruction defInstr
+) {
+ result = TRegisterOperand(useInstr, tag, defInstr)
+}
+
+/**
+ * Base class for all non-Phi memory operands. This is a placeholder for the IPA union type that we
+ * will eventually use for this purpose.
+ */
+private class NonPhiMemoryOperandBase extends TNonPhiMemoryOperand {
+ /** Gets a textual representation of this element. */
+ abstract string toString();
+}
+
+/**
+ * Returns the non-Phi memory operand with the specified parameters.
+ */
+private NonPhiMemoryOperandBase nonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
+ result = TNonPhiMemoryOperand(useInstr, tag)
+}
+
+/**
+ * Base class for all Phi operands. This is a placeholder for the IPA union type that we will
+ * eventually use for this purpose.
+ */
+private class PhiOperandBase extends TPhiOperand {
+ abstract string toString();
+}
+
+/**
+ * Returns the Phi operand with the specified parameters.
+ */
+private PhiOperandBase phiOperand(
+ Instruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
+) {
+ result = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
+}
+
/**
* A source operand of an `Instruction`. The operand represents a value consumed by the instruction.
*/
@@ -104,7 +147,17 @@ class Operand extends TOperand {
* For example: `this:r3_5`
*/
final string getDumpString() {
- result = getDumpLabel() + getInexactSpecifier() + getAnyDef().getResultId()
+ result = getDumpLabel() + getInexactSpecifier() + getDefinitionId()
+ }
+
+ /**
+ * Gets a string containing the identifier of the definition of this use, or `m?` if the
+ * definition is not modeled in SSA.
+ */
+ private string getDefinitionId() {
+ result = getAnyDef().getResultId()
+ or
+ not exists(getAnyDef()) and result = "m?"
}
/**
@@ -169,8 +222,8 @@ class Operand extends TOperand {
*/
class MemoryOperand extends Operand {
MemoryOperand() {
- this = TNonPhiMemoryOperand(_, _, _, _) or
- this = TPhiOperand(_, _, _, _)
+ this instanceof NonPhiMemoryOperandBase or
+ this instanceof PhiOperandBase
}
/**
@@ -204,18 +257,15 @@ class MemoryOperand extends Operand {
*/
class NonPhiOperand extends Operand {
Instruction useInstr;
- Instruction defInstr;
OperandTag tag;
NonPhiOperand() {
- this = TRegisterOperand(useInstr, tag, defInstr) or
- this = TNonPhiMemoryOperand(useInstr, tag, defInstr, _)
+ this = registerOperand(useInstr, tag, _) or
+ this = nonPhiMemoryOperand(useInstr, tag)
}
final override Instruction getUse() { result = useInstr }
- final override Instruction getAnyDef() { result = defInstr }
-
final override string getDumpLabel() { result = tag.getLabel() }
final override int getDumpSortOrder() { result = tag.getSortOrder() }
@@ -226,8 +276,15 @@ class NonPhiOperand extends Operand {
/**
* An operand that consumes a register (non-memory) result.
*/
-class RegisterOperand extends NonPhiOperand, TRegisterOperand {
+class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
override RegisterOperandTag tag;
+ Instruction defInstr;
+
+ RegisterOperand() { this = registerOperand(useInstr, tag, defInstr) }
+
+ final override string toString() { result = tag.toString() }
+
+ final override Instruction getAnyDef() { result = defInstr }
final override Overlap getDefinitionOverlap() {
// All register results overlap exactly with their uses.
@@ -235,13 +292,25 @@ class RegisterOperand extends NonPhiOperand, TRegisterOperand {
}
}
-class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOperand {
+class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase {
override MemoryOperandTag tag;
- Overlap overlap;
- NonPhiMemoryOperand() { this = TNonPhiMemoryOperand(useInstr, tag, defInstr, overlap) }
+ NonPhiMemoryOperand() { this = nonPhiMemoryOperand(useInstr, tag) }
- final override Overlap getDefinitionOverlap() { result = overlap }
+ final override string toString() { result = tag.toString() }
+
+ final override Instruction getAnyDef() {
+ result = unique(Instruction defInstr | hasDefinition(defInstr, _))
+ }
+
+ final override Overlap getDefinitionOverlap() { hasDefinition(_, result) }
+
+ pragma[noinline]
+ private predicate hasDefinition(Instruction defInstr, Overlap overlap) {
+ defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and
+ not Construction::isInCycle(useInstr) and
+ strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
+ }
}
class TypedOperand extends NonPhiMemoryOperand {
@@ -258,8 +327,6 @@ class TypedOperand extends NonPhiMemoryOperand {
*/
class AddressOperand extends RegisterOperand {
override AddressOperandTag tag;
-
- override string toString() { result = "Address" }
}
/**
@@ -268,8 +335,6 @@ class AddressOperand extends RegisterOperand {
*/
class BufferSizeOperand extends RegisterOperand {
override BufferSizeOperandTag tag;
-
- override string toString() { result = "BufferSize" }
}
/**
@@ -278,8 +343,6 @@ class BufferSizeOperand extends RegisterOperand {
*/
class LoadOperand extends TypedOperand {
override LoadOperandTag tag;
-
- override string toString() { result = "Load" }
}
/**
@@ -287,8 +350,6 @@ class LoadOperand extends TypedOperand {
*/
class StoreValueOperand extends RegisterOperand {
override StoreValueOperandTag tag;
-
- override string toString() { result = "StoreValue" }
}
/**
@@ -296,8 +357,6 @@ class StoreValueOperand extends RegisterOperand {
*/
class UnaryOperand extends RegisterOperand {
override UnaryOperandTag tag;
-
- override string toString() { result = "Unary" }
}
/**
@@ -305,8 +364,6 @@ class UnaryOperand extends RegisterOperand {
*/
class LeftOperand extends RegisterOperand {
override LeftOperandTag tag;
-
- override string toString() { result = "Left" }
}
/**
@@ -314,8 +371,6 @@ class LeftOperand extends RegisterOperand {
*/
class RightOperand extends RegisterOperand {
override RightOperandTag tag;
-
- override string toString() { result = "Right" }
}
/**
@@ -323,18 +378,6 @@ class RightOperand extends RegisterOperand {
*/
class ConditionOperand extends RegisterOperand {
override ConditionOperandTag tag;
-
- override string toString() { result = "Condition" }
-}
-
-/**
- * An operand of the special `UnmodeledUse` instruction, representing a value
- * whose set of uses is unknown.
- */
-class UnmodeledUseOperand extends NonPhiMemoryOperand {
- override UnmodeledUseOperandTag tag;
-
- override string toString() { result = "UnmodeledUse" }
}
/**
@@ -342,8 +385,6 @@ class UnmodeledUseOperand extends NonPhiMemoryOperand {
*/
class CallTargetOperand extends RegisterOperand {
override CallTargetOperandTag tag;
-
- override string toString() { result = "CallTarget" }
}
/**
@@ -361,8 +402,6 @@ class ArgumentOperand extends RegisterOperand {
*/
class ThisArgumentOperand extends ArgumentOperand {
override ThisArgumentOperandTag tag;
-
- override string toString() { result = "ThisArgument" }
}
/**
@@ -370,34 +409,27 @@ class ThisArgumentOperand extends ArgumentOperand {
*/
class PositionalArgumentOperand extends ArgumentOperand {
override PositionalArgumentOperandTag tag;
- int argIndex;
-
- PositionalArgumentOperand() { argIndex = tag.getArgIndex() }
-
- override string toString() { result = "Arg(" + argIndex + ")" }
/**
* Gets the zero-based index of the argument.
*/
- final int getIndex() { result = argIndex }
+ final int getIndex() { result = tag.getArgIndex() }
}
class SideEffectOperand extends TypedOperand {
override SideEffectOperandTag tag;
-
- override string toString() { result = "SideEffect" }
}
/**
* An operand of a `PhiInstruction`.
*/
-class PhiInputOperand extends MemoryOperand, TPhiOperand {
+class PhiInputOperand extends MemoryOperand, PhiOperandBase {
PhiInstruction useInstr;
Instruction defInstr;
IRBlock predecessorBlock;
Overlap overlap;
- PhiInputOperand() { this = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap) }
+ PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
override string toString() { result = "Phi" }
@@ -427,8 +459,6 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
class ChiTotalOperand extends NonPhiMemoryOperand {
override ChiTotalOperandTag tag;
- override string toString() { result = "ChiTotal" }
-
final override MemoryAccessKind getMemoryAccess() { result instanceof ChiTotalMemoryAccess }
}
@@ -438,7 +468,5 @@ class ChiTotalOperand extends NonPhiMemoryOperand {
class ChiPartialOperand extends NonPhiMemoryOperand {
override ChiPartialOperandTag tag;
- override string toString() { result = "ChiPartial" }
-
final override MemoryAccessKind getMemoryAccess() { result instanceof ChiPartialMemoryAccess }
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/constant/ConstantAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/constant/ConstantAnalysis.qll
index 40076b817a1..76f52f8334a 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/constant/ConstantAnalysis.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/constant/ConstantAnalysis.qll
@@ -14,8 +14,7 @@ int getConstantValue(Instruction instr) {
or
exists(PhiInstruction phi |
phi = instr and
- result = max(Operand op | op = phi.getAnInputOperand() | getConstantValue(op.getDef())) and
- result = min(Operand op | op = phi.getAnInputOperand() | getConstantValue(op.getDef()))
+ result = unique(Operand op | op = phi.getAnInputOperand() | getConstantValue(op.getDef()))
)
}
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 13d19587135..796fb792366 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
@@ -56,7 +56,7 @@ class ValueNumber extends TValueNumber {
or
this instanceof TInitializeParameterValueNumber and result = "InitializeParameter"
or
- this instanceof TInitializeThisValueNumber and result = "InitializeThis"
+ this instanceof TConstantValueNumber and result = "Constant"
or
this instanceof TStringConstantValueNumber and result = "StringConstant"
or
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll
index 169b0ef7ccf..2467d961892 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll
@@ -7,7 +7,6 @@ newtype TValueNumber =
TInitializeParameterValueNumber(IRFunction irFunc, Language::AST var) {
initializeParameterValueNumber(_, irFunc, var)
} or
- TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or
TConstantValueNumber(IRFunction irFunc, IRType type, string value) {
constantValueNumber(_, irFunc, type, value)
} or
@@ -79,8 +78,6 @@ private predicate numberableInstruction(Instruction instr) {
or
instr instanceof InitializeParameterInstruction
or
- instr instanceof InitializeThisInstruction
- or
instr instanceof ConstantInstruction
or
instr instanceof StringConstantInstruction
@@ -132,10 +129,6 @@ private predicate initializeParameterValueNumber(
instr.getIRVariable().getAST() = var
}
-private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
- instr.getEnclosingIRFunction() = irFunc
-}
-
private predicate constantValueNumber(
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
@@ -268,9 +261,6 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
result = TInitializeParameterValueNumber(irFunc, var)
)
or
- initializeThisValueNumber(instr, irFunc) and
- result = TInitializeThisValueNumber(irFunc)
- or
exists(string value, IRType type |
constantValueNumber(instr, irFunc, type, value) and
result = TConstantValueNumber(irFunc, type, value)
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 e2d3828fc52..1612e0065b7 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
@@ -204,7 +204,7 @@ private predicate isArgumentForParameter(CallInstruction ci, Operand operand, In
init.(InitializeParameterInstruction).getParameter() =
f.getParameter(operand.(PositionalArgumentOperand).getIndex())
or
- init instanceof InitializeThisInstruction and
+ init.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable and
init.getEnclosingFunction() = f and
operand instanceof ThisArgumentOperand
) and
@@ -247,6 +247,10 @@ private predicate resultMayReachReturn(Instruction instr) { operandMayReachRetur
private predicate resultEscapesNonReturn(Instruction instr) {
// The result escapes if it has at least one use that escapes.
operandEscapesNonReturn(instr.getAUse())
+ or
+ // The result also escapes if it is not modeled in SSA, because we do not know where it might be
+ // used.
+ not instr.isResultModeled()
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll
index e95086c89fc..69cd6e6dc29 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasConfiguration.qll
@@ -5,7 +5,7 @@ private import AliasAnalysis
private newtype TAllocation =
TVariableAllocation(IRVariable var) or
- TIndirectParameterAllocation(IRAutomaticUserVariable var) {
+ TIndirectParameterAllocation(IRAutomaticVariable var) {
exists(InitializeIndirectionInstruction instr | instr.getIRVariable() = var)
} or
TDynamicAllocation(CallInstruction call) {
@@ -74,7 +74,7 @@ class VariableAllocation extends Allocation, TVariableAllocation {
}
class IndirectParameterAllocation extends Allocation, TIndirectParameterAllocation {
- IRAutomaticUserVariable var;
+ IRAutomaticVariable var;
IndirectParameterAllocation() { this = TIndirectParameterAllocation(var) }
@@ -90,7 +90,7 @@ class IndirectParameterAllocation extends Allocation, TIndirectParameterAllocati
final override string getUniqueId() { result = var.getUniqueId() }
- final override IRType getIRType() { result = var.getIRType() }
+ final override IRType getIRType() { result instanceof IRUnknownType }
final override predicate isReadOnly() { none() }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll
new file mode 100644
index 00000000000..8ec63b7c1cb
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll
@@ -0,0 +1 @@
+import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRInternal.qll
index 4cc52d3bbf9..3a7a08accc0 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRInternal.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRInternal.qll
@@ -1,3 +1,4 @@
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
import SSAConstruction as Construction
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration
+import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Raw
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll
index dc02760c7c4..72bb239c153 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
@@ -6,11 +6,12 @@ private import DebugSSA
bindingset[offset]
private string getKeySuffixForOffset(int offset) {
+ offset >= 0 and
if offset % 2 = 0 then result = "" else result = "_Chi"
}
bindingset[offset]
-private int getIndexForOffset(int offset) { result = offset / 2 }
+private int getIndexForOffset(int offset) { offset >= 0 and result = offset / 2 }
/**
* Property provide that dumps the memory access of each result. Useful for debugging SSA
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.ql b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.ql
new file mode 100644
index 00000000000..3379f4530a1
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.ql
@@ -0,0 +1,8 @@
+/**
+ * @name Aliased SSA Consistency Check
+ * @description Performs consistency checks on the SSA construction. This query should have no results.
+ * @kind table
+ * @id cpp/aliased-ssa-consistency-check
+ */
+
+import SSAConsistency
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSASanity.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.qll
similarity index 58%
rename from cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSASanity.qll
rename to cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.qll
index 95e8443b2a3..5686bb439eb 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSASanity.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.qll
@@ -1,2 +1,2 @@
private import SSAConstruction as SSA
-import SSA::SSASanity
+import SSA::SSAConsistency
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 155934689b6..ae0e03e97da 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll
@@ -1,5 +1,11 @@
import SSAConstructionInternal
-private import SSAConstructionImports
+private import SSAConstructionImports as Imports
+private import Imports::Opcode
+private import Imports::OperandTag
+private import Imports::Overlap
+private import Imports::TInstruction
+private import Imports::RawIR as RawIR
+private import SSAInstructions
private import NewIR
private class OldBlock = Reachability::ReachableBlock;
@@ -10,54 +16,47 @@ import Cached
cached
private module Cached {
+ cached
+ predicate hasPhiInstructionCached(
+ OldInstruction blockStartInstr, Alias::MemoryLocation defLocation
+ ) {
+ exists(OldBlock oldBlock |
+ definitionHasPhiNode(defLocation, oldBlock) and
+ blockStartInstr = oldBlock.getFirstInstruction()
+ )
+ }
+
+ cached
+ predicate hasChiInstructionCached(OldInstruction primaryInstruction) {
+ hasChiNode(_, primaryInstruction)
+ }
+
+ cached
+ predicate hasUnreachedInstructionCached(IRFunction irFunc) {
+ exists(OldInstruction oldInstruction |
+ irFunc = oldInstruction.getEnclosingIRFunction() and
+ Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
+ )
+ }
+
+ class TStageInstruction =
+ TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction;
+
+ cached
+ predicate hasInstruction(TStageInstruction instr) {
+ instr instanceof TRawInstruction and instr instanceof OldInstruction
+ or
+ instr instanceof TPhiInstruction
+ or
+ instr instanceof TChiInstruction
+ or
+ instr instanceof TUnreachedInstruction
+ }
+
private IRBlock getNewBlock(OldBlock oldBlock) {
result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
}
- cached
- predicate functionHasIR(Language::Function func) {
- exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func)
- }
-
- cached
- OldInstruction getOldInstruction(Instruction instr) { instr = WrappedInstruction(result) }
-
- private IRVariable getNewIRVariable(OldIR::IRVariable var) {
- // This is just a type cast. Both classes derive from the same newtype.
- result = var
- }
-
- cached
- newtype TInstruction =
- WrappedInstruction(OldInstruction oldInstruction) {
- not oldInstruction instanceof OldIR::PhiInstruction
- } or
- Phi(OldBlock block, Alias::MemoryLocation defLocation) {
- definitionHasPhiNode(defLocation, block)
- } or
- Chi(OldInstruction oldInstruction) {
- not oldInstruction instanceof OldIR::PhiInstruction and
- hasChiNode(_, oldInstruction)
- } or
- Unreached(Language::Function function) {
- exists(OldInstruction oldInstruction |
- function = oldInstruction.getEnclosingFunction() and
- Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
- )
- }
-
- cached
- predicate hasTempVariable(
- Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type
- ) {
- exists(OldIR::IRTempVariable var |
- var.getEnclosingFunction() = func and
- var.getAST() = ast and
- var.getTag() = tag and
- var.getLanguageType() = type
- )
- }
-
cached
predicate hasModeledMemoryResult(Instruction instruction) {
exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or
@@ -67,15 +66,13 @@ private module Cached {
cached
predicate hasConflatedMemoryResult(Instruction instruction) {
- instruction instanceof UnmodeledDefinitionInstruction
- or
instruction instanceof AliasedDefinitionInstruction
or
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
or
// Chi instructions track virtual variables, and therefore a chi instruction is
// conflated if it's associated with the aliased virtual variable.
- exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) |
+ exists(OldInstruction oldInstruction | instruction = getChi(oldInstruction) |
Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof
Alias::AliasedVirtualVariable
)
@@ -83,7 +80,7 @@ private module Cached {
// Phi instructions track locations, and therefore a phi instruction is
// conflated if it's associated with a conflated location.
exists(Alias::MemoryLocation location |
- instruction = Phi(_, location) and
+ instruction = getPhi(_, location) and
not exists(location.getAllocation())
)
}
@@ -127,40 +124,13 @@ private module Cached {
oldInstruction = getOldInstruction(instruction) and
oldOperand = oldInstruction.getAnOperand() and
tag = oldOperand.getOperandTag() and
- (
- (
- if exists(Alias::getOperandMemoryLocation(oldOperand))
- then hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result)
- else (
- result = instruction.getEnclosingIRFunction().getUnmodeledDefinitionInstruction() and
- overlap instanceof MustTotallyOverlap
- )
- )
- or
- // Connect any definitions that are not being modeled in SSA to the
- // `UnmodeledUse` instruction.
- exists(OldInstruction oldDefinition |
- instruction instanceof UnmodeledUseInstruction and
- tag instanceof UnmodeledUseOperandTag and
- oldDefinition = oldOperand.getAnyDef() and
- not exists(Alias::getResultMemoryLocation(oldDefinition)) and
- result = getNewInstruction(oldDefinition) and
- overlap instanceof MustTotallyOverlap
- )
- )
+ hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result)
)
or
- instruction = Chi(getOldInstruction(result)) and
+ instruction = getChi(getOldInstruction(result)) and
tag instanceof ChiPartialOperandTag and
overlap instanceof MustExactlyOverlap
or
- exists(IRFunction f |
- tag instanceof UnmodeledUseOperandTag and
- result = f.getUnmodeledDefinitionInstruction() and
- instruction = f.getUnmodeledUseInstruction() and
- overlap instanceof MustTotallyOverlap
- )
- or
tag instanceof ChiTotalOperandTag and
result = getChiInstructionTotalOperand(instruction) and
overlap instanceof MustExactlyOverlap
@@ -201,13 +171,15 @@ private module Cached {
pragma[noopt]
cached
- Instruction getPhiOperandDefinition(Phi instr, IRBlock newPredecessorBlock, Overlap overlap) {
+ Instruction getPhiOperandDefinition(
+ PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap
+ ) {
exists(
Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock,
OldBlock predBlock, OldBlock defBlock, int defOffset, Alias::MemoryLocation actualDefLocation
|
hasPhiOperandDefinition(defLocation, useLocation, phiBlock, predBlock, defBlock, defOffset) and
- instr = Phi(phiBlock, useLocation) and
+ instr = getPhi(phiBlock, useLocation) and
newPredecessorBlock = getNewBlock(predBlock) and
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
overlap = Alias::getOverlap(actualDefLocation, useLocation)
@@ -220,7 +192,7 @@ private module Cached {
Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation,
OldBlock defBlock, int defRank, int defOffset, OldBlock useBlock, int useRank
|
- chiInstr = Chi(oldInstr) and
+ chiInstr = getChi(oldInstr) and
vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and
hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) and
hasUseAtRank(vvar, useBlock, useRank, oldInstr) and
@@ -232,21 +204,11 @@ private module Cached {
cached
Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
exists(OldBlock oldBlock |
- instr = Phi(oldBlock, _) and
+ instr = getPhi(oldBlock, _) and
result = getNewInstruction(oldBlock.getFirstInstruction())
)
}
- cached
- Language::Expr getInstructionConvertedResultExpression(Instruction instruction) {
- result = getOldInstruction(instruction).getConvertedResultExpression()
- }
-
- cached
- Language::Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
- result = getOldInstruction(instruction).getUnconvertedResultExpression()
- }
-
/*
* This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node,
* that node is its successor in the new successor relation, and the Chi node's successors are
@@ -257,20 +219,20 @@ private module Cached {
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
if hasChiNode(_, getOldInstruction(instruction))
then
- result = Chi(getOldInstruction(instruction)) and
+ result = getChi(getOldInstruction(instruction)) and
kind instanceof GotoEdge
else (
exists(OldInstruction oldInstruction |
oldInstruction = getOldInstruction(instruction) and
(
if Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind)
- then result = Unreached(instruction.getEnclosingFunction())
+ then result = unreachedInstruction(instruction.getEnclosingIRFunction())
else result = getNewInstruction(oldInstruction.getSuccessor(kind))
)
)
or
exists(OldInstruction oldInstruction |
- instruction = Chi(oldInstruction) and
+ instruction = getChi(oldInstruction) and
result = getNewInstruction(oldInstruction.getSuccessor(kind))
)
)
@@ -289,137 +251,73 @@ private module Cached {
// `oldInstruction`, in which case the back edge should come out of the
// chi node instead.
if hasChiNode(_, oldInstruction)
- then instruction = Chi(oldInstruction)
+ then instruction = getChi(oldInstruction)
else instruction = getNewInstruction(oldInstruction)
)
}
cached
- Language::AST getInstructionAST(Instruction instruction) {
- exists(OldInstruction oldInstruction |
- instruction = WrappedInstruction(oldInstruction)
- or
- instruction = Chi(oldInstruction)
- |
- result = oldInstruction.getAST()
+ Language::AST getInstructionAST(Instruction instr) {
+ result = getOldInstruction(instr).getAST()
+ or
+ exists(RawIR::Instruction blockStartInstr |
+ instr = phiInstruction(blockStartInstr, _) and
+ result = blockStartInstr.getAST()
)
or
- exists(OldBlock block |
- instruction = Phi(block, _) and
- result = block.getFirstInstruction().getAST()
+ exists(RawIR::Instruction primaryInstr |
+ instr = chiInstruction(primaryInstr) and
+ result = primaryInstr.getAST()
)
or
- instruction = Unreached(result)
+ exists(IRFunctionBase irFunc |
+ instr = unreachedInstruction(irFunc) and result = irFunc.getFunction()
+ )
}
cached
- Language::LanguageType getInstructionResultType(Instruction instruction) {
- exists(OldInstruction oldInstruction |
- instruction = WrappedInstruction(oldInstruction) and
- result = oldInstruction.getResultLanguageType()
+ Language::LanguageType getInstructionResultType(Instruction instr) {
+ result = instr.(RawIR::Instruction).getResultLanguageType()
+ or
+ exists(Alias::MemoryLocation defLocation |
+ instr = phiInstruction(_, defLocation) and
+ result = defLocation.getType()
)
or
- exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar |
- instruction = Chi(oldInstruction) and
- hasChiNode(vvar, oldInstruction) and
+ exists(Instruction primaryInstr, Alias::VirtualVariable vvar |
+ instr = chiInstruction(primaryInstr) and
+ hasChiNode(vvar, primaryInstr) and
result = vvar.getType()
)
or
- exists(Alias::MemoryLocation location |
- instruction = Phi(_, location) and
- result = location.getType()
+ instr = unreachedInstruction(_) and result = Language::getVoidType()
+ }
+
+ cached
+ Opcode getInstructionOpcode(Instruction instr) {
+ result = getOldInstruction(instr).getOpcode()
+ or
+ instr = phiInstruction(_, _) and result instanceof Opcode::Phi
+ or
+ instr = chiInstruction(_) and result instanceof Opcode::Chi
+ or
+ instr = unreachedInstruction(_) and result instanceof Opcode::Unreached
+ }
+
+ cached
+ IRFunctionBase getInstructionEnclosingIRFunction(Instruction instr) {
+ result = getOldInstruction(instr).getEnclosingIRFunction()
+ or
+ exists(OldInstruction blockStartInstr |
+ instr = phiInstruction(blockStartInstr, _) and
+ result = blockStartInstr.getEnclosingIRFunction()
)
or
- instruction = Unreached(_) and
- result = Language::getVoidType()
- }
-
- cached
- Opcode getInstructionOpcode(Instruction instruction) {
- exists(OldInstruction oldInstruction |
- instruction = WrappedInstruction(oldInstruction) and
- result = oldInstruction.getOpcode()
+ exists(OldInstruction primaryInstr |
+ instr = chiInstruction(primaryInstr) and result = primaryInstr.getEnclosingIRFunction()
)
or
- instruction instanceof Chi and
- result instanceof Opcode::Chi
- or
- instruction instanceof Phi and
- result instanceof Opcode::Phi
- or
- instruction instanceof Unreached and
- result instanceof Opcode::Unreached
- }
-
- cached
- IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
- exists(OldInstruction oldInstruction |
- instruction = WrappedInstruction(oldInstruction)
- or
- instruction = Chi(oldInstruction)
- |
- result.getFunction() = oldInstruction.getEnclosingFunction()
- )
- or
- exists(OldBlock block |
- instruction = Phi(block, _) and
- result.getFunction() = block.getEnclosingFunction()
- )
- or
- instruction = Unreached(result.getFunction())
- }
-
- cached
- IRVariable getInstructionVariable(Instruction instruction) {
- result =
- getNewIRVariable(getOldInstruction(instruction).(OldIR::VariableInstruction).getIRVariable())
- }
-
- cached
- Language::Field getInstructionField(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField()
- }
-
- cached
- int getInstructionIndex(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex()
- }
-
- cached
- Language::Function getInstructionFunction(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
- }
-
- cached
- string getInstructionConstantValue(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::ConstantValueInstruction).getValue()
- }
-
- cached
- Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
- result =
- getOldInstruction(instruction).(OldIR::BuiltInOperationInstruction).getBuiltInOperation()
- }
-
- cached
- Language::LanguageType getInstructionExceptionType(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType()
- }
-
- cached
- int getInstructionElementSize(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::PointerArithmeticInstruction).getElementSize()
- }
-
- cached
- predicate getInstructionInheritance(
- Instruction instruction, Language::Class baseClass, Language::Class derivedClass
- ) {
- exists(OldIR::InheritanceConversionInstruction oldInstr |
- oldInstr = getOldInstruction(instruction) and
- baseClass = oldInstr.getBaseClass() and
- derivedClass = oldInstr.getDerivedClass()
- )
+ instr = unreachedInstruction(result)
}
cached
@@ -430,7 +328,7 @@ private module Cached {
)
or
exists(OldIR::Instruction oldInstruction |
- instruction = Chi(oldInstruction) and
+ instruction = getChi(oldInstruction) and
result = getNewInstruction(oldInstruction)
)
}
@@ -438,6 +336,14 @@ private module Cached {
private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr }
+private OldInstruction getOldInstruction(Instruction instr) { instr = result }
+
+private ChiInstruction getChi(OldInstruction primaryInstr) { result = chiInstruction(primaryInstr) }
+
+private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) {
+ result = phiInstruction(defBlock.getFirstInstruction(), defLocation)
+}
+
/**
* Holds if instruction `def` needs to have a `Chi` instruction inserted after it, to account for a partial definition
* of a virtual variable. The `Chi` instruction provides a definition of the entire virtual variable of which the
@@ -617,7 +523,7 @@ module DefUse {
|
// An odd offset corresponds to the `Chi` instruction.
defOffset = oldOffset * 2 + 1 and
- result = Chi(oldInstr) and
+ result = getChi(oldInstr) and
(
defLocation = Alias::getResultMemoryLocation(oldInstr) or
defLocation = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable()
@@ -636,7 +542,7 @@ module DefUse {
or
defOffset = -1 and
hasDefinition(_, defLocation, defBlock, defOffset) and
- result = Phi(defBlock, defLocation) and
+ result = getPhi(defBlock, defLocation) and
actualDefLocation = defLocation
}
@@ -920,7 +826,7 @@ private module CachedForDebugging {
)
or
exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity |
- instr = Phi(phiBlock, location) and
+ instr = getPhi(phiBlock, location) and
result =
"Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and
if location instanceof Alias::VirtualVariable
@@ -930,7 +836,7 @@ private module CachedForDebugging {
else specificity = "s"
)
or
- instr = Unreached(_) and
+ instr = unreachedInstruction(_) and
result = "Unreached"
}
@@ -941,7 +847,10 @@ private module CachedForDebugging {
}
}
-module SSASanity {
+module SSAConsistency {
+ /**
+ * Holds if a `MemoryOperand` has more than one `MemoryLocation` assigned by alias analysis.
+ */
query predicate multipleOperandMemoryLocations(
OldIR::MemoryOperand operand, string message, OldIR::IRFunction func, string funcText
) {
@@ -954,6 +863,9 @@ module SSASanity {
)
}
+ /**
+ * Holds if a `MemoryLocation` does not have an associated `VirtualVariable`.
+ */
query predicate missingVirtualVariableForMemoryLocation(
Alias::MemoryLocation location, string message, OldIR::IRFunction func, string funcText
) {
@@ -962,4 +874,41 @@ module SSASanity {
funcText = Language::getIdentityString(func.getFunction()) and
message = "Memory location has no virtual variable in function '$@'."
}
+
+ /**
+ * Holds if a `MemoryLocation` is a member of more than one `VirtualVariable`.
+ */
+ query predicate multipleVirtualVariablesForMemoryLocation(
+ Alias::MemoryLocation location, string message, OldIR::IRFunction func, string funcText
+ ) {
+ exists(int vvarCount |
+ vvarCount = strictcount(location.getVirtualVariable()) and
+ vvarCount > 1 and
+ func = location.getIRFunction() and
+ funcText = Language::getIdentityString(func.getFunction()) and
+ message =
+ "Memory location has " + vvarCount.toString() + " virtual variables in function '$@': (" +
+ concat(Alias::VirtualVariable vvar |
+ vvar = location.getVirtualVariable()
+ |
+ vvar.toString(), ", "
+ ) + ")."
+ )
+ }
+}
+
+/**
+ * Provides the portion of the parameterized IR interface that is used to construct the SSA stages
+ * of the IR. The raw stage of the IR does not expose these predicates.
+ * These predicates are all just aliases for predicates defined in the `Cached` module. This ensures
+ * that all of SSA construction will be evaluated in the same stage.
+ */
+module SSA {
+ class MemoryLocation = Alias::MemoryLocation;
+
+ predicate hasPhiInstruction = Cached::hasPhiInstructionCached/2;
+
+ predicate hasChiInstruction = Cached::hasChiInstructionCached/1;
+
+ predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1;
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll
index 00f12020a29..f347df86ba1 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll
@@ -1,3 +1,5 @@
-import semmle.code.cpp.ir.implementation.Opcode
-import semmle.code.cpp.ir.implementation.internal.OperandTag
-import semmle.code.cpp.ir.internal.Overlap
+import semmle.code.cpp.ir.implementation.Opcode as Opcode
+import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
+import semmle.code.cpp.ir.internal.Overlap as Overlap
+import semmle.code.cpp.ir.implementation.internal.TInstruction as TInstruction
+import semmle.code.cpp.ir.implementation.raw.IR as RawIR
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll
index c0922aff891..bb068bdd489 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll
@@ -2,5 +2,6 @@ import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as OldIR
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.ReachableBlock as Reachability
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.Dominance as Dominance
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as NewIR
+import semmle.code.cpp.ir.implementation.internal.TInstruction::AliasedSSAInstructions as SSAInstructions
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
import AliasedSSA as Alias
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSASanity.ql b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSASanity.ql
deleted file mode 100644
index 3bd709a93dc..00000000000
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSASanity.ql
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * @name Aliased SSA Sanity Check
- * @description Performs sanity checks on the SSA construction. This query should have no results.
- * @kind table
- * @id cpp/aliased-ssa-sanity-check
- */
-
-import SSASanity
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll
new file mode 100644
index 00000000000..60895ce3d26
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll
@@ -0,0 +1,27 @@
+/**
+ * Provides a base class, `IRFunctionBase`, for the stage-independent portions of `IRFunction`.
+ */
+
+private import IRFunctionBaseInternal
+
+private newtype TIRFunction =
+ MkIRFunction(Language::Function func) { IRConstruction::Raw::functionHasIR(func) }
+
+/**
+ * The IR for a function. This base class contains only the predicates that are the same between all
+ * phases of the IR. Each instantiation of `IRFunction` extends this class.
+ */
+class IRFunctionBase extends TIRFunction {
+ Language::Function func;
+
+ IRFunctionBase() { this = MkIRFunction(func) }
+
+ /** Gets a textual representation of this element. */
+ final string toString() { result = "IR: " + func.toString() }
+
+ /** Gets the function whose IR is represented. */
+ final Language::Function getFunction() { result = func }
+
+ /** Gets the location of the function. */
+ final Language::Location getLocation() { result = func.getLocation() }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBaseInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBaseInternal.qll
new file mode 100644
index 00000000000..cc1bdb6444b
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBaseInternal.qll
@@ -0,0 +1,2 @@
+import semmle.code.cpp.ir.internal.IRCppLanguage as Language
+import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll
index 227b1a34041..ac284440648 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
@@ -1,3 +1,8 @@
+/**
+ * Defines the set of possible `OperandTag`s, which are used to identify the role each `Operand`
+ * plays in the evaluation of its `Instruction`.
+ */
+
private import OperandTagInternal
private newtype TOperandTag =
@@ -10,7 +15,6 @@ private newtype TOperandTag =
TLeftOperand() or
TRightOperand() or
TConditionOperand() or
- TUnmodeledUseOperand() or
TCallTargetOperand() or
TThisArgumentOperand() or
TPositionalArgumentOperand(int argIndex) { Language::hasPositionalArgIndex(argIndex) } or
@@ -24,10 +28,18 @@ private newtype TOperandTag =
* an `Instruction` is determined by the instruction's opcode.
*/
abstract class OperandTag extends TOperandTag {
+ /** Gets a textual representation of this operand tag */
abstract string toString();
+ /**
+ * Gets an integer that represents where this this operand will appear in the operand list of an
+ * instruction when the IR is printed.
+ */
abstract int getSortOrder();
+ /**
+ * Gets a label that will appear before the operand when the IR is printed.
+ */
string getLabel() { result = "" }
}
@@ -47,7 +59,7 @@ abstract class RegisterOperandTag extends OperandTag { }
abstract class TypedOperandTag extends MemoryOperandTag { }
// Note: individual subtypes are listed in the order that the operands should
-// appear in the operand list of the instruction when printing.
+// appear in the operand list of the instruction when the IR is printed.
/**
* The address operand of an instruction that loads or stores a value from
* memory (e.g. `Load`, `Store`, `InitializeParameter`, `IndirectReadSideEffect`).
@@ -152,18 +164,6 @@ class ConditionOperandTag extends RegisterOperandTag, TConditionOperand {
ConditionOperandTag conditionOperand() { result = TConditionOperand() }
-/**
- * An operand of the special `UnmodeledUse` instruction, representing a value
- * whose set of uses is unknown.
- */
-class UnmodeledUseOperandTag extends MemoryOperandTag, TUnmodeledUseOperand {
- final override string toString() { result = "UnmodeledUse" }
-
- final override int getSortOrder() { result = 9 }
-}
-
-UnmodeledUseOperandTag unmodeledUseOperand() { result = TUnmodeledUseOperand() }
-
/**
* The operand representing the target function of an `Call` instruction.
*/
@@ -221,7 +221,9 @@ PositionalArgumentOperandTag positionalArgumentOperand(int argIndex) {
result = TPositionalArgumentOperand(argIndex)
}
-class ChiTotalOperandTag extends MemoryOperandTag, TChiTotalOperand {
+abstract class ChiOperandTag extends MemoryOperandTag { }
+
+class ChiTotalOperandTag extends ChiOperandTag, TChiTotalOperand {
final override string toString() { result = "ChiTotal" }
final override int getSortOrder() { result = 13 }
@@ -231,7 +233,7 @@ class ChiTotalOperandTag extends MemoryOperandTag, TChiTotalOperand {
ChiTotalOperandTag chiTotalOperand() { result = TChiTotalOperand() }
-class ChiPartialOperandTag extends MemoryOperandTag, TChiPartialOperand {
+class ChiPartialOperandTag extends ChiOperandTag, TChiPartialOperand {
final override string toString() { result = "ChiPartial" }
final override int getSortOrder() { result = 14 }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariableInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariableInternal.qll
index 362274f387c..7984c4883fd 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariableInternal.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariableInternal.qll
@@ -1,5 +1,5 @@
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
-import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as Construction
+import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Construction
private import semmle.code.cpp.ir.implementation.TempVariableTag as TempVariableTag_
module Imports {
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll
new file mode 100644
index 00000000000..e16b71733b5
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll
@@ -0,0 +1,97 @@
+private import TInstructionInternal
+private import IRFunctionBase
+private import TInstructionImports as Imports
+private import Imports::IRType
+private import Imports::Opcode
+
+/**
+ * An IR instruction. `TInstruction` is shared across all phases of the IR. There are individual
+ * branches of this type for instructions created directly from the AST (`TRawInstruction`) and for
+ * instructions added by each stage of SSA construction (`T*PhiInstruction`, `T*ChiInstruction`,
+ * `T*UnreachedInstruction`). Each stage then defines a `TStageInstruction` type that is a union of
+ * all of the branches that can appear in that particular stage. The public `Instruction` class for
+ * each phase extends the `TStageInstruction` type for that stage.
+ */
+cached
+newtype TInstruction =
+ TRawInstruction(
+ IRConstruction::Raw::InstructionTag1 tag1, IRConstruction::Raw::InstructionTag2 tag2
+ ) {
+ IRConstruction::Raw::hasInstruction(tag1, tag2)
+ } or
+ TUnaliasedSSAPhiInstruction(
+ TRawInstruction blockStartInstr, UnaliasedSSA::SSA::MemoryLocation memoryLocation
+ ) {
+ UnaliasedSSA::SSA::hasPhiInstruction(blockStartInstr, memoryLocation)
+ } or
+ TUnaliasedSSAChiInstruction(TRawInstruction primaryInstruction) { none() } or
+ TUnaliasedSSAUnreachedInstruction(IRFunctionBase irFunc) {
+ UnaliasedSSA::SSA::hasUnreachedInstruction(irFunc)
+ } or
+ TAliasedSSAPhiInstruction(
+ TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation
+ ) {
+ AliasedSSA::SSA::hasPhiInstruction(blockStartInstr, memoryLocation)
+ } or
+ TAliasedSSAChiInstruction(TRawInstruction primaryInstruction) {
+ AliasedSSA::SSA::hasChiInstruction(primaryInstruction)
+ } or
+ TAliasedSSAUnreachedInstruction(IRFunctionBase irFunc) {
+ AliasedSSA::SSA::hasUnreachedInstruction(irFunc)
+ }
+
+/**
+ * Provides wrappers for the constructors of each branch of `TInstruction` that is used by the
+ * unaliased SSA stage.
+ * These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
+ * a class alias.
+ */
+module UnaliasedSSAInstructions {
+ class TPhiInstruction = TUnaliasedSSAPhiInstruction;
+
+ TPhiInstruction phiInstruction(
+ TRawInstruction blockStartInstr, UnaliasedSSA::SSA::MemoryLocation memoryLocation
+ ) {
+ result = TUnaliasedSSAPhiInstruction(blockStartInstr, memoryLocation)
+ }
+
+ class TChiInstruction = TUnaliasedSSAChiInstruction;
+
+ TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
+ result = TUnaliasedSSAChiInstruction(primaryInstruction)
+ }
+
+ class TUnreachedInstruction = TUnaliasedSSAUnreachedInstruction;
+
+ TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) {
+ result = TUnaliasedSSAUnreachedInstruction(irFunc)
+ }
+}
+
+/**
+ * Provides wrappers for the constructors of each branch of `TInstruction` that is used by the
+ * aliased SSA stage.
+ * These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
+ * a class alias.
+ */
+module AliasedSSAInstructions {
+ class TPhiInstruction = TAliasedSSAPhiInstruction;
+
+ TPhiInstruction phiInstruction(
+ TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation
+ ) {
+ result = TAliasedSSAPhiInstruction(blockStartInstr, memoryLocation)
+ }
+
+ class TChiInstruction = TAliasedSSAChiInstruction;
+
+ TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
+ result = TAliasedSSAChiInstruction(primaryInstruction)
+ }
+
+ class TUnreachedInstruction = TAliasedSSAUnreachedInstruction;
+
+ TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) {
+ result = TAliasedSSAUnreachedInstruction(irFunc)
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionImports.qll
new file mode 100644
index 00000000000..e008ce7d8d3
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionImports.qll
@@ -0,0 +1,2 @@
+import semmle.code.cpp.ir.implementation.IRType as IRType
+import semmle.code.cpp.ir.implementation.Opcode as Opcode
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionInternal.qll
new file mode 100644
index 00000000000..adaaaca9cd8
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionInternal.qll
@@ -0,0 +1,4 @@
+import semmle.code.cpp.ir.internal.IRCppLanguage as Language
+import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
+import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConstruction as UnaliasedSSA
+import semmle.code.cpp.ir.implementation.aliased_ssa.internal.SSAConstruction as AliasedSSA
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll
index 49cb4dd6dc4..94ef73b2769 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll
@@ -31,10 +31,14 @@ class IRBlockBase extends TIRBlock {
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
) and
this =
- rank[result + 1](IRBlock funcBlock |
- funcBlock.getEnclosingFunction() = getEnclosingFunction()
+ rank[result + 1](IRBlock funcBlock, int sortOverride |
+ funcBlock.getEnclosingFunction() = getEnclosingFunction() and
+ // Ensure that the block containing `EnterFunction` always comes first.
+ if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction
+ then sortOverride = 0
+ else sortOverride = 1
|
- funcBlock order by funcBlock.getUniqueId()
+ funcBlock order by sortOverride, funcBlock.getUniqueId()
)
}
@@ -101,23 +105,24 @@ class IRBlock extends IRBlockBase {
private predicate startsBasicBlock(Instruction instr) {
not instr instanceof PhiInstruction and
- (
- count(Instruction predecessor | instr = predecessor.getASuccessor()) != 1 // Multiple predecessors or no predecessor
- or
- exists(Instruction predecessor |
- instr = predecessor.getASuccessor() and
- strictcount(Instruction other | other = predecessor.getASuccessor()) > 1
- ) // Predecessor has multiple successors
- or
- exists(Instruction predecessor, EdgeKind kind |
- instr = predecessor.getSuccessor(kind) and
- not kind instanceof GotoEdge
- ) // Incoming edge is not a GotoEdge
- or
- exists(Instruction predecessor |
- instr = Construction::getInstructionBackEdgeSuccessor(predecessor, _)
- ) // A back edge enters this instruction
- )
+ not adjacentInBlock(_, instr)
+}
+
+/** Holds if `i2` follows `i1` in a `IRBlock`. */
+private predicate adjacentInBlock(Instruction i1, Instruction i2) {
+ // - i2 must be the only successor of i1
+ i2 = unique(Instruction i | i = i1.getASuccessor()) and
+ // - i1 must be the only predecessor of i2
+ i1 = unique(Instruction i | i.getASuccessor() = i2) and
+ // - The edge between the two must be a GotoEdge. We just check that one
+ // exists since we've already checked that it's unique.
+ exists(GotoEdge edgeKind | exists(i1.getSuccessor(edgeKind))) and
+ // - The edge must not be a back edge. This means we get the same back edges
+ // in the basic-block graph as we do in the raw CFG.
+ not exists(Construction::getInstructionBackEdgeSuccessor(i1, _))
+ // This predicate could be simplified to remove one of the `unique`s if we
+ // were willing to rely on the CFG being well-formed and thus never having
+ // more than one successor to an instruction that has a `GotoEdge` out of it.
}
private predicate isEntryBlock(TIRBlock block) {
@@ -129,12 +134,6 @@ private module Cached {
cached
newtype TIRBlock = MkIRBlock(Instruction firstInstr) { startsBasicBlock(firstInstr) }
- /** Holds if `i2` follows `i1` in a `IRBlock`. */
- private predicate adjacentInBlock(Instruction i1, Instruction i2) {
- exists(GotoEdge edgeKind | i2 = i1.getSuccessor(edgeKind)) and
- not startsBasicBlock(i2)
- }
-
/** Holds if `i` is the `index`th instruction the block starting with `first`. */
private Instruction getInstructionFromFirst(Instruction first, int index) =
shortestDistances(startsBasicBlock/1, adjacentInBlock/2)(first, result, index)
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRConsistency.ql b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRConsistency.ql
new file mode 100644
index 00000000000..0d8dd13543b
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRConsistency.ql
@@ -0,0 +1,8 @@
+/**
+ * @name Raw IR Consistency Check
+ * @description Performs consistency checks on the Intermediate Representation. This query should have no results.
+ * @kind table
+ * @id cpp/raw-ir-consistency-check
+ */
+
+import IRConsistency
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll
new file mode 100644
index 00000000000..6a87b9b4b5f
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll
@@ -0,0 +1,497 @@
+private import IR
+import InstructionConsistency // module is below
+import IRTypeConsistency // module is in IRType.qll
+
+module InstructionConsistency {
+ private import internal.InstructionImports as Imports
+ private import Imports::OperandTag
+ private import Imports::Overlap
+ private import internal.IRInternal
+
+ private newtype TOptionalIRFunction =
+ TPresentIRFunction(IRFunction irFunc) or
+ TMissingIRFunction()
+
+ /**
+ * An `IRFunction` that might not exist. This is used so that we can produce consistency failures
+ * for IR that also incorrectly lacks a `getEnclosingIRFunction()`.
+ */
+ abstract private class OptionalIRFunction extends TOptionalIRFunction {
+ abstract string toString();
+
+ abstract Language::Location getLocation();
+ }
+
+ private class PresentIRFunction extends OptionalIRFunction, TPresentIRFunction {
+ private IRFunction irFunc;
+
+ PresentIRFunction() { this = TPresentIRFunction(irFunc) }
+
+ override string toString() {
+ result = concat(Language::getIdentityString(irFunc.getFunction()), "; ")
+ }
+
+ override Language::Location getLocation() {
+ // To avoid an overwhelming number of results when the extractor merges functions with the
+ // same name, just pick a single location.
+ result =
+ rank[1](Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString())
+ }
+ }
+
+ private class MissingIRFunction extends OptionalIRFunction, TMissingIRFunction {
+ override string toString() { result = "" }
+
+ override Language::Location getLocation() { result instanceof Language::UnknownDefaultLocation }
+ }
+
+ private OptionalIRFunction getInstructionIRFunction(Instruction instr) {
+ result = TPresentIRFunction(instr.getEnclosingIRFunction())
+ or
+ not exists(instr.getEnclosingIRFunction()) and result = TMissingIRFunction()
+ }
+
+ pragma[inline]
+ private OptionalIRFunction getInstructionIRFunction(Instruction instr, string irFuncText) {
+ result = getInstructionIRFunction(instr) and
+ irFuncText = result.toString()
+ }
+
+ private OptionalIRFunction getOperandIRFunction(Operand operand) {
+ result = TPresentIRFunction(operand.getEnclosingIRFunction())
+ or
+ not exists(operand.getEnclosingIRFunction()) and result = TMissingIRFunction()
+ }
+
+ pragma[inline]
+ private OptionalIRFunction getOperandIRFunction(Operand operand, string irFuncText) {
+ result = getOperandIRFunction(operand) and
+ irFuncText = result.toString()
+ }
+
+ private OptionalIRFunction getBlockIRFunction(IRBlock block) {
+ result = TPresentIRFunction(block.getEnclosingIRFunction())
+ or
+ not exists(block.getEnclosingIRFunction()) and result = TMissingIRFunction()
+ }
+
+ /**
+ * Holds if instruction `instr` is missing an expected operand with tag `tag`.
+ */
+ query predicate missingOperand(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(OperandTag tag |
+ instr.getOpcode().hasOperand(tag) and
+ not exists(NonPhiOperand operand |
+ operand = instr.getAnOperand() and
+ operand.getOperandTag() = tag
+ ) and
+ message =
+ "Instruction '" + instr.getOpcode().toString() +
+ "' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ /**
+ * Holds if instruction `instr` has an unexpected operand with tag `tag`.
+ */
+ query predicate unexpectedOperand(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(OperandTag tag |
+ exists(NonPhiOperand operand |
+ operand = instr.getAnOperand() and
+ operand.getOperandTag() = tag
+ ) and
+ not instr.getOpcode().hasOperand(tag) and
+ not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
+ not (
+ instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
+ ) and
+ not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) and
+ message =
+ "Instruction '" + instr.toString() + "' has unexpected operand '" + tag.toString() +
+ "' in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ /**
+ * Holds if instruction `instr` has multiple operands with tag `tag`.
+ */
+ query predicate duplicateOperand(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(OperandTag tag, int operandCount |
+ operandCount =
+ strictcount(NonPhiOperand operand |
+ operand = instr.getAnOperand() and
+ operand.getOperandTag() = tag
+ ) and
+ operandCount > 1 and
+ message =
+ "Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
+ " in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ /**
+ * Holds if `Phi` instruction `instr` is missing an operand corresponding to
+ * the predecessor block `pred`.
+ */
+ query predicate missingPhiOperand(
+ PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(IRBlock pred |
+ pred = instr.getBlock().getAPredecessor() and
+ not exists(PhiInputOperand operand |
+ operand = instr.getAnOperand() and
+ operand.getPredecessorBlock() = pred
+ ) and
+ message =
+ "Instruction '" + instr.toString() + "' is missing an operand for predecessor block '" +
+ pred.toString() + "' in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ query predicate missingOperandType(
+ Operand operand, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(Instruction use |
+ not exists(operand.getType()) and
+ use = operand.getUse() and
+ message =
+ "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
+ "' is missing a type in function '$@'." and
+ irFunc = getOperandIRFunction(operand, irFuncText)
+ )
+ }
+
+ query predicate duplicateChiOperand(
+ ChiInstruction chi, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ chi.getTotal() = chi.getPartial() and
+ message =
+ "Chi instruction for " + chi.getPartial().toString() +
+ " has duplicate operands in function '$@'." and
+ irFunc = getInstructionIRFunction(chi, irFuncText)
+ }
+
+ query predicate sideEffectWithoutPrimary(
+ SideEffectInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ not exists(instr.getPrimaryInstruction()) and
+ message =
+ "Side effect instruction '" + instr + "' is missing a primary instruction in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ }
+
+ /**
+ * Holds if an instruction, other than `ExitFunction`, has no successors.
+ */
+ query predicate instructionWithoutSuccessor(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ not exists(instr.getASuccessor()) and
+ not instr instanceof ExitFunctionInstruction and
+ // Phi instructions aren't linked into the instruction-level flow graph.
+ not instr instanceof PhiInstruction and
+ not instr instanceof UnreachedInstruction and
+ message = "Instruction '" + instr.toString() + "' has no successors in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ }
+
+ /**
+ * Holds if there are multiple edges of the same kind from `source`.
+ */
+ query predicate ambiguousSuccessors(
+ Instruction source, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(EdgeKind kind, int n |
+ n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
+ n > 1 and
+ message =
+ "Instruction '" + source.toString() + "' has " + n.toString() + " successors of kind '" +
+ kind.toString() + "' in function '$@'." and
+ irFunc = getInstructionIRFunction(source, irFuncText)
+ )
+ }
+
+ /**
+ * Holds if `instr` is part of a loop even though the AST of `instr`'s enclosing function
+ * contains no element that can cause loops.
+ */
+ query predicate unexplainedLoop(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(Language::Function f |
+ exists(IRBlock block |
+ instr.getBlock() = block and
+ block.getEnclosingFunction() = f and
+ block.getASuccessor+() = block
+ ) and
+ not Language::hasPotentialLoop(f) and
+ message =
+ "Instruction '" + instr.toString() + "' is part of an unexplained loop in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ /**
+ * Holds if a `Phi` instruction is present in a block with fewer than two
+ * predecessors.
+ */
+ query predicate unnecessaryPhiInstruction(
+ PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(int n |
+ n = count(instr.getBlock().getAPredecessor()) and
+ n < 2 and
+ message =
+ "Instruction '" + instr.toString() + "' is in a block with only " + n.toString() +
+ " predecessors in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ /**
+ * Holds if a memory operand is connected to a definition with an unmodeled result.
+ */
+ query predicate memoryOperandDefinitionIsUnmodeled(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(MemoryOperand operand, Instruction def |
+ operand = instr.getAnOperand() and
+ def = operand.getAnyDef() and
+ not def.isResultModeled() and
+ message =
+ "Memory operand definition on instruction '" + instr.toString() +
+ "' has unmodeled result in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ /**
+ * Holds if operand `operand` consumes a value that was defined in
+ * a different function.
+ */
+ query predicate operandAcrossFunctions(
+ Operand operand, string message, OptionalIRFunction useIRFunc, string useIRFuncText,
+ OptionalIRFunction defIRFunc, string defIRFuncText
+ ) {
+ exists(Instruction useInstr, Instruction defInstr |
+ operand.getUse() = useInstr and
+ operand.getAnyDef() = defInstr and
+ useIRFunc = getInstructionIRFunction(useInstr, useIRFuncText) and
+ defIRFunc = getInstructionIRFunction(defInstr, defIRFuncText) and
+ useIRFunc != defIRFunc and
+ message =
+ "Operand '" + operand.toString() + "' is used on instruction '" + useInstr.toString() +
+ "' in function '$@', but is defined on instruction '" + defInstr.toString() +
+ "' in function '$@'."
+ )
+ }
+
+ /**
+ * Holds if instruction `instr` is not in exactly one block.
+ */
+ query predicate instructionWithoutUniqueBlock(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(int blockCount |
+ blockCount = count(instr.getBlock()) and
+ blockCount != 1 and
+ message =
+ "Instruction '" + instr.toString() + "' is a member of " + blockCount.toString() +
+ " blocks in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ private predicate forwardEdge(IRBlock b1, IRBlock b2) {
+ b1.getASuccessor() = b2 and
+ not b1.getBackEdgeSuccessor(_) = b2
+ }
+
+ /**
+ * Holds if `f` contains a loop in which no edge is a back edge.
+ *
+ * This check ensures we don't have too _few_ back edges.
+ */
+ query predicate containsLoopOfForwardEdges(IRFunction f, string message) {
+ exists(IRBlock block |
+ forwardEdge+(block, block) and
+ block.getEnclosingIRFunction() = f and
+ message = "Function contains a loop consisting of only forward edges."
+ )
+ }
+
+ /**
+ * Holds if `block` is reachable from its function entry point but would not
+ * be reachable by traversing only forward edges. This check is skipped for
+ * functions containing `goto` statements as the property does not generally
+ * hold there.
+ *
+ * This check ensures we don't have too _many_ back edges.
+ */
+ query predicate lostReachability(
+ IRBlock block, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(IRFunction f, IRBlock entry |
+ entry = f.getEntryBlock() and
+ entry.getASuccessor+() = block and
+ not forwardEdge+(entry, block) and
+ not Language::hasGoto(f.getFunction()) and
+ message =
+ "Block '" + block.toString() +
+ "' is not reachable by traversing only forward edges in function '$@'." and
+ irFunc = TPresentIRFunction(f) and
+ irFuncText = irFunc.toString()
+ )
+ }
+
+ /**
+ * Holds if the number of back edges differs between the `Instruction` graph
+ * and the `IRBlock` graph.
+ */
+ query predicate backEdgeCountMismatch(OptionalIRFunction irFunc, string message) {
+ exists(int fromInstr, int fromBlock |
+ fromInstr =
+ count(Instruction i1, Instruction i2 |
+ getInstructionIRFunction(i1) = irFunc and i1.getBackEdgeSuccessor(_) = i2
+ ) and
+ fromBlock =
+ count(IRBlock b1, IRBlock b2 |
+ getBlockIRFunction(b1) = irFunc and b1.getBackEdgeSuccessor(_) = b2
+ ) and
+ fromInstr != fromBlock and
+ message =
+ "The instruction graph for function '" + irFunc.toString() + "' contains " +
+ fromInstr.toString() + " back edges, but the block graph contains " + fromBlock.toString()
+ + " back edges."
+ )
+ }
+
+ /**
+ * Gets the point in the function at which the specified operand is evaluated. For most operands,
+ * this is at the instruction that consumes the use. For a `PhiInputOperand`, the effective point
+ * of evaluation is at the end of the corresponding predecessor block.
+ */
+ private predicate pointOfEvaluation(Operand operand, IRBlock block, int index) {
+ block = operand.(PhiInputOperand).getPredecessorBlock() and
+ index = block.getInstructionCount()
+ or
+ exists(Instruction use |
+ use = operand.(NonPhiOperand).getUse() and
+ block.getInstruction(index) = use
+ )
+ }
+
+ /**
+ * Holds if `useOperand` has a definition that does not dominate the use.
+ */
+ query predicate useNotDominatedByDefinition(
+ Operand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
+ pointOfEvaluation(useOperand, useBlock, useIndex) and
+ defInstr = useOperand.getAnyDef() and
+ (
+ defInstr instanceof PhiInstruction and
+ defBlock = defInstr.getBlock() and
+ defIndex = -1
+ or
+ defBlock.getInstruction(defIndex) = defInstr
+ ) and
+ not (
+ defBlock.strictlyDominates(useBlock)
+ or
+ defBlock = useBlock and
+ defIndex < useIndex
+ ) and
+ message =
+ "Operand '" + useOperand.toString() +
+ "' is not dominated by its definition in function '$@'." and
+ irFunc = getOperandIRFunction(useOperand, irFuncText)
+ )
+ }
+
+ query predicate switchInstructionWithoutDefaultEdge(
+ SwitchInstruction switchInstr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ not exists(switchInstr.getDefaultSuccessor()) and
+ message =
+ "SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and
+ irFunc = getInstructionIRFunction(switchInstr, irFuncText)
+ }
+
+ /**
+ * Holds if `instr` is on the chain of chi/phi instructions for all aliased
+ * memory.
+ */
+ private predicate isOnAliasedDefinitionChain(Instruction instr) {
+ instr instanceof AliasedDefinitionInstruction
+ or
+ isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal())
+ or
+ isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
+ }
+
+ private predicate shouldBeConflated(Instruction instr) {
+ isOnAliasedDefinitionChain(instr)
+ or
+ instr.getOpcode() instanceof Opcode::InitializeNonLocal
+ }
+
+ query predicate notMarkedAsConflated(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ shouldBeConflated(instr) and
+ not instr.isResultConflated() and
+ message =
+ "Instruction '" + instr.toString() +
+ "' should be marked as having a conflated result in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ }
+
+ query predicate wronglyMarkedAsConflated(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ instr.isResultConflated() and
+ not shouldBeConflated(instr) and
+ message =
+ "Instruction '" + instr.toString() +
+ "' should not be marked as having a conflated result in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ }
+
+ query predicate invalidOverlap(
+ MemoryOperand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(Overlap overlap |
+ overlap = useOperand.getDefinitionOverlap() and
+ overlap instanceof MayPartiallyOverlap and
+ message =
+ "MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
+ overlap.toString() + "'." and
+ irFunc = getOperandIRFunction(useOperand, irFuncText)
+ )
+ }
+
+ query predicate nonUniqueEnclosingIRFunction(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(int irFuncCount |
+ irFuncCount = count(instr.getEnclosingIRFunction()) and
+ irFuncCount != 1 and
+ message =
+ "Instruction '" + instr.toString() + "' has " + irFuncCount.toString() +
+ " results for `getEnclosingIRFunction()` in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll
index 1e9c2d1d913..6b2d32af48c 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll
@@ -1,29 +1,12 @@
private import internal.IRInternal
+private import internal.IRFunctionImports as Imports
+import Imports::IRFunctionBase
import Instruction
-private newtype TIRFunction =
- MkIRFunction(Language::Function func) { Construction::functionHasIR(func) }
-
/**
- * Represents the IR for a function.
+ * The IR for a function.
*/
-class IRFunction extends TIRFunction {
- Language::Function func;
-
- IRFunction() { this = MkIRFunction(func) }
-
- final string toString() { result = "IR: " + func.toString() }
-
- /**
- * Gets the function whose IR is represented.
- */
- final Language::Function getFunction() { result = func }
-
- /**
- * Gets the location of the function.
- */
- final Language::Location getLocation() { result = func.getLocation() }
-
+class IRFunction extends IRFunctionBase {
/**
* Gets the entry point for this function.
*/
@@ -40,16 +23,6 @@ class IRFunction extends TIRFunction {
result.getEnclosingIRFunction() = this
}
- pragma[noinline]
- final UnmodeledDefinitionInstruction getUnmodeledDefinitionInstruction() {
- result.getEnclosingIRFunction() = this
- }
-
- pragma[noinline]
- final UnmodeledUseInstruction getUnmodeledUseInstruction() {
- result.getEnclosingIRFunction() = this
- }
-
/**
* Gets the single return instruction for this function.
*/
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.ql b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.ql
deleted file mode 100644
index a9d6b7943fe..00000000000
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.ql
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * @name Raw IR Sanity Check
- * @description Performs sanity checks on the Intermediate Representation. This query should have no results.
- * @kind table
- * @id cpp/raw-ir-sanity-check
- */
-
-import IRSanity
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
deleted file mode 100644
index 94d0192fe18..00000000000
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll
+++ /dev/null
@@ -1,320 +0,0 @@
-private import IR
-import InstructionSanity // module is below
-import IRTypeSanity // module is in IRType.qll
-
-module InstructionSanity {
- private import internal.InstructionImports as Imports
- private import Imports::OperandTag
- private import Imports::Overlap
- private import internal.IRInternal
-
- /**
- * Holds if instruction `instr` is missing an expected operand with tag `tag`.
- */
- query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) {
- exists(OperandTag tag |
- instr.getOpcode().hasOperand(tag) and
- not exists(NonPhiOperand operand |
- operand = instr.getAnOperand() and
- operand.getOperandTag() = tag
- ) and
- message =
- "Instruction '" + instr.getOpcode().toString() +
- "' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
- func = instr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
- )
- }
-
- /**
- * Holds if instruction `instr` has an unexpected operand with tag `tag`.
- */
- query predicate unexpectedOperand(Instruction instr, OperandTag tag) {
- exists(NonPhiOperand operand |
- operand = instr.getAnOperand() and
- operand.getOperandTag() = tag
- ) and
- not instr.getOpcode().hasOperand(tag) and
- not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
- not (
- instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
- ) and
- not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag)
- }
-
- /**
- * Holds if instruction `instr` has multiple operands with tag `tag`.
- */
- query predicate duplicateOperand(
- Instruction instr, string message, IRFunction func, string funcText
- ) {
- exists(OperandTag tag, int operandCount |
- operandCount =
- strictcount(NonPhiOperand operand |
- operand = instr.getAnOperand() and
- operand.getOperandTag() = tag
- ) and
- operandCount > 1 and
- not tag instanceof UnmodeledUseOperandTag and
- message =
- "Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
- " in function '$@'." and
- func = instr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
- )
- }
-
- /**
- * Holds if `Phi` instruction `instr` is missing an operand corresponding to
- * the predecessor block `pred`.
- */
- query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
- pred = instr.getBlock().getAPredecessor() and
- not exists(PhiInputOperand operand |
- operand = instr.getAnOperand() and
- operand.getPredecessorBlock() = pred
- )
- }
-
- query predicate missingOperandType(Operand operand, string message) {
- exists(Language::Function func, Instruction use |
- not exists(operand.getType()) and
- use = operand.getUse() and
- func = use.getEnclosingFunction() and
- message =
- "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
- "' missing type in function '" + Language::getIdentityString(func) + "'."
- )
- }
-
- 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.
- */
- query predicate instructionWithoutSuccessor(Instruction instr) {
- not exists(instr.getASuccessor()) and
- not instr instanceof ExitFunctionInstruction and
- // Phi instructions aren't linked into the instruction-level flow graph.
- not instr instanceof PhiInstruction and
- not instr instanceof UnreachedInstruction
- }
-
- /**
- * Holds if there are multiple (`n`) edges of kind `kind` from `source`,
- * where `target` is among the targets of those edges.
- */
- query predicate ambiguousSuccessors(Instruction source, EdgeKind kind, int n, Instruction target) {
- n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
- n > 1 and
- source.getSuccessor(kind) = target
- }
-
- /**
- * Holds if `instr` in `f` is part of a loop even though the AST of `f`
- * contains no element that can cause loops.
- */
- query predicate unexplainedLoop(Language::Function f, Instruction instr) {
- exists(IRBlock block |
- instr.getBlock() = block and
- block.getEnclosingFunction() = f and
- block.getASuccessor+() = block
- ) and
- not Language::hasPotentialLoop(f)
- }
-
- /**
- * Holds if a `Phi` instruction is present in a block with fewer than two
- * predecessors.
- */
- query predicate unnecessaryPhiInstruction(PhiInstruction instr) {
- count(instr.getBlock().getAPredecessor()) < 2
- }
-
- /**
- * Holds if operand `operand` consumes a value that was defined in
- * a different function.
- */
- query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) {
- operand.getUse() = instr and
- operand.getAnyDef() = defInstr and
- instr.getEnclosingIRFunction() != defInstr.getEnclosingIRFunction()
- }
-
- /**
- * Holds if instruction `instr` is not in exactly one block.
- */
- query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) {
- blockCount = count(instr.getBlock()) and
- blockCount != 1
- }
-
- private predicate forwardEdge(IRBlock b1, IRBlock b2) {
- b1.getASuccessor() = b2 and
- not b1.getBackEdgeSuccessor(_) = b2
- }
-
- /**
- * Holds if `f` contains a loop in which no edge is a back edge.
- *
- * This check ensures we don't have too _few_ back edges.
- */
- query predicate containsLoopOfForwardEdges(IRFunction f) {
- exists(IRBlock block |
- forwardEdge+(block, block) and
- block.getEnclosingIRFunction() = f
- )
- }
-
- /**
- * Holds if `block` is reachable from its function entry point but would not
- * be reachable by traversing only forward edges. This check is skipped for
- * functions containing `goto` statements as the property does not generally
- * hold there.
- *
- * This check ensures we don't have too _many_ back edges.
- */
- query predicate lostReachability(IRBlock block) {
- exists(IRFunction f, IRBlock entry |
- entry = f.getEntryBlock() and
- entry.getASuccessor+() = block and
- not forwardEdge+(entry, block) and
- not Language::hasGoto(f.getFunction())
- )
- }
-
- /**
- * Holds if the number of back edges differs between the `Instruction` graph
- * and the `IRBlock` graph.
- */
- query predicate backEdgeCountMismatch(Language::Function f, int fromInstr, int fromBlock) {
- fromInstr =
- count(Instruction i1, Instruction i2 |
- i1.getEnclosingFunction() = f and i1.getBackEdgeSuccessor(_) = i2
- ) and
- fromBlock =
- count(IRBlock b1, IRBlock b2 |
- b1.getEnclosingFunction() = f and b1.getBackEdgeSuccessor(_) = b2
- ) and
- fromInstr != fromBlock
- }
-
- /**
- * Gets the point in the function at which the specified operand is evaluated. For most operands,
- * this is at the instruction that consumes the use. For a `PhiInputOperand`, the effective point
- * of evaluation is at the end of the corresponding predecessor block.
- */
- private predicate pointOfEvaluation(Operand operand, IRBlock block, int index) {
- block = operand.(PhiInputOperand).getPredecessorBlock() and
- index = block.getInstructionCount()
- or
- exists(Instruction use |
- use = operand.(NonPhiOperand).getUse() and
- block.getInstruction(index) = use
- )
- }
-
- /**
- * Holds if `useOperand` has a definition that does not dominate the use.
- */
- query predicate useNotDominatedByDefinition(
- Operand useOperand, string message, IRFunction func, string funcText
- ) {
- 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
- (
- defInstr instanceof PhiInstruction and
- defBlock = defInstr.getBlock() and
- defIndex = -1
- or
- defBlock.getInstruction(defIndex) = defInstr
- ) and
- not (
- defBlock.strictlyDominates(useBlock)
- or
- defBlock = useBlock and
- defIndex < useIndex
- ) and
- message =
- "Operand '" + useOperand.toString() +
- "' is not dominated by its definition in function '$@'." and
- func = useOperand.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
- )
- }
-
- query predicate switchInstructionWithoutDefaultEdge(
- SwitchInstruction switchInstr, string message, IRFunction func, string funcText
- ) {
- not exists(switchInstr.getDefaultSuccessor()) and
- message =
- "SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and
- func = switchInstr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
- }
-
- /**
- * Holds if `instr` is on the chain of chi/phi instructions for all aliased
- * memory.
- */
- private predicate isOnAliasedDefinitionChain(Instruction instr) {
- instr instanceof AliasedDefinitionInstruction
- or
- isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal())
- or
- isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
- }
-
- private predicate shouldBeConflated(Instruction instr) {
- isOnAliasedDefinitionChain(instr)
- or
- instr instanceof UnmodeledDefinitionInstruction
- or
- instr.getOpcode() instanceof Opcode::InitializeNonLocal
- }
-
- query predicate notMarkedAsConflated(Instruction instr) {
- shouldBeConflated(instr) and
- not instr.isResultConflated()
- }
-
- query predicate wronglyMarkedAsConflated(Instruction instr) {
- instr.isResultConflated() and
- not shouldBeConflated(instr)
- }
-
- query predicate invalidOverlap(
- MemoryOperand useOperand, string message, IRFunction func, string funcText
- ) {
- exists(Overlap overlap |
- overlap = useOperand.getDefinitionOverlap() and
- overlap instanceof MayPartiallyOverlap and
- message =
- "MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
- overlap.toString() + "'." and
- func = useOperand.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
- )
- }
-}
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 0d5e7fe595c..a01bd2dc79a 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll
@@ -217,10 +217,23 @@ class IRThrowVariable extends IRTempVariable {
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
* function that accepts a variable number of arguments.
*/
-class IREllipsisVariable extends IRTempVariable {
+class IREllipsisVariable extends IRTempVariable, IRParameter {
IREllipsisVariable() { tag = EllipsisTempVar() }
final override string toString() { result = "#ellipsis" }
+
+ final override int getIndex() { result = func.getNumberOfParameters() }
+}
+
+/**
+ * A temporary variable generated to hold the `this` pointer.
+ */
+class IRThisVariable extends IRTempVariable, IRParameter {
+ IRThisVariable() { tag = ThisTempVar() }
+
+ final override string toString() { result = "#this" }
+
+ final override int getIndex() { result = -1 }
}
/**
@@ -265,3 +278,29 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
}
+
+/**
+ * An IR variable which acts like a function parameter, including positional parameters and the
+ * temporary variables generated for `this` and ellipsis parameters.
+ */
+class IRParameter extends IRAutomaticVariable {
+ IRParameter() {
+ this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter
+ or
+ this = TIRTempVariable(_, _, ThisTempVar(), _)
+ or
+ this = TIRTempVariable(_, _, EllipsisTempVar(), _)
+ }
+
+ /**
+ * Gets the zero-based index of this parameter. The `this` parameter has index -1.
+ */
+ int getIndex() { none() }
+}
+
+/**
+ * An IR variable representing a positional parameter.
+ */
+class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable {
+ final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll
index 38216872f2b..409577d3e46 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll
@@ -29,7 +29,13 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil
/**
* Represents a single operation in the IR.
*/
-class Instruction extends Construction::TInstruction {
+class Instruction extends Construction::TStageInstruction {
+ Instruction() {
+ // The base `TStageInstruction` type is a superset of the actual instructions appearing in this
+ // stage. This call lets the stage filter out the ones that are not reused from raw IR.
+ Construction::hasInstruction(this)
+ }
+
final string toString() { result = getOpcode().toString() + ": " + getAST().toString() }
/**
@@ -190,17 +196,18 @@ class Instruction extends Construction::TInstruction {
final Language::Location getLocation() { result = getAST().getLocation() }
/**
- * Gets the `Expr` whose result is computed by this instruction, if any.
+ * Gets the `Expr` whose result is computed by this instruction, if any. The `Expr` may be a
+ * conversion.
*/
final Language::Expr getConvertedResultExpression() {
- result = Construction::getInstructionConvertedResultExpression(this)
+ result = Raw::getInstructionConvertedResultExpression(this)
}
/**
- * Gets the unconverted `Expr` whose result is computed by this instruction, if any.
+ * Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any.
*/
final Language::Expr getUnconvertedResultExpression() {
- result = Construction::getInstructionUnconvertedResultExpression(this)
+ result = Raw::getInstructionUnconvertedResultExpression(this)
}
final Language::LanguageType getResultLanguageType() {
@@ -211,6 +218,7 @@ class Instruction extends Construction::TInstruction {
* Gets the type of the result produced by this instruction. If the instruction does not produce
* a result, its result type will be `IRVoidType`.
*/
+ cached
final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
/**
@@ -249,7 +257,7 @@ class Instruction extends Construction::TInstruction {
* result of the `Load` instruction is a prvalue of type `int`, representing
* the integer value loaded from variable `x`.
*/
- final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
+ final predicate isGLValue() { getResultLanguageType().hasType(_, true) }
/**
* Gets the size of the result produced by this instruction, in bytes. If the
@@ -258,7 +266,7 @@ class Instruction extends Construction::TInstruction {
* If `this.isGLValue()` holds for this instruction, the value of
* `getResultSize()` will always be the size of a pointer.
*/
- final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
+ final int getResultSize() { result = getResultLanguageType().getByteSize() }
/**
* Gets the opcode that specifies the operation performed by this instruction.
@@ -319,8 +327,7 @@ class Instruction extends Construction::TInstruction {
/**
* Holds if the result of this instruction is precisely modeled in SSA. Always
* holds for a register result. For a memory result, a modeled result is
- * connected to its actual uses. An unmodeled result is connected to the
- * `UnmodeledUse` instruction.
+ * connected to its actual uses. An unmodeled result has no uses.
*
* For example:
* ```
@@ -395,7 +402,7 @@ class Instruction extends Construction::TInstruction {
class VariableInstruction extends Instruction {
IRVariable var;
- VariableInstruction() { var = Construction::getInstructionVariable(this) }
+ VariableInstruction() { var = Raw::getInstructionVariable(this) }
override string getImmediateString() { result = var.toString() }
@@ -410,7 +417,7 @@ class VariableInstruction extends Instruction {
class FieldInstruction extends Instruction {
Language::Field field;
- FieldInstruction() { field = Construction::getInstructionField(this) }
+ FieldInstruction() { field = Raw::getInstructionField(this) }
final override string getImmediateString() { result = field.toString() }
@@ -420,7 +427,7 @@ class FieldInstruction extends Instruction {
class FunctionInstruction extends Instruction {
Language::Function funcSymbol;
- FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) }
+ FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) }
final override string getImmediateString() { result = funcSymbol.toString() }
@@ -430,7 +437,7 @@ class FunctionInstruction extends Instruction {
class ConstantValueInstruction extends Instruction {
string value;
- ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) }
+ ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) }
final override string getImmediateString() { result = value }
@@ -440,7 +447,7 @@ class ConstantValueInstruction extends Instruction {
class IndexedInstruction extends Instruction {
int index;
- IndexedInstruction() { index = Construction::getInstructionIndex(this) }
+ IndexedInstruction() { index = Raw::getInstructionIndex(this) }
final override string getImmediateString() { result = index.toString() }
@@ -541,6 +548,11 @@ class ReturnIndirectionInstruction extends VariableInstruction {
* function.
*/
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
+
+ /**
+ * Holds if this instruction is the return indirection for `this`.
+ */
+ final predicate isThisIndirection() { var instanceof IRThisVariable }
}
class CopyInstruction extends Instruction {
@@ -584,9 +596,9 @@ class ConditionalBranchInstruction extends Instruction {
final Instruction getCondition() { result = getConditionOperand().getDef() }
- final Instruction getTrueSuccessor() { result = getSuccessor(trueEdge()) }
+ final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) }
- final Instruction getFalseSuccessor() { result = getSuccessor(falseEdge()) }
+ final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) }
}
class ExitFunctionInstruction extends Instruction {
@@ -598,11 +610,16 @@ class ConstantInstruction extends ConstantValueInstruction {
}
class IntegerConstantInstruction extends ConstantInstruction {
- IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType }
+ IntegerConstantInstruction() {
+ exists(IRType resultType |
+ resultType = getResultIRType() and
+ (resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
+ )
+ }
}
class FloatConstantInstruction extends ConstantInstruction {
- FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
+ FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType }
}
class StringConstantInstruction extends VariableInstruction {
@@ -699,7 +716,7 @@ class PointerArithmeticInstruction extends BinaryInstruction {
PointerArithmeticInstruction() {
getOpcode() instanceof PointerArithmeticOpcode and
- elementSize = Construction::getInstructionElementSize(this)
+ elementSize = Raw::getInstructionElementSize(this)
}
final override string getImmediateString() { result = elementSize.toString() }
@@ -748,7 +765,7 @@ class InheritanceConversionInstruction extends UnaryInstruction {
Language::Class derivedClass;
InheritanceConversionInstruction() {
- Construction::getInstructionInheritance(this, baseClass, derivedClass)
+ Raw::getInstructionInheritance(this, baseClass, derivedClass)
}
final override string getImmediateString() {
@@ -906,7 +923,7 @@ class SwitchInstruction extends Instruction {
final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) }
- final Instruction getDefaultSuccessor() { result = getSuccessor(defaultEdge()) }
+ final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) }
}
/**
@@ -1211,7 +1228,7 @@ class CatchByTypeInstruction extends CatchInstruction {
CatchByTypeInstruction() {
getOpcode() instanceof Opcode::CatchByType and
- exceptionType = Construction::getInstructionExceptionType(this)
+ exceptionType = Raw::getInstructionExceptionType(this)
}
final override string getImmediateString() { result = exceptionType.toString() }
@@ -1229,10 +1246,6 @@ class CatchAnyInstruction extends CatchInstruction {
CatchAnyInstruction() { getOpcode() instanceof Opcode::CatchAny }
}
-class UnmodeledDefinitionInstruction extends Instruction {
- UnmodeledDefinitionInstruction() { getOpcode() instanceof Opcode::UnmodeledDefinition }
-}
-
/**
* An instruction that initializes all escaped memory.
*/
@@ -1247,12 +1260,6 @@ class AliasedUseInstruction extends Instruction {
AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse }
}
-class UnmodeledUseInstruction extends Instruction {
- UnmodeledUseInstruction() { getOpcode() instanceof Opcode::UnmodeledUse }
-
- override string getOperandsString() { result = "mu*" }
-}
-
/**
* An instruction representing the choice of one of multiple input values based on control flow.
*
@@ -1367,7 +1374,7 @@ class BuiltInOperationInstruction extends Instruction {
BuiltInOperationInstruction() {
getOpcode() instanceof BuiltInOperationOpcode and
- operation = Construction::getInstructionBuiltInOperation(this)
+ operation = Raw::getInstructionBuiltInOperation(this)
}
final Language::BuiltInOperation getBuiltInOperation() { result = operation }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll
index 1836f4c4b2f..f82704094c8 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
@@ -14,16 +14,8 @@ private newtype TOperand =
not Construction::isInCycle(useInstr) and
strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1
} or
- TNonPhiMemoryOperand(
- Instruction useInstr, MemoryOperandTag tag, Instruction defInstr, Overlap overlap
- ) {
- defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and
- not Construction::isInCycle(useInstr) and
- (
- strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
- or
- tag instanceof UnmodeledUseOperandTag
- )
+ TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
+ useInstr.getOpcode().hasOperand(tag)
} or
TPhiOperand(
PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
@@ -31,6 +23,57 @@ private newtype TOperand =
defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
}
+/**
+ * Base class for all register operands. This is a placeholder for the IPA union type that we will
+ * eventually use for this purpose.
+ */
+private class RegisterOperandBase extends TRegisterOperand {
+ /** Gets a textual representation of this element. */
+ abstract string toString();
+}
+
+/**
+ * Returns the register operand with the specified parameters.
+ */
+private RegisterOperandBase registerOperand(
+ Instruction useInstr, RegisterOperandTag tag, Instruction defInstr
+) {
+ result = TRegisterOperand(useInstr, tag, defInstr)
+}
+
+/**
+ * Base class for all non-Phi memory operands. This is a placeholder for the IPA union type that we
+ * will eventually use for this purpose.
+ */
+private class NonPhiMemoryOperandBase extends TNonPhiMemoryOperand {
+ /** Gets a textual representation of this element. */
+ abstract string toString();
+}
+
+/**
+ * Returns the non-Phi memory operand with the specified parameters.
+ */
+private NonPhiMemoryOperandBase nonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
+ result = TNonPhiMemoryOperand(useInstr, tag)
+}
+
+/**
+ * Base class for all Phi operands. This is a placeholder for the IPA union type that we will
+ * eventually use for this purpose.
+ */
+private class PhiOperandBase extends TPhiOperand {
+ abstract string toString();
+}
+
+/**
+ * Returns the Phi operand with the specified parameters.
+ */
+private PhiOperandBase phiOperand(
+ Instruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
+) {
+ result = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
+}
+
/**
* A source operand of an `Instruction`. The operand represents a value consumed by the instruction.
*/
@@ -104,7 +147,17 @@ class Operand extends TOperand {
* For example: `this:r3_5`
*/
final string getDumpString() {
- result = getDumpLabel() + getInexactSpecifier() + getAnyDef().getResultId()
+ result = getDumpLabel() + getInexactSpecifier() + getDefinitionId()
+ }
+
+ /**
+ * Gets a string containing the identifier of the definition of this use, or `m?` if the
+ * definition is not modeled in SSA.
+ */
+ private string getDefinitionId() {
+ result = getAnyDef().getResultId()
+ or
+ not exists(getAnyDef()) and result = "m?"
}
/**
@@ -169,8 +222,8 @@ class Operand extends TOperand {
*/
class MemoryOperand extends Operand {
MemoryOperand() {
- this = TNonPhiMemoryOperand(_, _, _, _) or
- this = TPhiOperand(_, _, _, _)
+ this instanceof NonPhiMemoryOperandBase or
+ this instanceof PhiOperandBase
}
/**
@@ -204,18 +257,15 @@ class MemoryOperand extends Operand {
*/
class NonPhiOperand extends Operand {
Instruction useInstr;
- Instruction defInstr;
OperandTag tag;
NonPhiOperand() {
- this = TRegisterOperand(useInstr, tag, defInstr) or
- this = TNonPhiMemoryOperand(useInstr, tag, defInstr, _)
+ this = registerOperand(useInstr, tag, _) or
+ this = nonPhiMemoryOperand(useInstr, tag)
}
final override Instruction getUse() { result = useInstr }
- final override Instruction getAnyDef() { result = defInstr }
-
final override string getDumpLabel() { result = tag.getLabel() }
final override int getDumpSortOrder() { result = tag.getSortOrder() }
@@ -226,8 +276,15 @@ class NonPhiOperand extends Operand {
/**
* An operand that consumes a register (non-memory) result.
*/
-class RegisterOperand extends NonPhiOperand, TRegisterOperand {
+class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
override RegisterOperandTag tag;
+ Instruction defInstr;
+
+ RegisterOperand() { this = registerOperand(useInstr, tag, defInstr) }
+
+ final override string toString() { result = tag.toString() }
+
+ final override Instruction getAnyDef() { result = defInstr }
final override Overlap getDefinitionOverlap() {
// All register results overlap exactly with their uses.
@@ -235,13 +292,25 @@ class RegisterOperand extends NonPhiOperand, TRegisterOperand {
}
}
-class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOperand {
+class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase {
override MemoryOperandTag tag;
- Overlap overlap;
- NonPhiMemoryOperand() { this = TNonPhiMemoryOperand(useInstr, tag, defInstr, overlap) }
+ NonPhiMemoryOperand() { this = nonPhiMemoryOperand(useInstr, tag) }
- final override Overlap getDefinitionOverlap() { result = overlap }
+ final override string toString() { result = tag.toString() }
+
+ final override Instruction getAnyDef() {
+ result = unique(Instruction defInstr | hasDefinition(defInstr, _))
+ }
+
+ final override Overlap getDefinitionOverlap() { hasDefinition(_, result) }
+
+ pragma[noinline]
+ private predicate hasDefinition(Instruction defInstr, Overlap overlap) {
+ defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and
+ not Construction::isInCycle(useInstr) and
+ strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
+ }
}
class TypedOperand extends NonPhiMemoryOperand {
@@ -258,8 +327,6 @@ class TypedOperand extends NonPhiMemoryOperand {
*/
class AddressOperand extends RegisterOperand {
override AddressOperandTag tag;
-
- override string toString() { result = "Address" }
}
/**
@@ -268,8 +335,6 @@ class AddressOperand extends RegisterOperand {
*/
class BufferSizeOperand extends RegisterOperand {
override BufferSizeOperandTag tag;
-
- override string toString() { result = "BufferSize" }
}
/**
@@ -278,8 +343,6 @@ class BufferSizeOperand extends RegisterOperand {
*/
class LoadOperand extends TypedOperand {
override LoadOperandTag tag;
-
- override string toString() { result = "Load" }
}
/**
@@ -287,8 +350,6 @@ class LoadOperand extends TypedOperand {
*/
class StoreValueOperand extends RegisterOperand {
override StoreValueOperandTag tag;
-
- override string toString() { result = "StoreValue" }
}
/**
@@ -296,8 +357,6 @@ class StoreValueOperand extends RegisterOperand {
*/
class UnaryOperand extends RegisterOperand {
override UnaryOperandTag tag;
-
- override string toString() { result = "Unary" }
}
/**
@@ -305,8 +364,6 @@ class UnaryOperand extends RegisterOperand {
*/
class LeftOperand extends RegisterOperand {
override LeftOperandTag tag;
-
- override string toString() { result = "Left" }
}
/**
@@ -314,8 +371,6 @@ class LeftOperand extends RegisterOperand {
*/
class RightOperand extends RegisterOperand {
override RightOperandTag tag;
-
- override string toString() { result = "Right" }
}
/**
@@ -323,18 +378,6 @@ class RightOperand extends RegisterOperand {
*/
class ConditionOperand extends RegisterOperand {
override ConditionOperandTag tag;
-
- override string toString() { result = "Condition" }
-}
-
-/**
- * An operand of the special `UnmodeledUse` instruction, representing a value
- * whose set of uses is unknown.
- */
-class UnmodeledUseOperand extends NonPhiMemoryOperand {
- override UnmodeledUseOperandTag tag;
-
- override string toString() { result = "UnmodeledUse" }
}
/**
@@ -342,8 +385,6 @@ class UnmodeledUseOperand extends NonPhiMemoryOperand {
*/
class CallTargetOperand extends RegisterOperand {
override CallTargetOperandTag tag;
-
- override string toString() { result = "CallTarget" }
}
/**
@@ -361,8 +402,6 @@ class ArgumentOperand extends RegisterOperand {
*/
class ThisArgumentOperand extends ArgumentOperand {
override ThisArgumentOperandTag tag;
-
- override string toString() { result = "ThisArgument" }
}
/**
@@ -370,34 +409,27 @@ class ThisArgumentOperand extends ArgumentOperand {
*/
class PositionalArgumentOperand extends ArgumentOperand {
override PositionalArgumentOperandTag tag;
- int argIndex;
-
- PositionalArgumentOperand() { argIndex = tag.getArgIndex() }
-
- override string toString() { result = "Arg(" + argIndex + ")" }
/**
* Gets the zero-based index of the argument.
*/
- final int getIndex() { result = argIndex }
+ final int getIndex() { result = tag.getArgIndex() }
}
class SideEffectOperand extends TypedOperand {
override SideEffectOperandTag tag;
-
- override string toString() { result = "SideEffect" }
}
/**
* An operand of a `PhiInstruction`.
*/
-class PhiInputOperand extends MemoryOperand, TPhiOperand {
+class PhiInputOperand extends MemoryOperand, PhiOperandBase {
PhiInstruction useInstr;
Instruction defInstr;
IRBlock predecessorBlock;
Overlap overlap;
- PhiInputOperand() { this = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap) }
+ PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
override string toString() { result = "Phi" }
@@ -427,8 +459,6 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
class ChiTotalOperand extends NonPhiMemoryOperand {
override ChiTotalOperandTag tag;
- override string toString() { result = "ChiTotal" }
-
final override MemoryAccessKind getMemoryAccess() { result instanceof ChiTotalMemoryAccess }
}
@@ -438,7 +468,5 @@ class ChiTotalOperand extends NonPhiMemoryOperand {
class ChiPartialOperand extends NonPhiMemoryOperand {
override ChiPartialOperandTag tag;
- override string toString() { result = "ChiPartial" }
-
final override MemoryAccessKind getMemoryAccess() { result instanceof ChiPartialMemoryAccess }
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll
index 40076b817a1..76f52f8334a 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll
@@ -14,8 +14,7 @@ int getConstantValue(Instruction instr) {
or
exists(PhiInstruction phi |
phi = instr and
- result = max(Operand op | op = phi.getAnInputOperand() | getConstantValue(op.getDef())) and
- result = min(Operand op | op = phi.getAnInputOperand() | getConstantValue(op.getDef()))
+ result = unique(Operand op | op = phi.getAnInputOperand() | getConstantValue(op.getDef()))
)
}
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 13d19587135..796fb792366 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
@@ -56,7 +56,7 @@ class ValueNumber extends TValueNumber {
or
this instanceof TInitializeParameterValueNumber and result = "InitializeParameter"
or
- this instanceof TInitializeThisValueNumber and result = "InitializeThis"
+ this instanceof TConstantValueNumber and result = "Constant"
or
this instanceof TStringConstantValueNumber and result = "StringConstant"
or
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll
index 169b0ef7ccf..2467d961892 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll
@@ -7,7 +7,6 @@ newtype TValueNumber =
TInitializeParameterValueNumber(IRFunction irFunc, Language::AST var) {
initializeParameterValueNumber(_, irFunc, var)
} or
- TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or
TConstantValueNumber(IRFunction irFunc, IRType type, string value) {
constantValueNumber(_, irFunc, type, value)
} or
@@ -79,8 +78,6 @@ private predicate numberableInstruction(Instruction instr) {
or
instr instanceof InitializeParameterInstruction
or
- instr instanceof InitializeThisInstruction
- or
instr instanceof ConstantInstruction
or
instr instanceof StringConstantInstruction
@@ -132,10 +129,6 @@ private predicate initializeParameterValueNumber(
instr.getIRVariable().getAST() = var
}
-private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
- instr.getEnclosingIRFunction() = irFunc
-}
-
private predicate constantValueNumber(
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
@@ -268,9 +261,6 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
result = TInitializeParameterValueNumber(irFunc, var)
)
or
- initializeThisValueNumber(instr, irFunc) and
- result = TInitializeThisValueNumber(irFunc)
- or
exists(string value, IRType type |
constantValueNumber(instr, irFunc, type, value) and
result = TConstantValueNumber(irFunc, type, value)
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 18a77452929..15200491e98 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll
@@ -1,6 +1,9 @@
private import cpp
import semmle.code.cpp.ir.implementation.raw.IR
private import semmle.code.cpp.ir.implementation.internal.OperandTag
+private import semmle.code.cpp.ir.implementation.internal.IRFunctionBase
+private import semmle.code.cpp.ir.implementation.internal.TInstruction
+private import semmle.code.cpp.ir.implementation.internal.TIRVariable
private import semmle.code.cpp.ir.internal.CppType
private import semmle.code.cpp.ir.internal.Overlap
private import semmle.code.cpp.ir.internal.TempVariableTag
@@ -12,23 +15,30 @@ private import TranslatedStmt
private import TranslatedFunction
TranslatedElement getInstructionTranslatedElement(Instruction instruction) {
- instruction = MkInstruction(result, _)
+ instruction = TRawInstruction(result, _)
}
-InstructionTag getInstructionTag(Instruction instruction) { instruction = MkInstruction(_, result) }
-
-import Cached
+InstructionTag getInstructionTag(Instruction instruction) {
+ instruction = TRawInstruction(_, result)
+}
+/**
+ * Provides the portion of the parameterized IR interface that is used to construct the initial
+ * "raw" stage of the IR. The other stages of the IR do not expose these predicates.
+ */
cached
-private module Cached {
+module Raw {
+ class InstructionTag1 = TranslatedElement;
+
+ class InstructionTag2 = InstructionTag;
+
cached
predicate functionHasIR(Function func) { exists(getTranslatedFunction(func)) }
cached
- newtype TInstruction =
- MkInstruction(TranslatedElement element, InstructionTag tag) {
- element.hasInstruction(_, tag, _)
- }
+ predicate hasInstruction(TranslatedElement element, InstructionTag tag) {
+ element.hasInstruction(_, tag, _)
+ }
cached
predicate hasUserVariable(Function func, Variable var, CppType type) {
@@ -59,236 +69,7 @@ private module Cached {
}
cached
- predicate hasModeledMemoryResult(Instruction instruction) { none() }
-
- cached
- predicate hasConflatedMemoryResult(Instruction instruction) {
- instruction instanceof UnmodeledDefinitionInstruction
- or
- instruction instanceof AliasedDefinitionInstruction
- or
- instruction.getOpcode() instanceof Opcode::InitializeNonLocal
- }
-
- cached
- Expr getInstructionConvertedResultExpression(Instruction instruction) {
- exists(TranslatedExpr translatedExpr |
- translatedExpr = getTranslatedExpr(result) and
- instruction = translatedExpr.getResult() and
- // Only associate `instruction` with this expression if the translated
- // expression actually produced the instruction; not if it merely
- // forwarded the result of another translated expression.
- instruction = translatedExpr.getInstruction(_)
- )
- }
-
- cached
- Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
- result = getInstructionConvertedResultExpression(instruction).getUnconverted()
- }
-
- cached
- Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) {
- result =
- getInstructionTranslatedElement(instruction)
- .getInstructionOperand(getInstructionTag(instruction), tag)
- }
-
- cached
- Instruction getMemoryOperandDefinition(
- Instruction instruction, MemoryOperandTag tag, Overlap overlap
- ) {
- result =
- getInstructionTranslatedElement(instruction)
- .getInstructionOperand(getInstructionTag(instruction), tag) and
- overlap instanceof MustTotallyOverlap
- }
-
- /** Gets a non-phi instruction that defines an operand of `instr`. */
- private Instruction getNonPhiOperandDef(Instruction instr) {
- result = getRegisterOperandDefinition(instr, _)
- or
- result = getMemoryOperandDefinition(instr, _, _)
- }
-
- /**
- * Gets a non-phi instruction that defines an operand of `instr` but only if
- * both `instr` and the result have neighbor on the other side of the edge
- * between them. This is a necessary condition for being in a cycle, and it
- * removes about two thirds of the tuples that would otherwise be in this
- * predicate.
- */
- private Instruction getNonPhiOperandDefOfIntermediate(Instruction instr) {
- result = getNonPhiOperandDef(instr) and
- exists(getNonPhiOperandDef(result)) and
- instr = getNonPhiOperandDef(_)
- }
-
- /**
- * Holds if `instr` is part of a cycle in the operand graph that doesn't go
- * through a phi instruction and therefore should be impossible.
- *
- * If such cycles are present, either due to a programming error in the IR
- * generation or due to a malformed database, it can cause infinite loops in
- * analyses that assume a cycle-free graph of non-phi operands. Therefore it's
- * better to remove these operands than to leave cycles in the operand graph.
- */
- pragma[noopt]
- cached
- predicate isInCycle(Instruction instr) {
- instr instanceof Instruction and
- getNonPhiOperandDefOfIntermediate+(instr) = instr
- }
-
- cached
- CppType getInstructionOperandType(Instruction instruction, TypedOperandTag tag) {
- // For all `LoadInstruction`s, the operand type of the `LoadOperand` is the same as
- // the result type of the load.
- result = instruction.(LoadInstruction).getResultLanguageType()
- or
- not instruction instanceof LoadInstruction and
- result =
- getInstructionTranslatedElement(instruction)
- .getInstructionOperandType(getInstructionTag(instruction), tag)
- }
-
- cached
- Instruction getPhiOperandDefinition(
- PhiInstruction instruction, IRBlock predecessorBlock, Overlap overlap
- ) {
- none()
- }
-
- cached
- Instruction getPhiInstructionBlockStart(PhiInstruction instr) { none() }
-
- cached
- Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
- result =
- getInstructionTranslatedElement(instruction)
- .getInstructionSuccessor(getInstructionTag(instruction), kind)
- }
-
- /**
- * Holds if the CFG edge (`sourceElement`, `sourceTag`) ---`kind`-->
- * `targetInstruction` is a back edge under the condition that
- * `requiredAncestor` is an ancestor of `sourceElement`.
- */
- private predicate backEdgeCandidate(
- TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor,
- Instruction targetInstruction, EdgeKind kind
- ) {
- // While loop:
- // Any edge from within the body of the loop to the condition of the loop
- // is a back edge. This includes edges from `continue` and the fall-through
- // edge(s) after the last instruction(s) in the body.
- exists(TranslatedWhileStmt s |
- targetInstruction = s.getFirstConditionInstruction() and
- targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
- requiredAncestor = s.getBody()
- )
- or
- // Do-while loop:
- // The back edge should be the edge(s) from the condition to the
- // body. This ensures that it's the back edge that will be pruned in a `do
- // { ... } while (0)` statement. Note that all `continue` statements in a
- // do-while loop produce forward edges.
- exists(TranslatedDoStmt s |
- targetInstruction = s.getBody().getFirstInstruction() and
- targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
- requiredAncestor = s.getCondition()
- )
- or
- // For loop:
- // Any edge from within the body or update of the loop to the condition of
- // the loop is a back edge. When there is no loop update expression, this
- // includes edges from `continue` and the fall-through edge(s) after the
- // last instruction(s) in the body. A for loop may not have a condition, in
- // which case `getFirstConditionInstruction` returns the body instead.
- exists(TranslatedForStmt s |
- targetInstruction = s.getFirstConditionInstruction() and
- targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
- (
- requiredAncestor = s.getUpdate()
- or
- not exists(s.getUpdate()) and
- requiredAncestor = s.getBody()
- )
- )
- or
- // Range-based for loop:
- // Any edge from within the update of the loop to the condition of
- // the loop is a back edge.
- exists(TranslatedRangeBasedForStmt s |
- targetInstruction = s.getCondition().getFirstInstruction() and
- targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
- requiredAncestor = s.getUpdate()
- )
- }
-
- private predicate jumpSourceHasAncestor(TranslatedElement jumpSource, TranslatedElement ancestor) {
- backEdgeCandidate(jumpSource, _, _, _, _) and
- ancestor = jumpSource
- or
- // For performance, we don't want a fastTC here
- jumpSourceHasAncestor(jumpSource, ancestor.getAChild())
- }
-
- cached
- Instruction getInstructionBackEdgeSuccessor(Instruction instruction, EdgeKind kind) {
- exists(
- TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor
- |
- backEdgeCandidate(sourceElement, sourceTag, requiredAncestor, result, kind) and
- jumpSourceHasAncestor(sourceElement, requiredAncestor) and
- instruction = sourceElement.getInstruction(sourceTag)
- )
- or
- // Goto statement:
- // As a conservative approximation, any edge out of `goto` is a back edge
- // unless it goes strictly forward in the program text. A `goto` whose
- // source and target are both inside a macro will be seen as having the
- // same location for source and target, so we conservatively assume that
- // such a `goto` creates a back edge.
- exists(TranslatedElement s, GotoStmt goto |
- not isStrictlyForwardGoto(goto) and
- goto = s.getAST() and
- exists(InstructionTag tag |
- result = s.getInstructionSuccessor(tag, kind) and
- instruction = s.getInstruction(tag)
- )
- )
- }
-
- /** Holds if `goto` jumps strictly forward in the program text. */
- private predicate isStrictlyForwardGoto(GotoStmt goto) {
- goto.getLocation().isBefore(goto.getTarget().getLocation())
- }
-
- cached
- Locatable getInstructionAST(Instruction instruction) {
- result = getInstructionTranslatedElement(instruction).getAST()
- }
-
- cached
- CppType getInstructionResultType(Instruction instruction) {
- getInstructionTranslatedElement(instruction)
- .hasInstruction(_, getInstructionTag(instruction), result)
- }
-
- cached
- Opcode getInstructionOpcode(Instruction instruction) {
- getInstructionTranslatedElement(instruction)
- .hasInstruction(result, getInstructionTag(instruction), _)
- }
-
- cached
- IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
- result.getFunction() = getInstructionTranslatedElement(instruction).getFunction()
- }
-
- cached
- IRVariable getInstructionVariable(Instruction instruction) {
+ TIRVariable getInstructionVariable(Instruction instruction) {
exists(TranslatedElement element, InstructionTag tag |
element = getInstructionTranslatedElement(instruction) and
tag = getInstructionTag(instruction) and
@@ -301,10 +82,9 @@ private module Cached {
cached
Field getInstructionField(Instruction instruction) {
- exists(TranslatedElement element, InstructionTag tag |
- instructionOrigin(instruction, element, tag) and
- result = element.getInstructionField(tag)
- )
+ result =
+ getInstructionTranslatedElement(instruction)
+ .getInstructionField(getInstructionTag(instruction))
}
cached
@@ -323,10 +103,9 @@ private module Cached {
cached
int getInstructionIndex(Instruction instruction) {
- exists(TranslatedElement element, InstructionTag tag |
- instructionOrigin(instruction, element, tag) and
- result = element.getInstructionIndex(tag)
- )
+ result =
+ getInstructionTranslatedElement(instruction)
+ .getInstructionIndex(getInstructionTag(instruction))
}
cached
@@ -349,20 +128,11 @@ private module Cached {
.getInstructionInheritance(getInstructionTag(instruction), baseClass, derivedClass)
}
- pragma[noinline]
- private predicate instructionOrigin(
- Instruction instruction, TranslatedElement element, InstructionTag tag
- ) {
- element = getInstructionTranslatedElement(instruction) and
- tag = getInstructionTag(instruction)
- }
-
cached
int getInstructionElementSize(Instruction instruction) {
- exists(TranslatedElement element, InstructionTag tag |
- instructionOrigin(instruction, element, tag) and
- result = element.getInstructionElementSize(tag)
- )
+ result =
+ getInstructionTranslatedElement(instruction)
+ .getInstructionElementSize(getInstructionTag(instruction))
}
cached
@@ -371,22 +141,225 @@ private module Cached {
}
cached
- int getInstructionResultSize(Instruction instruction) {
- exists(TranslatedElement element, InstructionTag tag |
- instructionOrigin(instruction, element, tag) and
- result = element.getInstructionResultSize(tag)
+ Expr getInstructionConvertedResultExpression(Instruction instruction) {
+ exists(TranslatedExpr translatedExpr |
+ translatedExpr = getTranslatedExpr(result) and
+ instruction = translatedExpr.getResult() and
+ // Only associate `instruction` with this expression if the translated
+ // expression actually produced the instruction; not if it merely
+ // forwarded the result of another translated expression.
+ instruction = translatedExpr.getInstruction(_)
)
}
cached
- Instruction getPrimaryInstructionForSideEffect(Instruction instruction) {
- exists(TranslatedElement element, InstructionTag tag |
- instructionOrigin(instruction, element, tag) and
- result = element.getPrimaryInstructionForSideEffect(tag)
- )
+ Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
+ result = getInstructionConvertedResultExpression(instruction).getUnconverted()
}
}
+class TStageInstruction = TRawInstruction;
+
+predicate hasInstruction(TRawInstruction instr) { any() }
+
+predicate hasModeledMemoryResult(Instruction instruction) { none() }
+
+predicate hasConflatedMemoryResult(Instruction instruction) {
+ instruction instanceof AliasedDefinitionInstruction
+ or
+ instruction.getOpcode() instanceof Opcode::InitializeNonLocal
+}
+
+Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) {
+ result =
+ getInstructionTranslatedElement(instruction)
+ .getInstructionRegisterOperand(getInstructionTag(instruction), tag)
+}
+
+Instruction getMemoryOperandDefinition(
+ Instruction instruction, MemoryOperandTag tag, Overlap overlap
+) {
+ none()
+}
+
+/** Gets a non-phi instruction that defines an operand of `instr`. */
+private Instruction getNonPhiOperandDef(Instruction instr) {
+ result = getRegisterOperandDefinition(instr, _)
+ or
+ result = getMemoryOperandDefinition(instr, _, _)
+}
+
+/**
+ * Gets a non-phi instruction that defines an operand of `instr` but only if
+ * both `instr` and the result have neighbor on the other side of the edge
+ * between them. This is a necessary condition for being in a cycle, and it
+ * removes about two thirds of the tuples that would otherwise be in this
+ * predicate.
+ */
+private Instruction getNonPhiOperandDefOfIntermediate(Instruction instr) {
+ result = getNonPhiOperandDef(instr) and
+ exists(getNonPhiOperandDef(result)) and
+ instr = getNonPhiOperandDef(_)
+}
+
+/**
+ * Holds if `instr` is part of a cycle in the operand graph that doesn't go
+ * through a phi instruction and therefore should be impossible.
+ *
+ * If such cycles are present, either due to a programming error in the IR
+ * generation or due to a malformed database, it can cause infinite loops in
+ * analyses that assume a cycle-free graph of non-phi operands. Therefore it's
+ * better to remove these operands than to leave cycles in the operand graph.
+ */
+pragma[noopt]
+predicate isInCycle(Instruction instr) {
+ instr instanceof Instruction and
+ getNonPhiOperandDefOfIntermediate+(instr) = instr
+}
+
+CppType getInstructionOperandType(Instruction instruction, TypedOperandTag tag) {
+ // For all `LoadInstruction`s, the operand type of the `LoadOperand` is the same as
+ // the result type of the load.
+ tag instanceof LoadOperandTag and
+ result = instruction.(LoadInstruction).getResultLanguageType()
+ or
+ not instruction instanceof LoadInstruction and
+ result =
+ getInstructionTranslatedElement(instruction)
+ .getInstructionMemoryOperandType(getInstructionTag(instruction), tag)
+}
+
+Instruction getPhiOperandDefinition(
+ PhiInstruction instruction, IRBlock predecessorBlock, Overlap overlap
+) {
+ none()
+}
+
+Instruction getPhiInstructionBlockStart(PhiInstruction instr) { none() }
+
+Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
+ result =
+ getInstructionTranslatedElement(instruction)
+ .getInstructionSuccessor(getInstructionTag(instruction), kind)
+}
+
+/**
+ * Holds if the CFG edge (`sourceElement`, `sourceTag`) ---`kind`-->
+ * `targetInstruction` is a back edge under the condition that
+ * `requiredAncestor` is an ancestor of `sourceElement`.
+ */
+private predicate backEdgeCandidate(
+ TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor,
+ Instruction targetInstruction, EdgeKind kind
+) {
+ // While loop:
+ // Any edge from within the body of the loop to the condition of the loop
+ // is a back edge. This includes edges from `continue` and the fall-through
+ // edge(s) after the last instruction(s) in the body.
+ exists(TranslatedWhileStmt s |
+ targetInstruction = s.getFirstConditionInstruction() and
+ targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
+ requiredAncestor = s.getBody()
+ )
+ or
+ // Do-while loop:
+ // The back edge should be the edge(s) from the condition to the
+ // body. This ensures that it's the back edge that will be pruned in a `do
+ // { ... } while (0)` statement. Note that all `continue` statements in a
+ // do-while loop produce forward edges.
+ exists(TranslatedDoStmt s |
+ targetInstruction = s.getBody().getFirstInstruction() and
+ targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
+ requiredAncestor = s.getCondition()
+ )
+ or
+ // For loop:
+ // Any edge from within the body or update of the loop to the condition of
+ // the loop is a back edge. When there is no loop update expression, this
+ // includes edges from `continue` and the fall-through edge(s) after the
+ // last instruction(s) in the body. A for loop may not have a condition, in
+ // which case `getFirstConditionInstruction` returns the body instead.
+ exists(TranslatedForStmt s |
+ targetInstruction = s.getFirstConditionInstruction() and
+ targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
+ (
+ requiredAncestor = s.getUpdate()
+ or
+ not exists(s.getUpdate()) and
+ requiredAncestor = s.getBody()
+ )
+ )
+ or
+ // Range-based for loop:
+ // Any edge from within the update of the loop to the condition of
+ // the loop is a back edge.
+ exists(TranslatedRangeBasedForStmt s |
+ targetInstruction = s.getCondition().getFirstInstruction() and
+ targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
+ requiredAncestor = s.getUpdate()
+ )
+}
+
+private predicate jumpSourceHasAncestor(TranslatedElement jumpSource, TranslatedElement ancestor) {
+ backEdgeCandidate(jumpSource, _, _, _, _) and
+ ancestor = jumpSource
+ or
+ // For performance, we don't want a fastTC here
+ jumpSourceHasAncestor(jumpSource, ancestor.getAChild())
+}
+
+Instruction getInstructionBackEdgeSuccessor(Instruction instruction, EdgeKind kind) {
+ exists(
+ TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor
+ |
+ backEdgeCandidate(sourceElement, sourceTag, requiredAncestor, result, kind) and
+ jumpSourceHasAncestor(sourceElement, requiredAncestor) and
+ instruction = sourceElement.getInstruction(sourceTag)
+ )
+ or
+ // Goto statement:
+ // As a conservative approximation, any edge out of `goto` is a back edge
+ // unless it goes strictly forward in the program text. A `goto` whose
+ // source and target are both inside a macro will be seen as having the
+ // same location for source and target, so we conservatively assume that
+ // such a `goto` creates a back edge.
+ exists(TranslatedElement s, GotoStmt goto |
+ not isStrictlyForwardGoto(goto) and
+ goto = s.getAST() and
+ exists(InstructionTag tag |
+ result = s.getInstructionSuccessor(tag, kind) and
+ instruction = s.getInstruction(tag)
+ )
+ )
+}
+
+/** Holds if `goto` jumps strictly forward in the program text. */
+private predicate isStrictlyForwardGoto(GotoStmt goto) {
+ goto.getLocation().isBefore(goto.getTarget().getLocation())
+}
+
+Locatable getInstructionAST(TStageInstruction instr) {
+ result = getInstructionTranslatedElement(instr).getAST()
+}
+
+CppType getInstructionResultType(TStageInstruction instr) {
+ getInstructionTranslatedElement(instr).hasInstruction(_, getInstructionTag(instr), result)
+}
+
+Opcode getInstructionOpcode(TStageInstruction instr) {
+ getInstructionTranslatedElement(instr).hasInstruction(result, getInstructionTag(instr), _)
+}
+
+IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) {
+ result.getFunction() = getInstructionTranslatedElement(instr).getFunction()
+}
+
+Instruction getPrimaryInstructionForSideEffect(SideEffectInstruction instruction) {
+ result =
+ getInstructionTranslatedElement(instruction)
+ .getPrimaryInstructionForSideEffect(getInstructionTag(instruction))
+}
+
import CachedForDebugging
cached
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll
new file mode 100644
index 00000000000..8ec63b7c1cb
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll
@@ -0,0 +1 @@
+import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRInternal.qll
index 8fd2f662f34..82cc38ac092 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRInternal.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRInternal.qll
@@ -1,3 +1,4 @@
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
import IRConstruction as Construction
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration
+import IRConstruction::Raw as Raw
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionTag.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionTag.qll
index 4ddef00ee7b..122a23b76a0 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
@@ -2,7 +2,6 @@ private import cpp
newtype TInstructionTag =
OnlyInstructionTag() or // Single instruction (not including implicit Load)
- InitializeThisTag() or
InitializerVariableAddressTag() or
InitializerLoadStringTag() or
InitializerStoreTag() or
@@ -28,8 +27,6 @@ newtype TInstructionTag =
ReturnValueAddressTag() or
ReturnTag() or
ExitFunctionTag() or
- UnmodeledDefinitionTag() or
- UnmodeledUseTag() or
AliasedDefinitionTag() or
InitializeNonLocalTag() or
AliasedUseTag() or
@@ -72,7 +69,9 @@ newtype TInstructionTag =
VarArgsMoveNextTag() or
VarArgsVAListStoreTag() or
AsmTag() or
- AsmInputTag(int elementIndex) { exists(AsmStmt asm | exists(asm.getChild(elementIndex))) }
+ AsmInputTag(int elementIndex) { exists(AsmStmt asm | exists(asm.getChild(elementIndex))) } or
+ ThisAddressTag() or
+ ThisLoadTag()
class InstructionTag extends TInstructionTag {
final string toString() { result = "Tag" }
@@ -127,10 +126,6 @@ string getInstructionTagId(TInstructionTag tag) {
or
tag = ExitFunctionTag() and result = "ExitFunc"
or
- tag = UnmodeledDefinitionTag() and result = "UnmodeledDef"
- or
- tag = UnmodeledUseTag() and result = "UnmodeledUse"
- or
tag = AliasedDefinitionTag() and result = "AliasedDef"
or
tag = InitializeNonLocalTag() and result = "InitNonLocal"
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 22f104e12c8..821bf94709a 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
@@ -94,7 +94,7 @@ abstract class TranslatedCall extends TranslatedExpr {
)
}
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = CallTag() and
(
operandTag instanceof CallTargetOperandTag and
@@ -108,14 +108,11 @@ abstract class TranslatedCall extends TranslatedExpr {
result = getArgument(argTag.getArgIndex()).getResult()
)
)
- or
- tag = CallSideEffectTag() and
- hasSideEffect() and
- operandTag instanceof SideEffectOperandTag and
- result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
}
- final override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
+ final override CppType getInstructionMemoryOperandType(
+ InstructionTag tag, TypedOperandTag operandTag
+ ) {
tag = CallSideEffectTag() and
hasSideEffect() and
operandTag instanceof SideEffectOperandTag and
@@ -375,13 +372,13 @@ class TranslatedAllocationSideEffects extends TranslatedSideEffects,
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = OnlyInstructionTag() and
- kind = gotoEdge() and
+ kind = EdgeKind::gotoEdge() and
if exists(getChild(0))
then result = getChild(0).getFirstInstruction()
else result = getParent().getChildSuccessor(this)
}
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = OnlyInstructionTag() and
operandTag = addressOperand() and
result = getPrimaryInstructionForSideEffect(OnlyInstructionTag())
@@ -437,7 +434,7 @@ class TranslatedStructorCallSideEffects extends TranslatedCallSideEffects {
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag instanceof OnlyInstructionTag and
operandTag instanceof AddressOperandTag and
result = getParent().(TranslatedStructorCall).getQualifierResult()
@@ -513,17 +510,12 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff
kind instanceof GotoEdge
}
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(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
@@ -531,7 +523,8 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff
.getFullyConverted()).getResult()
}
- override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
+ override CppType getInstructionMemoryOperandType(InstructionTag tag, TypedOperandTag operandTag) {
+ not isWrite() and
if hasSpecificReadSideEffect(any(BufferAccessOpcode op))
then
result = getUnknownType() and
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 e2fc86db7ce..0779d6fbda5 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
@@ -182,7 +182,7 @@ class TranslatedValueCondition extends TranslatedCondition, TTranslatedValueCond
)
}
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = ValueConditionConditionalBranchTag() and
operandTag instanceof ConditionOperandTag and
result = getValueExpr().getResult()
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 6cc7fd7853f..de63b81c876 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
@@ -181,14 +181,11 @@ class TranslatedStaticLocalVariableDeclarationEntry extends TranslatedDeclaratio
tag = DynamicInitializationFlagConstantTag() and result = "1"
}
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = DynamicInitializationFlagLoadTag() and
(
operandTag instanceof AddressOperandTag and
result = getInstruction(DynamicInitializationFlagAddressTag())
- or
- operandTag instanceof LoadOperandTag and
- result = getTranslatedFunction(var.getFunction()).getUnmodeledDefinitionInstruction()
)
or
tag = DynamicInitializationConditionalBranchTag() and
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 2f143bf02ca..8bf5fa9d44b 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
@@ -200,7 +200,11 @@ private predicate usedAsCondition(Expr expr) {
or
exists(IfStmt ifStmt | ifStmt.getCondition().getFullyConverted() = expr)
or
- exists(ConditionalExpr condExpr | condExpr.getCondition().getFullyConverted() = expr)
+ exists(ConditionalExpr condExpr |
+ // The two-operand form of `ConditionalExpr` treats its condition as a value, since it needs to
+ // be reused as a value if the condition is true.
+ condExpr.getCondition().getFullyConverted() = expr and not condExpr.isTwoOperand()
+ )
or
exists(NotExpr notExpr |
notExpr.getOperand().getFullyConverted() = expr and
@@ -396,6 +400,9 @@ newtype TTranslatedElement =
TTranslatedConstructorInitList(Function func) { translateFunction(func) } or
// A destructor destruction list
TTranslatedDestructorDestructionList(Function func) { translateFunction(func) } or
+ TTranslatedThisParameter(Function func) {
+ translateFunction(func) and func.isMember() and not func.isStatic()
+ } or
// A function parameter
TTranslatedParameter(Parameter param) {
exists(Function func |
@@ -408,8 +415,11 @@ newtype TTranslatedElement =
} or
TTranslatedEllipsisParameter(Function func) { translateFunction(func) and func.isVarargs() } or
TTranslatedReadEffects(Function func) { translateFunction(func) } or
+ TTranslatedThisReadEffect(Function func) {
+ translateFunction(func) and func.isMember() and not func.isStatic()
+ } or
// The read side effects in a function's return block
- TTranslatedReadEffect(Parameter param) {
+ TTranslatedParameterReadEffect(Parameter param) {
translateFunction(param.getFunction()) and
exists(Type t | t = param.getUnspecifiedType() |
t instanceof ArrayType or
@@ -456,7 +466,7 @@ newtype TTranslatedElement =
)
} or
// The side effects of an allocation, i.e. `new`, `new[]` or `malloc`
- TTranslatedAllocationSideEffects(AllocationExpr expr) or
+ TTranslatedAllocationSideEffects(AllocationExpr expr) { not ignoreExpr(expr) } or
// A precise side effect of an argument to a `Call`
TTranslatedArgumentSideEffect(Call call, Expr expr, int n, boolean isWrite) {
(
@@ -695,12 +705,8 @@ abstract class TranslatedElement extends TTranslatedElement {
int getInstructionElementSize(InstructionTag tag) { none() }
/**
- * If the instruction specified by `tag` has a result of type `UnknownType`,
- * gets the size of the result in bytes. If the result does not have a knonwn
- * constant size, this predicate does not hold.
+ * Holds if the generated IR refers to an opaque type with size `byteSize`.
*/
- int getInstructionResultSize(InstructionTag tag) { none() }
-
predicate needsUnknownOpaqueType(int byteSize) { none() }
/**
@@ -732,12 +738,12 @@ abstract class TranslatedElement extends TTranslatedElement {
* Gets the instruction whose result is consumed as an operand of the
* instruction specified by `tag`, with the operand specified by `operandTag`.
*/
- Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { none() }
+ Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) { none() }
/**
* Gets the type of the memory operand specified by `operandTag` on the the instruction specified by `tag`.
*/
- CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { none() }
+ CppType getInstructionMemoryOperandType(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 b8ede454ba2..75e70d1986f 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
@@ -183,7 +183,7 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext,
)
}
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = ConditionValueTrueStoreTag() and
(
operandTag instanceof AddressOperandTag and
@@ -206,9 +206,6 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext,
(
operandTag instanceof AddressOperandTag and
result = getInstruction(ConditionValueResultTempAddressTag())
- or
- operandTag instanceof LoadOperandTag and
- result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
)
}
@@ -282,14 +279,11 @@ class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
override Instruction getResult() { result = getInstruction(LoadTag()) }
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = LoadTag() and
(
operandTag instanceof AddressOperandTag and
result = getOperand().getResult()
- or
- operandTag instanceof LoadOperandTag and
- result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
)
}
@@ -332,7 +326,7 @@ class TranslatedResultCopy extends TranslatedExpr, TTranslatedResultCopy {
override Instruction getResult() { result = getInstruction(ResultCopyTag()) }
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = ResultCopyTag() and
operandTag instanceof UnaryOperandTag and
result = getOperand().getResult()
@@ -369,7 +363,9 @@ class TranslatedCommaExpr extends TranslatedNonConstantExpr {
none()
}
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { none() }
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
+ none()
+ }
private TranslatedExpr getLeftOperand() {
result = getTranslatedExpr(expr.getLeftOperand().getFullyConverted())
@@ -429,7 +425,7 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
resultType = getTypeForPRValue(expr.getType())
}
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = CrementOpTag() and
(
operandTag instanceof LeftOperandTag and
@@ -580,7 +576,7 @@ class TranslatedArrayExpr extends TranslatedNonConstantExpr {
resultType = getTypeForGLValue(expr.getType())
}
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = OnlyInstructionTag() and
(
operandTag instanceof LeftOperandTag and
@@ -622,7 +618,7 @@ abstract class TranslatedTransparentExpr extends TranslatedNonConstantExpr {
final override Instruction getResult() { result = getOperand().getResult() }
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
none()
}
@@ -668,31 +664,40 @@ class TranslatedThisExpr extends TranslatedNonConstantExpr {
final override TranslatedElement getChild(int id) { none() }
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
- tag = OnlyInstructionTag() and
- opcode instanceof Opcode::CopyValue and
+ tag = ThisAddressTag() and
+ opcode instanceof Opcode::VariableAddress and
+ resultType = getTypeForGLValue(any(UnknownType t))
+ or
+ tag = ThisLoadTag() and
+ opcode instanceof Opcode::Load and
resultType = getResultType()
}
- final override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) }
+ final override Instruction getResult() { result = getInstruction(ThisLoadTag()) }
- final override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
+ final override Instruction getFirstInstruction() { result = getInstruction(ThisAddressTag()) }
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
kind instanceof GotoEdge and
- tag = OnlyInstructionTag() and
+ tag = ThisAddressTag() and
+ result = getInstruction(ThisLoadTag())
+ or
+ kind instanceof GotoEdge and
+ tag = ThisLoadTag() and
result = getParent().getChildSuccessor(this)
}
final override Instruction getChildSuccessor(TranslatedElement child) { none() }
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
- tag = OnlyInstructionTag() and
- operandTag instanceof UnaryOperandTag and
- result = getInitializeThisInstruction()
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
+ tag = ThisLoadTag() and
+ operandTag instanceof AddressOperandTag and
+ result = getInstruction(ThisAddressTag())
}
- private Instruction getInitializeThisInstruction() {
- result = getTranslatedFunction(expr.getEnclosingFunction()).getInitializeThisInstruction()
+ override IRVariable getInstructionVariable(InstructionTag tag) {
+ tag = ThisAddressTag() and
+ result = this.getEnclosingFunction().getThisVariable()
}
}
@@ -729,7 +734,9 @@ class TranslatedNonFieldVariableAccess extends TranslatedVariableAccess {
else result = getInstruction(OnlyInstructionTag())
}
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { none() }
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
+ none()
+ }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
@@ -748,7 +755,7 @@ class TranslatedFieldAccess extends TranslatedVariableAccess {
override Instruction getFirstInstruction() { result = getQualifier().getFirstInstruction() }
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = OnlyInstructionTag() and
operandTag instanceof UnaryOperandTag and
result = getQualifier().getResult()
@@ -822,7 +829,7 @@ abstract class TranslatedConstantExpr extends TranslatedCoreExpr, TTranslatedVal
final override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) }
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
none()
}
@@ -906,7 +913,7 @@ class TranslatedUnaryExpr extends TranslatedSingleInstructionExpr {
child = getOperand() and result = getInstruction(OnlyInstructionTag())
}
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = OnlyInstructionTag() and
result = getOperand().getResult() and
operandTag instanceof UnaryOperandTag
@@ -960,7 +967,7 @@ abstract class TranslatedSingleInstructionConversion extends TranslatedConversio
override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) }
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = OnlyInstructionTag() and
operandTag instanceof UnaryOperandTag and
result = getOperand().getResult()
@@ -1070,7 +1077,7 @@ class TranslatedBoolConversion extends TranslatedConversion {
override Instruction getResult() { result = getInstruction(BoolConversionCompareTag()) }
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = BoolConversionCompareTag() and
(
operandTag instanceof LeftOperandTag and
@@ -1100,13 +1107,36 @@ private Opcode binaryBitwiseOpcode(BinaryBitwiseOperation expr) {
}
private Opcode binaryArithmeticOpcode(BinaryArithmeticOperation expr) {
- expr instanceof AddExpr and result instanceof Opcode::Add
+ (
+ expr instanceof AddExpr
+ or
+ expr instanceof ImaginaryRealAddExpr
+ or
+ expr instanceof RealImaginaryAddExpr
+ ) and
+ result instanceof Opcode::Add
or
- expr instanceof SubExpr and result instanceof Opcode::Sub
+ (
+ expr instanceof SubExpr
+ or
+ expr instanceof ImaginaryRealSubExpr
+ or
+ expr instanceof RealImaginarySubExpr
+ ) and
+ result instanceof Opcode::Sub
or
- expr instanceof MulExpr and result instanceof Opcode::Mul
+ (
+ expr instanceof MulExpr
+ or
+ expr instanceof ImaginaryMulExpr
+ ) and
+ result instanceof Opcode::Mul
or
- expr instanceof DivExpr and result instanceof Opcode::Div
+ (
+ expr instanceof DivExpr or
+ expr instanceof ImaginaryDivExpr
+ ) and
+ result instanceof Opcode::Div
or
expr instanceof RemExpr and result instanceof Opcode::Rem
or
@@ -1149,7 +1179,7 @@ class TranslatedBinaryOperation extends TranslatedSingleInstructionExpr {
id = 1 and result = getRightOperand()
}
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = OnlyInstructionTag() and
if swapOperandsOnOp()
then (
@@ -1283,7 +1313,7 @@ class TranslatedAssignExpr extends TranslatedNonConstantExpr {
resultType = getTypeForPRValue(expr.getType()) // Always a prvalue
}
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = AssignmentStoreTag() and
(
operandTag instanceof AddressOperandTag and
@@ -1459,7 +1489,7 @@ class TranslatedAssignOperation extends TranslatedNonConstantExpr {
result = getElementSize(expr.getType())
}
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
leftOperandNeedsConversion() and
tag = AssignOperationConvertLeftTag() and
operandTag instanceof UnaryOperandTag and
@@ -1603,7 +1633,7 @@ class TranslatedNonConstantAllocationSize extends TranslatedAllocationSize {
result = expr.getAllocatedElementType().getSize().toString()
}
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = AllocationSizeTag() and
(
operandTag instanceof LeftOperandTag and result = getInstruction(AllocationExtentConvertTag())
@@ -1668,7 +1698,8 @@ class TranslatedAllocatorCall extends TTranslatedAllocatorCall, TranslatedDirect
else
if index = 1 and expr.hasAlignedAllocation()
then result = getTranslatedExpr(expr.getAlignmentArgument())
- else result = getTranslatedExpr(expr.getAllocatorCall().getArgument(index))
+ else
+ result = getTranslatedExpr(expr.getAllocatorCall().getArgument(index).getFullyConverted())
}
}
@@ -1719,7 +1750,7 @@ class TranslatedDestructorFieldDestruction extends TranslatedNonConstantExpr, St
final override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = OnlyInstructionTag() and
operandTag instanceof UnaryOperandTag and
result = getTranslatedFunction(expr.getEnclosingFunction()).getInitializeThisInstruction()
@@ -1735,20 +1766,20 @@ class TranslatedDestructorFieldDestruction extends TranslatedNonConstantExpr, St
private TranslatedExpr getDestructorCall() { result = getTranslatedExpr(expr.getExpr()) }
}
-class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionContext {
+/**
+ * The IR translation of the `?:` operator. This class has the portions of the implementation that
+ * are shared between the standard three-operand form (`a ? b : c`) and the GCC-extension
+ * two-operand form (`a ?: c`).
+ */
+abstract class TranslatedConditionalExpr extends TranslatedNonConstantExpr {
override ConditionalExpr expr;
- final override TranslatedElement getChild(int id) {
- id = 0 and result = getCondition()
- or
- id = 1 and result = getThen()
- or
- id = 2 and result = getElse()
- }
-
- override Instruction getFirstInstruction() { result = getCondition().getFirstInstruction() }
-
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
+ // Note that the ternary flavor needs no explicit `ConditionalBranch` instruction here, because
+ // the condition is a `TranslatedCondition`, which will simply connect the successor edges of
+ // the condition directly to the appropriate then/else block via
+ // `getChild[True|False]Successor()`.
+ // The binary flavor will override this predicate to add the `ConditionalBranch`.
not resultIsVoid() and
(
(
@@ -1809,7 +1840,7 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont
)
}
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
not resultIsVoid() and
(
not thenIsVoid() and
@@ -1836,20 +1867,17 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont
(
operandTag instanceof AddressOperandTag and
result = getInstruction(ConditionValueResultTempAddressTag())
- or
- operandTag instanceof LoadOperandTag and
- result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
)
)
}
- override predicate hasTempVariable(TempVariableTag tag, CppType type) {
+ final override predicate hasTempVariable(TempVariableTag tag, CppType type) {
not resultIsVoid() and
tag = ConditionValueTempVar() and
type = getResultType()
}
- override IRVariable getInstructionVariable(InstructionTag tag) {
+ final override IRVariable getInstructionVariable(InstructionTag tag) {
not resultIsVoid() and
(
tag = ConditionValueTrueTempAddressTag() or
@@ -1859,25 +1887,75 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont
result = getTempVariable(ConditionValueTempVar())
}
- override Instruction getResult() {
+ final override Instruction getResult() {
not resultIsVoid() and
result = getInstruction(ConditionValueResultLoadTag())
}
override Instruction getChildSuccessor(TranslatedElement child) {
+ child = getElse() and
+ if elseIsVoid()
+ then result = getParent().getChildSuccessor(this)
+ else result = getInstruction(ConditionValueFalseTempAddressTag())
+ }
+
+ /**
+ * Gets the `TranslatedExpr` for the "then" result. Note that nothing in the base implementation
+ * of this class assumes that `getThen()` is disjoint from `getCondition()`.
+ */
+ abstract TranslatedExpr getThen();
+
+ /**
+ * Gets the `TranslatedExpr` for the "else" result.
+ */
+ final TranslatedExpr getElse() { result = getTranslatedExpr(expr.getElse().getFullyConverted()) }
+
+ final predicate thenIsVoid() {
+ getThen().getResultType().getIRType() instanceof IRVoidType
+ or
+ // A `ThrowExpr.getType()` incorrectly returns the type of exception being
+ // thrown, rather than `void`. Handle that case here.
+ expr.getThen() instanceof ThrowExpr
+ }
+
+ private predicate elseIsVoid() {
+ 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().getIRType() instanceof IRVoidType }
+}
+
+/**
+ * The IR translation of the ternary conditional operator (`a ? b : c`).
+ * For this version, we expand the condition as a `TranslatedCondition`, rather than a
+ * `TranslatedExpr`, to simplify the control flow in the presence of short-ciruit logical operators.
+ */
+class TranslatedTernaryConditionalExpr extends TranslatedConditionalExpr, ConditionContext {
+ TranslatedTernaryConditionalExpr() { not expr.isTwoOperand() }
+
+ final override TranslatedElement getChild(int id) {
+ id = 0 and result = getCondition()
+ or
+ id = 1 and result = getThen()
+ or
+ id = 2 and result = getElse()
+ }
+
+ override Instruction getFirstInstruction() { result = getCondition().getFirstInstruction() }
+
+ override Instruction getChildSuccessor(TranslatedElement child) {
+ result = TranslatedConditionalExpr.super.getChildSuccessor(child)
+ or
(
child = getThen() and
if thenIsVoid()
then result = getParent().getChildSuccessor(this)
else result = getInstruction(ConditionValueTrueTempAddressTag())
)
- or
- (
- child = getElse() and
- if elseIsVoid()
- then result = getParent().getChildSuccessor(this)
- else result = getInstruction(ConditionValueFalseTempAddressTag())
- )
}
override Instruction getChildTrueSuccessor(TranslatedCondition child) {
@@ -1894,31 +1972,81 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont
result = getTranslatedCondition(expr.getCondition().getFullyConverted())
}
- private TranslatedExpr getThen() {
+ final override TranslatedExpr getThen() {
result = getTranslatedExpr(expr.getThen().getFullyConverted())
}
+}
- private TranslatedExpr getElse() {
- result = getTranslatedExpr(expr.getElse().getFullyConverted())
- }
+/**
+ * The IR translation of a two-operand conditional operator (`a ?: b`). This is a GCC language
+ * extension.
+ * This version of the conditional expression returns its first operand (the condition) if that
+ * condition is non-zero. Since we'll be reusing the value of the condition, we'll compute that
+ * value directly before branching, even if that value was a short-circuit logical expression.
+ */
+class TranslatedBinaryConditionalExpr extends TranslatedConditionalExpr {
+ TranslatedBinaryConditionalExpr() { expr.isTwoOperand() }
- private predicate thenIsVoid() {
- getThen().getResultType().getIRType() instanceof IRVoidType
+ final override TranslatedElement getChild(int id) {
+ // We only truly have two children, because our "condition" and "then" are the same as far as
+ // the extractor is concerned.
+ id = 0 and result = getCondition()
or
- // A `ThrowExpr.getType()` incorrectly returns the type of exception being
- // thrown, rather than `void`. Handle that case here.
- expr.getThen() instanceof ThrowExpr
+ id = 1 and result = getElse()
}
- private predicate elseIsVoid() {
- getElse().getResultType().getIRType() instanceof IRVoidType
+ override Instruction getFirstInstruction() { result = getCondition().getFirstInstruction() }
+
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
+ super.hasInstruction(opcode, tag, resultType)
or
- // A `ThrowExpr.getType()` incorrectly returns the type of exception being
- // thrown, rather than `void`. Handle that case here.
- expr.getElse() instanceof ThrowExpr
+ // For the binary variant, we create our own conditional branch.
+ tag = ValueConditionConditionalBranchTag() and
+ opcode instanceof Opcode::ConditionalBranch and
+ resultType = getVoidType()
}
- private predicate resultIsVoid() { getResultType().getIRType() instanceof IRVoidType }
+ override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
+ result = super.getInstructionSuccessor(tag, kind)
+ or
+ tag = ValueConditionConditionalBranchTag() and
+ (
+ kind instanceof TrueEdge and
+ result = getInstruction(ConditionValueTrueTempAddressTag())
+ or
+ kind instanceof FalseEdge and
+ result = getElse().getFirstInstruction()
+ )
+ }
+
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
+ result = super.getInstructionRegisterOperand(tag, operandTag)
+ or
+ tag = ValueConditionConditionalBranchTag() and
+ operandTag instanceof ConditionOperandTag and
+ result = getCondition().getResult()
+ }
+
+ override Instruction getChildSuccessor(TranslatedElement child) {
+ result = super.getChildSuccessor(child)
+ or
+ child = getCondition() and result = getInstruction(ValueConditionConditionalBranchTag())
+ }
+
+ private TranslatedExpr getCondition() {
+ result = getTranslatedExpr(expr.getCondition().getFullyConverted())
+ }
+
+ final override TranslatedExpr getThen() {
+ // The extractor returns the exact same expression for `ConditionalExpr::getCondition()` and
+ // `ConditionalExpr::getThen()`, even though the condition may have been converted to `bool`,
+ // and the "then" may have been converted to the result type. We'll strip the top-level implicit
+ // conversions from this, to skip any conversion to `bool`. We don't have enough information to
+ // know how to convert the result to the destination type, especially in the class pointer case,
+ // so we'll still sometimes wind up with one operand as the wrong type. This is better than
+ // always converting the "then" operand to `bool`, which is almost always the wrong type.
+ result = getTranslatedExpr(expr.getThen().getExplicitlyConverted())
+ }
}
/**
@@ -1970,20 +2098,19 @@ class TranslatedThrowValueExpr extends TranslatedThrowExpr, TranslatedVariableIn
type = getTypeForPRValue(getExceptionType())
}
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
- result = TranslatedVariableInitialization.super.getInstructionOperand(tag, operandTag)
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
+ result = TranslatedVariableInitialization.super.getInstructionRegisterOperand(tag, operandTag)
or
tag = ThrowTag() and
(
operandTag instanceof AddressOperandTag and
result = getInstruction(InitializerVariableAddressTag())
- or
- operandTag instanceof LoadOperandTag and
- result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
)
}
- final override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
+ final override CppType getInstructionMemoryOperandType(
+ InstructionTag tag, TypedOperandTag operandTag
+ ) {
tag = ThrowTag() and
operandTag instanceof LoadOperandTag and
result = getTypeForPRValue(getExceptionType())
@@ -2068,7 +2195,7 @@ class TranslatedBuiltInOperation extends TranslatedNonConstantExpr {
resultType = getResultType()
}
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = OnlyInstructionTag() and
exists(int index |
operandTag = positionalArgumentOperand(index) and
@@ -2188,7 +2315,7 @@ class TranslatedVarArgsStart extends TranslatedNonConstantExpr {
result = getEnclosingFunction().getEllipsisVariable()
}
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = VarArgsStartTag() and
operandTag instanceof UnaryOperandTag and
result = getInstruction(VarArgsStartEllipsisAddressTag())
@@ -2259,14 +2386,11 @@ class TranslatedVarArg extends TranslatedNonConstantExpr {
result = getInstruction(VarArgsVAListLoadTag())
}
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = VarArgsVAListLoadTag() and
(
operandTag instanceof AddressOperandTag and
result = getVAList().getResult()
- or
- operandTag instanceof LoadOperandTag and
- result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
)
or
tag = VarArgsArgAddressTag() and
@@ -2319,7 +2443,7 @@ class TranslatedVarArgsEnd extends TranslatedNonConstantExpr {
result = getInstruction(OnlyInstructionTag())
}
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = OnlyInstructionTag() and
operandTag instanceof UnaryOperandTag and
result = getVAList().getResult()
@@ -2380,14 +2504,11 @@ class TranslatedVarArgCopy extends TranslatedNonConstantExpr {
result = getInstruction(VarArgsVAListStoreTag())
}
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = VarArgsVAListLoadTag() and
(
operandTag instanceof AddressOperandTag and
result = getSourceVAList().getResult()
- or
- operandTag instanceof LoadOperandTag and
- result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
)
or
tag = VarArgsVAListStoreTag() and
@@ -2437,7 +2558,7 @@ abstract class TranslatedNewOrNewArrayExpr extends TranslatedNonConstantExpr, In
child = getInitialization() and result = getParent().getChildSuccessor(this)
}
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = OnlyInstructionTag() and
operandTag instanceof UnaryOperandTag and
result = getAllocatorCall().getResult()
@@ -2500,7 +2621,7 @@ class TranslatedDeleteArrayExprPlaceHolder extends TranslatedSingleInstructionEx
child = getOperand() and result = getInstruction(OnlyInstructionTag())
}
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
none()
}
@@ -2534,7 +2655,7 @@ class TranslatedDeleteExprPlaceHolder extends TranslatedSingleInstructionExpr {
child = getOperand() and result = getInstruction(OnlyInstructionTag())
}
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
none()
}
@@ -2638,7 +2759,7 @@ class TranslatedLambdaExpr extends TranslatedNonConstantExpr, InitializationCont
resultType = getTypeForPRValue(expr.getType())
}
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = InitializerStoreTag() and
operandTag instanceof AddressOperandTag and
result = getInstruction(InitializerVariableAddressTag())
@@ -2647,9 +2768,6 @@ class TranslatedLambdaExpr extends TranslatedNonConstantExpr, InitializationCont
(
operandTag instanceof AddressOperandTag and
result = getInstruction(InitializerVariableAddressTag())
- or
- operandTag instanceof LoadOperandTag and
- result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
)
}
@@ -2709,7 +2827,7 @@ class TranslatedStmtExpr extends TranslatedNonConstantExpr {
override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) }
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag instanceof OnlyInstructionTag and
operandTag instanceof UnaryOperandTag and
result = getTranslatedExpr(expr.getResultExpr().getFullyConverted()).getResult()
@@ -2733,7 +2851,7 @@ class TranslatedErrorExpr extends TranslatedSingleInstructionExpr {
final override Instruction getChildSuccessor(TranslatedElement child) { none() }
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
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 2c54fdf539a..f55d661b202 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll
@@ -49,6 +49,11 @@ CppType getEllipsisVariablePRValueType() {
CppType getEllipsisVariableGLValueType() { result = getTypeForGLValue(any(UnknownType t)) }
+/**
+ * Holds if the function returns a value, as opposed to returning `void`.
+ */
+predicate hasReturnValue(Function func) { not func.getUnspecifiedType() instanceof VoidType }
+
/**
* Represents the IR translation of a function. This is the root elements for
* all other elements associated with this function.
@@ -68,15 +73,15 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
final override Function getFunction() { result = func }
final override TranslatedElement getChild(int id) {
- id = -4 and result = getReadEffects()
+ id = -5 and result = getReadEffects()
or
- id = -3 and result = getConstructorInitList()
+ id = -4 and result = getConstructorInitList()
or
- id = -2 and result = getBody()
+ id = -3 and result = getBody()
or
- id = -1 and result = getDestructorDestructionList()
+ id = -2 and result = getDestructorDestructionList()
or
- id >= 0 and result = getParameter(id)
+ id >= -1 and result = getParameter(id)
}
final private TranslatedConstructorInitList getConstructorInitList() {
@@ -92,6 +97,9 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
final private TranslatedReadEffects getReadEffects() { result = getTranslatedReadEffects(func) }
final private TranslatedParameter getParameter(int index) {
+ result = getTranslatedThisParameter(func) and
+ index = -1
+ or
result = getTranslatedParameter(func.getParameter(index))
or
index = getEllipsisParameterIndexForFunction(func) and
@@ -109,36 +117,23 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
tag = AliasedDefinitionTag() and
result = getInstruction(InitializeNonLocalTag())
or
- tag = InitializeNonLocalTag() and
- result = getInstruction(UnmodeledDefinitionTag())
- or
(
- tag = UnmodeledDefinitionTag() and
+ tag = InitializeNonLocalTag() and
if exists(getThisType())
- then result = getInstruction(InitializeThisTag())
+ then result = getParameter(-1).getFirstInstruction()
else
if exists(getParameter(0))
then result = getParameter(0).getFirstInstruction()
else result = getBody().getFirstInstruction()
)
or
- (
- tag = InitializeThisTag() and
- if exists(getParameter(0))
- then result = getParameter(0).getFirstInstruction()
- else result = getConstructorInitList().getFirstInstruction()
- )
- or
tag = ReturnValueAddressTag() and
result = getInstruction(ReturnTag())
or
tag = ReturnTag() and
- result = getInstruction(UnmodeledUseTag())
+ result = getInstruction(AliasedUseTag())
or
tag = UnwindTag() and
- result = getInstruction(UnmodeledUseTag())
- or
- tag = UnmodeledUseTag() and
result = getInstruction(AliasedUseTag())
or
tag = AliasedUseTag() and
@@ -177,10 +172,6 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
opcode instanceof Opcode::EnterFunction and
resultType = getVoidType()
or
- tag = UnmodeledDefinitionTag() and
- opcode instanceof Opcode::UnmodeledDefinition and
- resultType = getUnknownType()
- or
tag = AliasedDefinitionTag() and
opcode instanceof Opcode::AliasedDefinition and
resultType = getUnknownType()
@@ -189,10 +180,6 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
opcode instanceof Opcode::InitializeNonLocal and
resultType = getUnknownType()
or
- tag = InitializeThisTag() and
- opcode instanceof Opcode::InitializeThis and
- resultType = getTypeForGLValue(getThisType())
- or
tag = ReturnValueAddressTag() and
opcode instanceof Opcode::VariableAddress and
resultType = getTypeForGLValue(getReturnType()) and
@@ -216,10 +203,6 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
exists(ThrowExpr throw | throw.getEnclosingFunction() = func)
)
or
- tag = UnmodeledUseTag() and
- opcode instanceof Opcode::UnmodeledUse and
- resultType = getVoidType()
- or
tag = AliasedUseTag() and
opcode instanceof Opcode::AliasedUse and
resultType = getVoidType()
@@ -234,32 +217,16 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
result = getInstruction(UnwindTag())
}
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
- tag = UnmodeledUseTag() and
- operandTag instanceof UnmodeledUseOperandTag and
- result.getEnclosingFunction() = func and
- result.hasMemoryResult()
- or
- tag = UnmodeledUseTag() and
- operandTag instanceof UnmodeledUseOperandTag and
- result = getUnmodeledDefinitionInstruction()
- or
- tag = AliasedUseTag() and
- operandTag instanceof SideEffectOperandTag and
- result = getUnmodeledDefinitionInstruction()
- or
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = ReturnTag() and
hasReturnValue() and
- (
- operandTag instanceof AddressOperandTag and
- result = getInstruction(ReturnValueAddressTag())
- or
- operandTag instanceof LoadOperandTag and
- result = getUnmodeledDefinitionInstruction()
- )
+ operandTag instanceof AddressOperandTag and
+ result = getInstruction(ReturnValueAddressTag())
}
- final override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
+ final override CppType getInstructionMemoryOperandType(
+ InstructionTag tag, TypedOperandTag operandTag
+ ) {
tag = ReturnTag() and
hasReturnValue() and
operandTag instanceof LoadOperandTag and
@@ -287,6 +254,9 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
tag = EllipsisTempVar() and
func.isVarargs() and
type = getEllipsisVariablePRValueType()
+ or
+ tag = ThisTempVar() and
+ type = getTypeForGLValue(getThisType())
}
/**
@@ -310,22 +280,22 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
final IREllipsisVariable getEllipsisVariable() { result.getEnclosingFunction() = func }
/**
- * Holds if the function has a non-`void` return type.
+ * Gets the variable that represents the `this` pointer for this function, if any.
*/
- final predicate hasReturnValue() { not func.getUnspecifiedType() instanceof VoidType }
+ final IRThisVariable getThisVariable() { result = getIRTempVariable(func, ThisTempVar()) }
/**
- * Gets the single `UnmodeledDefinition` instruction for this function.
+ * Holds if the function has a non-`void` return type.
*/
- final Instruction getUnmodeledDefinitionInstruction() {
- result = getInstruction(UnmodeledDefinitionTag())
- }
+ final predicate hasReturnValue() { hasReturnValue(func) }
/**
* Gets the single `InitializeThis` instruction for this function. Holds only
* if the function is an instance member function, constructor, or destructor.
*/
- final Instruction getInitializeThisInstruction() { result = getInstruction(InitializeThisTag()) }
+ final Instruction getInitializeThisInstruction() {
+ result = getTranslatedThisParameter(func).getInstruction(InitializerStoreTag())
+ }
/**
* Gets the type pointed to by the `this` pointer for this function (i.e. `*this`).
@@ -366,6 +336,11 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
final Type getReturnType() { result = func.getType() }
}
+/**
+ * Gets the `TranslatedThisParameter` for function `func`, if one exists.
+ */
+TranslatedThisParameter getTranslatedThisParameter(Function func) { result.getFunction() = func }
+
/**
* Gets the `TranslatedPositionalParameter` that represents parameter `param`.
*/
@@ -380,8 +355,9 @@ TranslatedEllipsisParameter getTranslatedEllipsisParameter(Function func) {
/**
* The IR translation of a parameter to a function. This can be either a user-declared parameter
- * (`TranslatedPositionParameter`) or the synthesized parameter used to represent a `...` in a
- * varargs function (`TranslatedEllipsisParameter`).
+ * (`TranslatedPositionParameter`), the synthesized parameter used to represent `this`, or the
+ * synthesized parameter used to represent a `...` in a varargs function
+ * (`TranslatedEllipsisParameter`).
*/
abstract class TranslatedParameter extends TranslatedElement {
final override TranslatedElement getChild(int id) { none() }
@@ -428,7 +404,7 @@ abstract class TranslatedParameter extends TranslatedElement {
hasIndirection() and
tag = InitializerIndirectStoreTag() and
opcode instanceof Opcode::InitializeIndirection and
- resultType = getUnknownType()
+ resultType = getInitializationResultType()
}
final override IRVariable getInstructionVariable(InstructionTag tag) {
@@ -440,7 +416,7 @@ abstract class TranslatedParameter extends TranslatedElement {
result = getIRVariable()
}
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = InitializerStoreTag() and
(
operandTag instanceof AddressOperandTag and
@@ -452,9 +428,6 @@ abstract class TranslatedParameter extends TranslatedElement {
(
operandTag instanceof AddressOperandTag and
result = getInstruction(InitializerVariableAddressTag())
- or
- operandTag instanceof LoadOperandTag and
- result = getInstruction(InitializerStoreTag())
)
or
tag = InitializerIndirectStoreTag() and
@@ -468,9 +441,43 @@ abstract class TranslatedParameter extends TranslatedElement {
abstract CppType getPRValueType();
+ abstract CppType getInitializationResultType();
+
abstract IRAutomaticVariable getIRVariable();
}
+/**
+ * The IR translation of the synthesized parameter used to represent the `...` in a varargs
+ * function.
+ */
+class TranslatedThisParameter extends TranslatedParameter, TTranslatedThisParameter {
+ Function func;
+
+ TranslatedThisParameter() { this = TTranslatedThisParameter(func) }
+
+ final override string toString() { result = "this" }
+
+ final override Locatable getAST() { result = func }
+
+ final override Function getFunction() { result = func }
+
+ final override predicate hasIndirection() { any() }
+
+ final override CppType getGLValueType() { result = getTypeForGLValue(any(UnknownType t)) }
+
+ final override CppType getPRValueType() {
+ result = getTypeForGLValue(getTranslatedFunction(func).getThisType())
+ }
+
+ final override CppType getInitializationResultType() {
+ result = getTypeForPRValue(getTranslatedFunction(func).getThisType())
+ }
+
+ final override IRThisVariable getIRVariable() {
+ result = getTranslatedFunction(func).getThisVariable()
+ }
+}
+
/**
* Represents the IR translation of a function parameter, including the
* initialization of that parameter with the incoming argument.
@@ -501,6 +508,8 @@ class TranslatedPositionalParameter extends TranslatedParameter, TTranslatedPara
final override CppType getPRValueType() { result = getTypeForPRValue(getVariableType(param)) }
+ final override CppType getInitializationResultType() { result = getUnknownType() }
+
final override IRAutomaticUserVariable getIRVariable() {
result = getIRUserVariable(getFunction(), param)
}
@@ -527,6 +536,8 @@ class TranslatedEllipsisParameter extends TranslatedParameter, TTranslatedEllips
final override CppType getPRValueType() { result = getEllipsisVariablePRValueType() }
+ final override CppType getInitializationResultType() { result = getUnknownType() }
+
final override IREllipsisVariable getIRVariable() {
result = getTranslatedFunction(func).getEllipsisVariable()
}
@@ -665,14 +676,17 @@ class TranslatedReadEffects extends TranslatedElement, TTranslatedReadEffects {
override string toString() { result = "read effects: " + func.toString() }
override TranslatedElement getChild(int id) {
- result = getTranslatedReadEffect(func.getParameter(id))
+ result = getTranslatedThisReadEffect(func) and
+ id = -1
+ or
+ result = getTranslatedParameterReadEffect(func.getParameter(id))
}
override Instruction getFirstInstruction() {
if exists(getAChild())
then
result =
- min(TranslatedReadEffect child, int id | child = getChild(id) | child order by id)
+ min(TranslatedElement child, int id | child = getChild(id) | child order by id)
.getFirstInstruction()
else result = getParent().getChildSuccessor(this)
}
@@ -698,52 +712,81 @@ class TranslatedReadEffects extends TranslatedElement, TTranslatedReadEffects {
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
}
-private TranslatedReadEffect getTranslatedReadEffect(Parameter param) { result.getAST() = param }
+private TranslatedThisReadEffect getTranslatedThisReadEffect(Function func) {
+ result.getAST() = func
+}
-class TranslatedReadEffect extends TranslatedElement, TTranslatedReadEffect {
- Parameter param;
-
- TranslatedReadEffect() { this = TTranslatedReadEffect(param) }
-
- override Locatable getAST() { result = param }
-
- override string toString() { result = "read effect: " + param.toString() }
+private TranslatedParameterReadEffect getTranslatedParameterReadEffect(Parameter param) {
+ result.getAST() = param
+}
+abstract class TranslatedReadEffect extends TranslatedElement {
override TranslatedElement getChild(int id) { none() }
override Instruction getChildSuccessor(TranslatedElement child) { none() }
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind edge) {
tag = OnlyInstructionTag() and
- edge = gotoEdge() and
+ edge = EdgeKind::gotoEdge() and
result = getParent().getChildSuccessor(this)
}
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
- override Function getFunction() { result = param.getFunction() }
-
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
opcode instanceof Opcode::ReturnIndirection and
tag = OnlyInstructionTag() and
resultType = getVoidType()
}
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
- tag = OnlyInstructionTag() and
- operandTag = sideEffectOperand() and
- result = getTranslatedFunction(getFunction()).getUnmodeledDefinitionInstruction()
- or
- tag = OnlyInstructionTag() and
- operandTag = addressOperand() and
- result = getTranslatedParameter(param).getInstruction(InitializerIndirectAddressTag())
- }
-
- final override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
+ final override CppType getInstructionMemoryOperandType(
+ InstructionTag tag, TypedOperandTag operandTag
+ ) {
tag = OnlyInstructionTag() and
operandTag = sideEffectOperand() and
result = getUnknownType()
}
+}
+
+class TranslatedThisReadEffect extends TranslatedReadEffect, TTranslatedThisReadEffect {
+ Function func;
+
+ TranslatedThisReadEffect() { this = TTranslatedThisReadEffect(func) }
+
+ override Locatable getAST() { result = func }
+
+ override Function getFunction() { result = func }
+
+ override string toString() { result = "read effect: this" }
+
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
+ tag = OnlyInstructionTag() and
+ operandTag = addressOperand() and
+ result = getTranslatedThisParameter(func).getInstruction(InitializerIndirectAddressTag())
+ }
+
+ final override IRVariable getInstructionVariable(InstructionTag tag) {
+ tag = OnlyInstructionTag() and
+ result = getTranslatedFunction(func).getThisVariable()
+ }
+}
+
+class TranslatedParameterReadEffect extends TranslatedReadEffect, TTranslatedParameterReadEffect {
+ Parameter param;
+
+ TranslatedParameterReadEffect() { this = TTranslatedParameterReadEffect(param) }
+
+ override Locatable getAST() { result = param }
+
+ override string toString() { result = "read effect: " + param.toString() }
+
+ override Function getFunction() { result = param.getFunction() }
+
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
+ tag = OnlyInstructionTag() and
+ operandTag = addressOperand() and
+ result = getTranslatedParameter(param).getInstruction(InitializerIndirectAddressTag())
+ }
final override IRVariable getInstructionVariable(InstructionTag tag) {
tag = OnlyInstructionTag() and
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll
index 5ca04d91a05..4b6538654db 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll
@@ -75,7 +75,7 @@ abstract class TranslatedVariableInitialization extends TranslatedElement, Initi
child = getInitialization() and result = getInitializationSuccessor()
}
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
hasUninitializedInstruction() and
tag = InitializerStoreTag() and
operandTag instanceof AddressOperandTag and
@@ -262,7 +262,7 @@ class TranslatedSimpleDirectInitialization extends TranslatedDirectInitializatio
child = getInitializer() and result = getInstruction(InitializerStoreTag())
}
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = InitializerStoreTag() and
(
operandTag instanceof AddressOperandTag and
@@ -355,14 +355,11 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
child = getInitializer() and result = getInstruction(InitializerLoadStringTag())
}
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = InitializerLoadStringTag() and
(
operandTag instanceof AddressOperandTag and
result = getInitializer().getResult()
- or
- operandTag instanceof LoadOperandTag and
- result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
)
or
tag = InitializerStoreTag() and
@@ -418,17 +415,6 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
)
}
- override int getInstructionResultSize(InstructionTag tag) {
- exists(int elementCount |
- zeroInitRange(_, elementCount) and
- (
- tag = ZeroPadStringConstantTag() or
- tag = ZeroPadStringStoreTag()
- ) and
- result = elementCount * getElementType().getSize()
- )
- }
-
private Type getElementType() {
result = getContext().getTargetType().getUnspecifiedType().(ArrayType).getBaseType()
}
@@ -461,7 +447,9 @@ class TranslatedConstructorInitialization extends TranslatedDirectInitialization
child = getInitializer() and result = getParent().getChildSuccessor(this)
}
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { none() }
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
+ none()
+ }
override Instruction getReceiver() { result = getContext().getTargetAddress() }
}
@@ -508,7 +496,7 @@ abstract class TranslatedFieldInitialization extends TranslatedElement {
resultType = getTypeForGLValue(field.getType())
}
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = getFieldAddressTag() and
operandTag instanceof UnaryOperandTag and
result = getParent().(InitializationContext).getTargetAddress()
@@ -599,8 +587,8 @@ class TranslatedFieldValueInitialization extends TranslatedFieldInitialization,
result = getZeroValue(field.getUnspecifiedType())
}
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
- result = TranslatedFieldInitialization.super.getInstructionOperand(tag, operandTag)
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
+ result = TranslatedFieldInitialization.super.getInstructionRegisterOperand(tag, operandTag)
or
tag = getFieldDefaultValueStoreTag() and
(
@@ -656,7 +644,7 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
kind instanceof GotoEdge
}
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = getElementAddressTag() and
(
operandTag instanceof LeftOperandTag and
@@ -773,17 +761,8 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
result = getZeroValue(getElementType())
}
- override int getInstructionResultSize(InstructionTag tag) {
- elementCount > 1 and
- (
- tag = getElementDefaultValueTag() or
- tag = getElementDefaultValueStoreTag()
- ) and
- result = elementCount * getElementType().getSize()
- }
-
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
- result = TranslatedElementInitialization.super.getInstructionOperand(tag, operandTag)
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
+ result = TranslatedElementInitialization.super.getInstructionRegisterOperand(tag, operandTag)
or
tag = getElementDefaultValueStoreTag() and
(
@@ -861,7 +840,7 @@ abstract class TranslatedBaseStructorCall extends TranslatedStructorCallFromStru
final override Instruction getReceiver() { result = getInstruction(OnlyInstructionTag()) }
- final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = OnlyInstructionTag() and
operandTag instanceof UnaryOperandTag and
result = getTranslatedFunction(getFunction()).getInitializeThisInstruction()
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 88a7d4c99ea..3339046d391 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
@@ -131,8 +131,11 @@ abstract class TranslatedReturnStmt extends TranslatedStmt {
}
}
+/**
+ * The IR translation of a `return` statement that returns a value.
+ */
class TranslatedReturnValueStmt extends TranslatedReturnStmt, TranslatedVariableInitialization {
- TranslatedReturnValueStmt() { stmt.hasExpr() }
+ TranslatedReturnValueStmt() { stmt.hasExpr() and hasReturnValue(stmt.getEnclosingFunction()) }
final override Instruction getInitializationSuccessor() {
result = getEnclosingFunction().getReturnSuccessorInstruction()
@@ -147,8 +150,49 @@ class TranslatedReturnValueStmt extends TranslatedReturnStmt, TranslatedVariable
final override IRVariable getIRVariable() { result = getEnclosingFunction().getReturnVariable() }
}
+/**
+ * The IR translation of a `return` statement that returns an expression of `void` type.
+ */
+class TranslatedReturnVoidExpressionStmt extends TranslatedReturnStmt {
+ TranslatedReturnVoidExpressionStmt() {
+ stmt.hasExpr() and not hasReturnValue(stmt.getEnclosingFunction())
+ }
+
+ override TranslatedElement getChild(int id) {
+ id = 0 and
+ result = getExpr()
+ }
+
+ override Instruction getFirstInstruction() { result = getExpr().getFirstInstruction() }
+
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
+ tag = OnlyInstructionTag() and
+ opcode instanceof Opcode::NoOp and
+ resultType = getVoidType()
+ }
+
+ override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
+ tag = OnlyInstructionTag() and
+ result = getEnclosingFunction().getReturnSuccessorInstruction() and
+ kind instanceof GotoEdge
+ }
+
+ override Instruction getChildSuccessor(TranslatedElement child) {
+ child = getExpr() and
+ result = getInstruction(OnlyInstructionTag())
+ }
+
+ private TranslatedExpr getExpr() { result = getTranslatedExpr(stmt.getExpr()) }
+}
+
+/**
+ * The IR translation of a `return` statement that does not return a value. This includes implicit
+ * return statements at the end of `void`-returning functions.
+ */
class TranslatedReturnVoidStmt extends TranslatedReturnStmt {
- TranslatedReturnVoidStmt() { not stmt.hasExpr() }
+ TranslatedReturnVoidStmt() {
+ not stmt.hasExpr() and not hasReturnValue(stmt.getEnclosingFunction())
+ }
override TranslatedElement getChild(int id) { none() }
@@ -169,6 +213,33 @@ class TranslatedReturnVoidStmt extends TranslatedReturnStmt {
override Instruction getChildSuccessor(TranslatedElement child) { none() }
}
+/**
+ * The IR translation of an implicit `return` statement generated by the extractor to handle control
+ * flow that reaches the end of a non-`void`-returning function body. Since such control flow
+ * produces undefined behavior, we simply generate an `Unreached` instruction to prevent that flow
+ * from continuing on to pollute other analysis. The assumption is that the developer is certain
+ * that the implicit `return` is unreachable, even if the compiler cannot prove it.
+ */
+class TranslatedUnreachableReturnStmt extends TranslatedReturnStmt {
+ TranslatedUnreachableReturnStmt() {
+ not stmt.hasExpr() and hasReturnValue(stmt.getEnclosingFunction())
+ }
+
+ override TranslatedElement getChild(int id) { none() }
+
+ override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
+
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
+ tag = OnlyInstructionTag() and
+ opcode instanceof Opcode::Unreached and
+ resultType = getVoidType()
+ }
+
+ override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
+
+ override Instruction getChildSuccessor(TranslatedElement child) { none() }
+}
+
/**
* The IR translation of a C++ `try` statement.
*/
@@ -642,7 +713,7 @@ class TranslatedSwitchStmt extends TranslatedStmt {
resultType = getVoidType()
}
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = SwitchBranchTag() and
operandTag instanceof ConditionOperandTag and
result = getExpr().getResult()
@@ -688,11 +759,7 @@ class TranslatedAsmStmt extends TranslatedStmt {
resultType = getUnknownType()
}
- override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
- tag = AsmTag() and
- operandTag instanceof SideEffectOperandTag and
- result = getTranslatedFunction(stmt.getEnclosingFunction()).getUnmodeledDefinitionInstruction()
- or
+ override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
exists(int index |
tag = AsmTag() and
operandTag = asmOperand(index) and
@@ -700,7 +767,9 @@ class TranslatedAsmStmt extends TranslatedStmt {
)
}
- final override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
+ final override CppType getInstructionMemoryOperandType(
+ InstructionTag tag, TypedOperandTag operandTag
+ ) {
tag = AsmTag() and
operandTag instanceof SideEffectOperandTag and
result = getUnknownType()
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll
index 49cb4dd6dc4..94ef73b2769 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll
@@ -31,10 +31,14 @@ class IRBlockBase extends TIRBlock {
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
) and
this =
- rank[result + 1](IRBlock funcBlock |
- funcBlock.getEnclosingFunction() = getEnclosingFunction()
+ rank[result + 1](IRBlock funcBlock, int sortOverride |
+ funcBlock.getEnclosingFunction() = getEnclosingFunction() and
+ // Ensure that the block containing `EnterFunction` always comes first.
+ if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction
+ then sortOverride = 0
+ else sortOverride = 1
|
- funcBlock order by funcBlock.getUniqueId()
+ funcBlock order by sortOverride, funcBlock.getUniqueId()
)
}
@@ -101,23 +105,24 @@ class IRBlock extends IRBlockBase {
private predicate startsBasicBlock(Instruction instr) {
not instr instanceof PhiInstruction and
- (
- count(Instruction predecessor | instr = predecessor.getASuccessor()) != 1 // Multiple predecessors or no predecessor
- or
- exists(Instruction predecessor |
- instr = predecessor.getASuccessor() and
- strictcount(Instruction other | other = predecessor.getASuccessor()) > 1
- ) // Predecessor has multiple successors
- or
- exists(Instruction predecessor, EdgeKind kind |
- instr = predecessor.getSuccessor(kind) and
- not kind instanceof GotoEdge
- ) // Incoming edge is not a GotoEdge
- or
- exists(Instruction predecessor |
- instr = Construction::getInstructionBackEdgeSuccessor(predecessor, _)
- ) // A back edge enters this instruction
- )
+ not adjacentInBlock(_, instr)
+}
+
+/** Holds if `i2` follows `i1` in a `IRBlock`. */
+private predicate adjacentInBlock(Instruction i1, Instruction i2) {
+ // - i2 must be the only successor of i1
+ i2 = unique(Instruction i | i = i1.getASuccessor()) and
+ // - i1 must be the only predecessor of i2
+ i1 = unique(Instruction i | i.getASuccessor() = i2) and
+ // - The edge between the two must be a GotoEdge. We just check that one
+ // exists since we've already checked that it's unique.
+ exists(GotoEdge edgeKind | exists(i1.getSuccessor(edgeKind))) and
+ // - The edge must not be a back edge. This means we get the same back edges
+ // in the basic-block graph as we do in the raw CFG.
+ not exists(Construction::getInstructionBackEdgeSuccessor(i1, _))
+ // This predicate could be simplified to remove one of the `unique`s if we
+ // were willing to rely on the CFG being well-formed and thus never having
+ // more than one successor to an instruction that has a `GotoEdge` out of it.
}
private predicate isEntryBlock(TIRBlock block) {
@@ -129,12 +134,6 @@ private module Cached {
cached
newtype TIRBlock = MkIRBlock(Instruction firstInstr) { startsBasicBlock(firstInstr) }
- /** Holds if `i2` follows `i1` in a `IRBlock`. */
- private predicate adjacentInBlock(Instruction i1, Instruction i2) {
- exists(GotoEdge edgeKind | i2 = i1.getSuccessor(edgeKind)) and
- not startsBasicBlock(i2)
- }
-
/** Holds if `i` is the `index`th instruction the block starting with `first`. */
private Instruction getInstructionFromFirst(Instruction first, int index) =
shortestDistances(startsBasicBlock/1, adjacentInBlock/2)(first, result, index)
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.ql b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.ql
new file mode 100644
index 00000000000..909a7a5fc24
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.ql
@@ -0,0 +1,8 @@
+/**
+ * @name SSA IR Consistency Check
+ * @description Performs consistency checks on the Intermediate Representation. This query should have no results.
+ * @kind table
+ * @id cpp/ssa-ir-consistency-check
+ */
+
+import IRConsistency
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll
new file mode 100644
index 00000000000..6a87b9b4b5f
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll
@@ -0,0 +1,497 @@
+private import IR
+import InstructionConsistency // module is below
+import IRTypeConsistency // module is in IRType.qll
+
+module InstructionConsistency {
+ private import internal.InstructionImports as Imports
+ private import Imports::OperandTag
+ private import Imports::Overlap
+ private import internal.IRInternal
+
+ private newtype TOptionalIRFunction =
+ TPresentIRFunction(IRFunction irFunc) or
+ TMissingIRFunction()
+
+ /**
+ * An `IRFunction` that might not exist. This is used so that we can produce consistency failures
+ * for IR that also incorrectly lacks a `getEnclosingIRFunction()`.
+ */
+ abstract private class OptionalIRFunction extends TOptionalIRFunction {
+ abstract string toString();
+
+ abstract Language::Location getLocation();
+ }
+
+ private class PresentIRFunction extends OptionalIRFunction, TPresentIRFunction {
+ private IRFunction irFunc;
+
+ PresentIRFunction() { this = TPresentIRFunction(irFunc) }
+
+ override string toString() {
+ result = concat(Language::getIdentityString(irFunc.getFunction()), "; ")
+ }
+
+ override Language::Location getLocation() {
+ // To avoid an overwhelming number of results when the extractor merges functions with the
+ // same name, just pick a single location.
+ result =
+ rank[1](Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString())
+ }
+ }
+
+ private class MissingIRFunction extends OptionalIRFunction, TMissingIRFunction {
+ override string toString() { result = "" }
+
+ override Language::Location getLocation() { result instanceof Language::UnknownDefaultLocation }
+ }
+
+ private OptionalIRFunction getInstructionIRFunction(Instruction instr) {
+ result = TPresentIRFunction(instr.getEnclosingIRFunction())
+ or
+ not exists(instr.getEnclosingIRFunction()) and result = TMissingIRFunction()
+ }
+
+ pragma[inline]
+ private OptionalIRFunction getInstructionIRFunction(Instruction instr, string irFuncText) {
+ result = getInstructionIRFunction(instr) and
+ irFuncText = result.toString()
+ }
+
+ private OptionalIRFunction getOperandIRFunction(Operand operand) {
+ result = TPresentIRFunction(operand.getEnclosingIRFunction())
+ or
+ not exists(operand.getEnclosingIRFunction()) and result = TMissingIRFunction()
+ }
+
+ pragma[inline]
+ private OptionalIRFunction getOperandIRFunction(Operand operand, string irFuncText) {
+ result = getOperandIRFunction(operand) and
+ irFuncText = result.toString()
+ }
+
+ private OptionalIRFunction getBlockIRFunction(IRBlock block) {
+ result = TPresentIRFunction(block.getEnclosingIRFunction())
+ or
+ not exists(block.getEnclosingIRFunction()) and result = TMissingIRFunction()
+ }
+
+ /**
+ * Holds if instruction `instr` is missing an expected operand with tag `tag`.
+ */
+ query predicate missingOperand(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(OperandTag tag |
+ instr.getOpcode().hasOperand(tag) and
+ not exists(NonPhiOperand operand |
+ operand = instr.getAnOperand() and
+ operand.getOperandTag() = tag
+ ) and
+ message =
+ "Instruction '" + instr.getOpcode().toString() +
+ "' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ /**
+ * Holds if instruction `instr` has an unexpected operand with tag `tag`.
+ */
+ query predicate unexpectedOperand(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(OperandTag tag |
+ exists(NonPhiOperand operand |
+ operand = instr.getAnOperand() and
+ operand.getOperandTag() = tag
+ ) and
+ not instr.getOpcode().hasOperand(tag) and
+ not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
+ not (
+ instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
+ ) and
+ not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) and
+ message =
+ "Instruction '" + instr.toString() + "' has unexpected operand '" + tag.toString() +
+ "' in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ /**
+ * Holds if instruction `instr` has multiple operands with tag `tag`.
+ */
+ query predicate duplicateOperand(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(OperandTag tag, int operandCount |
+ operandCount =
+ strictcount(NonPhiOperand operand |
+ operand = instr.getAnOperand() and
+ operand.getOperandTag() = tag
+ ) and
+ operandCount > 1 and
+ message =
+ "Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
+ " in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ /**
+ * Holds if `Phi` instruction `instr` is missing an operand corresponding to
+ * the predecessor block `pred`.
+ */
+ query predicate missingPhiOperand(
+ PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(IRBlock pred |
+ pred = instr.getBlock().getAPredecessor() and
+ not exists(PhiInputOperand operand |
+ operand = instr.getAnOperand() and
+ operand.getPredecessorBlock() = pred
+ ) and
+ message =
+ "Instruction '" + instr.toString() + "' is missing an operand for predecessor block '" +
+ pred.toString() + "' in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ query predicate missingOperandType(
+ Operand operand, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(Instruction use |
+ not exists(operand.getType()) and
+ use = operand.getUse() and
+ message =
+ "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
+ "' is missing a type in function '$@'." and
+ irFunc = getOperandIRFunction(operand, irFuncText)
+ )
+ }
+
+ query predicate duplicateChiOperand(
+ ChiInstruction chi, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ chi.getTotal() = chi.getPartial() and
+ message =
+ "Chi instruction for " + chi.getPartial().toString() +
+ " has duplicate operands in function '$@'." and
+ irFunc = getInstructionIRFunction(chi, irFuncText)
+ }
+
+ query predicate sideEffectWithoutPrimary(
+ SideEffectInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ not exists(instr.getPrimaryInstruction()) and
+ message =
+ "Side effect instruction '" + instr + "' is missing a primary instruction in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ }
+
+ /**
+ * Holds if an instruction, other than `ExitFunction`, has no successors.
+ */
+ query predicate instructionWithoutSuccessor(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ not exists(instr.getASuccessor()) and
+ not instr instanceof ExitFunctionInstruction and
+ // Phi instructions aren't linked into the instruction-level flow graph.
+ not instr instanceof PhiInstruction and
+ not instr instanceof UnreachedInstruction and
+ message = "Instruction '" + instr.toString() + "' has no successors in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ }
+
+ /**
+ * Holds if there are multiple edges of the same kind from `source`.
+ */
+ query predicate ambiguousSuccessors(
+ Instruction source, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(EdgeKind kind, int n |
+ n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
+ n > 1 and
+ message =
+ "Instruction '" + source.toString() + "' has " + n.toString() + " successors of kind '" +
+ kind.toString() + "' in function '$@'." and
+ irFunc = getInstructionIRFunction(source, irFuncText)
+ )
+ }
+
+ /**
+ * Holds if `instr` is part of a loop even though the AST of `instr`'s enclosing function
+ * contains no element that can cause loops.
+ */
+ query predicate unexplainedLoop(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(Language::Function f |
+ exists(IRBlock block |
+ instr.getBlock() = block and
+ block.getEnclosingFunction() = f and
+ block.getASuccessor+() = block
+ ) and
+ not Language::hasPotentialLoop(f) and
+ message =
+ "Instruction '" + instr.toString() + "' is part of an unexplained loop in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ /**
+ * Holds if a `Phi` instruction is present in a block with fewer than two
+ * predecessors.
+ */
+ query predicate unnecessaryPhiInstruction(
+ PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(int n |
+ n = count(instr.getBlock().getAPredecessor()) and
+ n < 2 and
+ message =
+ "Instruction '" + instr.toString() + "' is in a block with only " + n.toString() +
+ " predecessors in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ /**
+ * Holds if a memory operand is connected to a definition with an unmodeled result.
+ */
+ query predicate memoryOperandDefinitionIsUnmodeled(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(MemoryOperand operand, Instruction def |
+ operand = instr.getAnOperand() and
+ def = operand.getAnyDef() and
+ not def.isResultModeled() and
+ message =
+ "Memory operand definition on instruction '" + instr.toString() +
+ "' has unmodeled result in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ /**
+ * Holds if operand `operand` consumes a value that was defined in
+ * a different function.
+ */
+ query predicate operandAcrossFunctions(
+ Operand operand, string message, OptionalIRFunction useIRFunc, string useIRFuncText,
+ OptionalIRFunction defIRFunc, string defIRFuncText
+ ) {
+ exists(Instruction useInstr, Instruction defInstr |
+ operand.getUse() = useInstr and
+ operand.getAnyDef() = defInstr and
+ useIRFunc = getInstructionIRFunction(useInstr, useIRFuncText) and
+ defIRFunc = getInstructionIRFunction(defInstr, defIRFuncText) and
+ useIRFunc != defIRFunc and
+ message =
+ "Operand '" + operand.toString() + "' is used on instruction '" + useInstr.toString() +
+ "' in function '$@', but is defined on instruction '" + defInstr.toString() +
+ "' in function '$@'."
+ )
+ }
+
+ /**
+ * Holds if instruction `instr` is not in exactly one block.
+ */
+ query predicate instructionWithoutUniqueBlock(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(int blockCount |
+ blockCount = count(instr.getBlock()) and
+ blockCount != 1 and
+ message =
+ "Instruction '" + instr.toString() + "' is a member of " + blockCount.toString() +
+ " blocks in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+
+ private predicate forwardEdge(IRBlock b1, IRBlock b2) {
+ b1.getASuccessor() = b2 and
+ not b1.getBackEdgeSuccessor(_) = b2
+ }
+
+ /**
+ * Holds if `f` contains a loop in which no edge is a back edge.
+ *
+ * This check ensures we don't have too _few_ back edges.
+ */
+ query predicate containsLoopOfForwardEdges(IRFunction f, string message) {
+ exists(IRBlock block |
+ forwardEdge+(block, block) and
+ block.getEnclosingIRFunction() = f and
+ message = "Function contains a loop consisting of only forward edges."
+ )
+ }
+
+ /**
+ * Holds if `block` is reachable from its function entry point but would not
+ * be reachable by traversing only forward edges. This check is skipped for
+ * functions containing `goto` statements as the property does not generally
+ * hold there.
+ *
+ * This check ensures we don't have too _many_ back edges.
+ */
+ query predicate lostReachability(
+ IRBlock block, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(IRFunction f, IRBlock entry |
+ entry = f.getEntryBlock() and
+ entry.getASuccessor+() = block and
+ not forwardEdge+(entry, block) and
+ not Language::hasGoto(f.getFunction()) and
+ message =
+ "Block '" + block.toString() +
+ "' is not reachable by traversing only forward edges in function '$@'." and
+ irFunc = TPresentIRFunction(f) and
+ irFuncText = irFunc.toString()
+ )
+ }
+
+ /**
+ * Holds if the number of back edges differs between the `Instruction` graph
+ * and the `IRBlock` graph.
+ */
+ query predicate backEdgeCountMismatch(OptionalIRFunction irFunc, string message) {
+ exists(int fromInstr, int fromBlock |
+ fromInstr =
+ count(Instruction i1, Instruction i2 |
+ getInstructionIRFunction(i1) = irFunc and i1.getBackEdgeSuccessor(_) = i2
+ ) and
+ fromBlock =
+ count(IRBlock b1, IRBlock b2 |
+ getBlockIRFunction(b1) = irFunc and b1.getBackEdgeSuccessor(_) = b2
+ ) and
+ fromInstr != fromBlock and
+ message =
+ "The instruction graph for function '" + irFunc.toString() + "' contains " +
+ fromInstr.toString() + " back edges, but the block graph contains " + fromBlock.toString()
+ + " back edges."
+ )
+ }
+
+ /**
+ * Gets the point in the function at which the specified operand is evaluated. For most operands,
+ * this is at the instruction that consumes the use. For a `PhiInputOperand`, the effective point
+ * of evaluation is at the end of the corresponding predecessor block.
+ */
+ private predicate pointOfEvaluation(Operand operand, IRBlock block, int index) {
+ block = operand.(PhiInputOperand).getPredecessorBlock() and
+ index = block.getInstructionCount()
+ or
+ exists(Instruction use |
+ use = operand.(NonPhiOperand).getUse() and
+ block.getInstruction(index) = use
+ )
+ }
+
+ /**
+ * Holds if `useOperand` has a definition that does not dominate the use.
+ */
+ query predicate useNotDominatedByDefinition(
+ Operand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
+ pointOfEvaluation(useOperand, useBlock, useIndex) and
+ defInstr = useOperand.getAnyDef() and
+ (
+ defInstr instanceof PhiInstruction and
+ defBlock = defInstr.getBlock() and
+ defIndex = -1
+ or
+ defBlock.getInstruction(defIndex) = defInstr
+ ) and
+ not (
+ defBlock.strictlyDominates(useBlock)
+ or
+ defBlock = useBlock and
+ defIndex < useIndex
+ ) and
+ message =
+ "Operand '" + useOperand.toString() +
+ "' is not dominated by its definition in function '$@'." and
+ irFunc = getOperandIRFunction(useOperand, irFuncText)
+ )
+ }
+
+ query predicate switchInstructionWithoutDefaultEdge(
+ SwitchInstruction switchInstr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ not exists(switchInstr.getDefaultSuccessor()) and
+ message =
+ "SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and
+ irFunc = getInstructionIRFunction(switchInstr, irFuncText)
+ }
+
+ /**
+ * Holds if `instr` is on the chain of chi/phi instructions for all aliased
+ * memory.
+ */
+ private predicate isOnAliasedDefinitionChain(Instruction instr) {
+ instr instanceof AliasedDefinitionInstruction
+ or
+ isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal())
+ or
+ isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
+ }
+
+ private predicate shouldBeConflated(Instruction instr) {
+ isOnAliasedDefinitionChain(instr)
+ or
+ instr.getOpcode() instanceof Opcode::InitializeNonLocal
+ }
+
+ query predicate notMarkedAsConflated(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ shouldBeConflated(instr) and
+ not instr.isResultConflated() and
+ message =
+ "Instruction '" + instr.toString() +
+ "' should be marked as having a conflated result in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ }
+
+ query predicate wronglyMarkedAsConflated(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ instr.isResultConflated() and
+ not shouldBeConflated(instr) and
+ message =
+ "Instruction '" + instr.toString() +
+ "' should not be marked as having a conflated result in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ }
+
+ query predicate invalidOverlap(
+ MemoryOperand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(Overlap overlap |
+ overlap = useOperand.getDefinitionOverlap() and
+ overlap instanceof MayPartiallyOverlap and
+ message =
+ "MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
+ overlap.toString() + "'." and
+ irFunc = getOperandIRFunction(useOperand, irFuncText)
+ )
+ }
+
+ query predicate nonUniqueEnclosingIRFunction(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(int irFuncCount |
+ irFuncCount = count(instr.getEnclosingIRFunction()) and
+ irFuncCount != 1 and
+ message =
+ "Instruction '" + instr.toString() + "' has " + irFuncCount.toString() +
+ " results for `getEnclosingIRFunction()` in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll
index 1e9c2d1d913..6b2d32af48c 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll
@@ -1,29 +1,12 @@
private import internal.IRInternal
+private import internal.IRFunctionImports as Imports
+import Imports::IRFunctionBase
import Instruction
-private newtype TIRFunction =
- MkIRFunction(Language::Function func) { Construction::functionHasIR(func) }
-
/**
- * Represents the IR for a function.
+ * The IR for a function.
*/
-class IRFunction extends TIRFunction {
- Language::Function func;
-
- IRFunction() { this = MkIRFunction(func) }
-
- final string toString() { result = "IR: " + func.toString() }
-
- /**
- * Gets the function whose IR is represented.
- */
- final Language::Function getFunction() { result = func }
-
- /**
- * Gets the location of the function.
- */
- final Language::Location getLocation() { result = func.getLocation() }
-
+class IRFunction extends IRFunctionBase {
/**
* Gets the entry point for this function.
*/
@@ -40,16 +23,6 @@ class IRFunction extends TIRFunction {
result.getEnclosingIRFunction() = this
}
- pragma[noinline]
- final UnmodeledDefinitionInstruction getUnmodeledDefinitionInstruction() {
- result.getEnclosingIRFunction() = this
- }
-
- pragma[noinline]
- final UnmodeledUseInstruction getUnmodeledUseInstruction() {
- result.getEnclosingIRFunction() = this
- }
-
/**
* Gets the single return instruction for this function.
*/
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.ql b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.ql
deleted file mode 100644
index eee45030caf..00000000000
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.ql
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * @name SSA IR Sanity Check
- * @description Performs sanity checks on the Intermediate Representation. This query should have no results.
- * @kind table
- * @id cpp/ssa-ir-sanity-check
- */
-
-import IRSanity
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
deleted file mode 100644
index 94d0192fe18..00000000000
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll
+++ /dev/null
@@ -1,320 +0,0 @@
-private import IR
-import InstructionSanity // module is below
-import IRTypeSanity // module is in IRType.qll
-
-module InstructionSanity {
- private import internal.InstructionImports as Imports
- private import Imports::OperandTag
- private import Imports::Overlap
- private import internal.IRInternal
-
- /**
- * Holds if instruction `instr` is missing an expected operand with tag `tag`.
- */
- query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) {
- exists(OperandTag tag |
- instr.getOpcode().hasOperand(tag) and
- not exists(NonPhiOperand operand |
- operand = instr.getAnOperand() and
- operand.getOperandTag() = tag
- ) and
- message =
- "Instruction '" + instr.getOpcode().toString() +
- "' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
- func = instr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
- )
- }
-
- /**
- * Holds if instruction `instr` has an unexpected operand with tag `tag`.
- */
- query predicate unexpectedOperand(Instruction instr, OperandTag tag) {
- exists(NonPhiOperand operand |
- operand = instr.getAnOperand() and
- operand.getOperandTag() = tag
- ) and
- not instr.getOpcode().hasOperand(tag) and
- not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
- not (
- instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
- ) and
- not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag)
- }
-
- /**
- * Holds if instruction `instr` has multiple operands with tag `tag`.
- */
- query predicate duplicateOperand(
- Instruction instr, string message, IRFunction func, string funcText
- ) {
- exists(OperandTag tag, int operandCount |
- operandCount =
- strictcount(NonPhiOperand operand |
- operand = instr.getAnOperand() and
- operand.getOperandTag() = tag
- ) and
- operandCount > 1 and
- not tag instanceof UnmodeledUseOperandTag and
- message =
- "Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
- " in function '$@'." and
- func = instr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
- )
- }
-
- /**
- * Holds if `Phi` instruction `instr` is missing an operand corresponding to
- * the predecessor block `pred`.
- */
- query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
- pred = instr.getBlock().getAPredecessor() and
- not exists(PhiInputOperand operand |
- operand = instr.getAnOperand() and
- operand.getPredecessorBlock() = pred
- )
- }
-
- query predicate missingOperandType(Operand operand, string message) {
- exists(Language::Function func, Instruction use |
- not exists(operand.getType()) and
- use = operand.getUse() and
- func = use.getEnclosingFunction() and
- message =
- "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
- "' missing type in function '" + Language::getIdentityString(func) + "'."
- )
- }
-
- 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.
- */
- query predicate instructionWithoutSuccessor(Instruction instr) {
- not exists(instr.getASuccessor()) and
- not instr instanceof ExitFunctionInstruction and
- // Phi instructions aren't linked into the instruction-level flow graph.
- not instr instanceof PhiInstruction and
- not instr instanceof UnreachedInstruction
- }
-
- /**
- * Holds if there are multiple (`n`) edges of kind `kind` from `source`,
- * where `target` is among the targets of those edges.
- */
- query predicate ambiguousSuccessors(Instruction source, EdgeKind kind, int n, Instruction target) {
- n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
- n > 1 and
- source.getSuccessor(kind) = target
- }
-
- /**
- * Holds if `instr` in `f` is part of a loop even though the AST of `f`
- * contains no element that can cause loops.
- */
- query predicate unexplainedLoop(Language::Function f, Instruction instr) {
- exists(IRBlock block |
- instr.getBlock() = block and
- block.getEnclosingFunction() = f and
- block.getASuccessor+() = block
- ) and
- not Language::hasPotentialLoop(f)
- }
-
- /**
- * Holds if a `Phi` instruction is present in a block with fewer than two
- * predecessors.
- */
- query predicate unnecessaryPhiInstruction(PhiInstruction instr) {
- count(instr.getBlock().getAPredecessor()) < 2
- }
-
- /**
- * Holds if operand `operand` consumes a value that was defined in
- * a different function.
- */
- query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) {
- operand.getUse() = instr and
- operand.getAnyDef() = defInstr and
- instr.getEnclosingIRFunction() != defInstr.getEnclosingIRFunction()
- }
-
- /**
- * Holds if instruction `instr` is not in exactly one block.
- */
- query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) {
- blockCount = count(instr.getBlock()) and
- blockCount != 1
- }
-
- private predicate forwardEdge(IRBlock b1, IRBlock b2) {
- b1.getASuccessor() = b2 and
- not b1.getBackEdgeSuccessor(_) = b2
- }
-
- /**
- * Holds if `f` contains a loop in which no edge is a back edge.
- *
- * This check ensures we don't have too _few_ back edges.
- */
- query predicate containsLoopOfForwardEdges(IRFunction f) {
- exists(IRBlock block |
- forwardEdge+(block, block) and
- block.getEnclosingIRFunction() = f
- )
- }
-
- /**
- * Holds if `block` is reachable from its function entry point but would not
- * be reachable by traversing only forward edges. This check is skipped for
- * functions containing `goto` statements as the property does not generally
- * hold there.
- *
- * This check ensures we don't have too _many_ back edges.
- */
- query predicate lostReachability(IRBlock block) {
- exists(IRFunction f, IRBlock entry |
- entry = f.getEntryBlock() and
- entry.getASuccessor+() = block and
- not forwardEdge+(entry, block) and
- not Language::hasGoto(f.getFunction())
- )
- }
-
- /**
- * Holds if the number of back edges differs between the `Instruction` graph
- * and the `IRBlock` graph.
- */
- query predicate backEdgeCountMismatch(Language::Function f, int fromInstr, int fromBlock) {
- fromInstr =
- count(Instruction i1, Instruction i2 |
- i1.getEnclosingFunction() = f and i1.getBackEdgeSuccessor(_) = i2
- ) and
- fromBlock =
- count(IRBlock b1, IRBlock b2 |
- b1.getEnclosingFunction() = f and b1.getBackEdgeSuccessor(_) = b2
- ) and
- fromInstr != fromBlock
- }
-
- /**
- * Gets the point in the function at which the specified operand is evaluated. For most operands,
- * this is at the instruction that consumes the use. For a `PhiInputOperand`, the effective point
- * of evaluation is at the end of the corresponding predecessor block.
- */
- private predicate pointOfEvaluation(Operand operand, IRBlock block, int index) {
- block = operand.(PhiInputOperand).getPredecessorBlock() and
- index = block.getInstructionCount()
- or
- exists(Instruction use |
- use = operand.(NonPhiOperand).getUse() and
- block.getInstruction(index) = use
- )
- }
-
- /**
- * Holds if `useOperand` has a definition that does not dominate the use.
- */
- query predicate useNotDominatedByDefinition(
- Operand useOperand, string message, IRFunction func, string funcText
- ) {
- 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
- (
- defInstr instanceof PhiInstruction and
- defBlock = defInstr.getBlock() and
- defIndex = -1
- or
- defBlock.getInstruction(defIndex) = defInstr
- ) and
- not (
- defBlock.strictlyDominates(useBlock)
- or
- defBlock = useBlock and
- defIndex < useIndex
- ) and
- message =
- "Operand '" + useOperand.toString() +
- "' is not dominated by its definition in function '$@'." and
- func = useOperand.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
- )
- }
-
- query predicate switchInstructionWithoutDefaultEdge(
- SwitchInstruction switchInstr, string message, IRFunction func, string funcText
- ) {
- not exists(switchInstr.getDefaultSuccessor()) and
- message =
- "SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and
- func = switchInstr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
- }
-
- /**
- * Holds if `instr` is on the chain of chi/phi instructions for all aliased
- * memory.
- */
- private predicate isOnAliasedDefinitionChain(Instruction instr) {
- instr instanceof AliasedDefinitionInstruction
- or
- isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal())
- or
- isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
- }
-
- private predicate shouldBeConflated(Instruction instr) {
- isOnAliasedDefinitionChain(instr)
- or
- instr instanceof UnmodeledDefinitionInstruction
- or
- instr.getOpcode() instanceof Opcode::InitializeNonLocal
- }
-
- query predicate notMarkedAsConflated(Instruction instr) {
- shouldBeConflated(instr) and
- not instr.isResultConflated()
- }
-
- query predicate wronglyMarkedAsConflated(Instruction instr) {
- instr.isResultConflated() and
- not shouldBeConflated(instr)
- }
-
- query predicate invalidOverlap(
- MemoryOperand useOperand, string message, IRFunction func, string funcText
- ) {
- exists(Overlap overlap |
- overlap = useOperand.getDefinitionOverlap() and
- overlap instanceof MayPartiallyOverlap and
- message =
- "MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
- overlap.toString() + "'." and
- func = useOperand.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
- )
- }
-}
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 0d5e7fe595c..a01bd2dc79a 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll
@@ -217,10 +217,23 @@ class IRThrowVariable extends IRTempVariable {
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
* function that accepts a variable number of arguments.
*/
-class IREllipsisVariable extends IRTempVariable {
+class IREllipsisVariable extends IRTempVariable, IRParameter {
IREllipsisVariable() { tag = EllipsisTempVar() }
final override string toString() { result = "#ellipsis" }
+
+ final override int getIndex() { result = func.getNumberOfParameters() }
+}
+
+/**
+ * A temporary variable generated to hold the `this` pointer.
+ */
+class IRThisVariable extends IRTempVariable, IRParameter {
+ IRThisVariable() { tag = ThisTempVar() }
+
+ final override string toString() { result = "#this" }
+
+ final override int getIndex() { result = -1 }
}
/**
@@ -265,3 +278,29 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
}
+
+/**
+ * An IR variable which acts like a function parameter, including positional parameters and the
+ * temporary variables generated for `this` and ellipsis parameters.
+ */
+class IRParameter extends IRAutomaticVariable {
+ IRParameter() {
+ this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter
+ or
+ this = TIRTempVariable(_, _, ThisTempVar(), _)
+ or
+ this = TIRTempVariable(_, _, EllipsisTempVar(), _)
+ }
+
+ /**
+ * Gets the zero-based index of this parameter. The `this` parameter has index -1.
+ */
+ int getIndex() { none() }
+}
+
+/**
+ * An IR variable representing a positional parameter.
+ */
+class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable {
+ final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll
index 38216872f2b..409577d3e46 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll
@@ -29,7 +29,13 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil
/**
* Represents a single operation in the IR.
*/
-class Instruction extends Construction::TInstruction {
+class Instruction extends Construction::TStageInstruction {
+ Instruction() {
+ // The base `TStageInstruction` type is a superset of the actual instructions appearing in this
+ // stage. This call lets the stage filter out the ones that are not reused from raw IR.
+ Construction::hasInstruction(this)
+ }
+
final string toString() { result = getOpcode().toString() + ": " + getAST().toString() }
/**
@@ -190,17 +196,18 @@ class Instruction extends Construction::TInstruction {
final Language::Location getLocation() { result = getAST().getLocation() }
/**
- * Gets the `Expr` whose result is computed by this instruction, if any.
+ * Gets the `Expr` whose result is computed by this instruction, if any. The `Expr` may be a
+ * conversion.
*/
final Language::Expr getConvertedResultExpression() {
- result = Construction::getInstructionConvertedResultExpression(this)
+ result = Raw::getInstructionConvertedResultExpression(this)
}
/**
- * Gets the unconverted `Expr` whose result is computed by this instruction, if any.
+ * Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any.
*/
final Language::Expr getUnconvertedResultExpression() {
- result = Construction::getInstructionUnconvertedResultExpression(this)
+ result = Raw::getInstructionUnconvertedResultExpression(this)
}
final Language::LanguageType getResultLanguageType() {
@@ -211,6 +218,7 @@ class Instruction extends Construction::TInstruction {
* Gets the type of the result produced by this instruction. If the instruction does not produce
* a result, its result type will be `IRVoidType`.
*/
+ cached
final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
/**
@@ -249,7 +257,7 @@ class Instruction extends Construction::TInstruction {
* result of the `Load` instruction is a prvalue of type `int`, representing
* the integer value loaded from variable `x`.
*/
- final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
+ final predicate isGLValue() { getResultLanguageType().hasType(_, true) }
/**
* Gets the size of the result produced by this instruction, in bytes. If the
@@ -258,7 +266,7 @@ class Instruction extends Construction::TInstruction {
* If `this.isGLValue()` holds for this instruction, the value of
* `getResultSize()` will always be the size of a pointer.
*/
- final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
+ final int getResultSize() { result = getResultLanguageType().getByteSize() }
/**
* Gets the opcode that specifies the operation performed by this instruction.
@@ -319,8 +327,7 @@ class Instruction extends Construction::TInstruction {
/**
* Holds if the result of this instruction is precisely modeled in SSA. Always
* holds for a register result. For a memory result, a modeled result is
- * connected to its actual uses. An unmodeled result is connected to the
- * `UnmodeledUse` instruction.
+ * connected to its actual uses. An unmodeled result has no uses.
*
* For example:
* ```
@@ -395,7 +402,7 @@ class Instruction extends Construction::TInstruction {
class VariableInstruction extends Instruction {
IRVariable var;
- VariableInstruction() { var = Construction::getInstructionVariable(this) }
+ VariableInstruction() { var = Raw::getInstructionVariable(this) }
override string getImmediateString() { result = var.toString() }
@@ -410,7 +417,7 @@ class VariableInstruction extends Instruction {
class FieldInstruction extends Instruction {
Language::Field field;
- FieldInstruction() { field = Construction::getInstructionField(this) }
+ FieldInstruction() { field = Raw::getInstructionField(this) }
final override string getImmediateString() { result = field.toString() }
@@ -420,7 +427,7 @@ class FieldInstruction extends Instruction {
class FunctionInstruction extends Instruction {
Language::Function funcSymbol;
- FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) }
+ FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) }
final override string getImmediateString() { result = funcSymbol.toString() }
@@ -430,7 +437,7 @@ class FunctionInstruction extends Instruction {
class ConstantValueInstruction extends Instruction {
string value;
- ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) }
+ ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) }
final override string getImmediateString() { result = value }
@@ -440,7 +447,7 @@ class ConstantValueInstruction extends Instruction {
class IndexedInstruction extends Instruction {
int index;
- IndexedInstruction() { index = Construction::getInstructionIndex(this) }
+ IndexedInstruction() { index = Raw::getInstructionIndex(this) }
final override string getImmediateString() { result = index.toString() }
@@ -541,6 +548,11 @@ class ReturnIndirectionInstruction extends VariableInstruction {
* function.
*/
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
+
+ /**
+ * Holds if this instruction is the return indirection for `this`.
+ */
+ final predicate isThisIndirection() { var instanceof IRThisVariable }
}
class CopyInstruction extends Instruction {
@@ -584,9 +596,9 @@ class ConditionalBranchInstruction extends Instruction {
final Instruction getCondition() { result = getConditionOperand().getDef() }
- final Instruction getTrueSuccessor() { result = getSuccessor(trueEdge()) }
+ final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) }
- final Instruction getFalseSuccessor() { result = getSuccessor(falseEdge()) }
+ final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) }
}
class ExitFunctionInstruction extends Instruction {
@@ -598,11 +610,16 @@ class ConstantInstruction extends ConstantValueInstruction {
}
class IntegerConstantInstruction extends ConstantInstruction {
- IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType }
+ IntegerConstantInstruction() {
+ exists(IRType resultType |
+ resultType = getResultIRType() and
+ (resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
+ )
+ }
}
class FloatConstantInstruction extends ConstantInstruction {
- FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
+ FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType }
}
class StringConstantInstruction extends VariableInstruction {
@@ -699,7 +716,7 @@ class PointerArithmeticInstruction extends BinaryInstruction {
PointerArithmeticInstruction() {
getOpcode() instanceof PointerArithmeticOpcode and
- elementSize = Construction::getInstructionElementSize(this)
+ elementSize = Raw::getInstructionElementSize(this)
}
final override string getImmediateString() { result = elementSize.toString() }
@@ -748,7 +765,7 @@ class InheritanceConversionInstruction extends UnaryInstruction {
Language::Class derivedClass;
InheritanceConversionInstruction() {
- Construction::getInstructionInheritance(this, baseClass, derivedClass)
+ Raw::getInstructionInheritance(this, baseClass, derivedClass)
}
final override string getImmediateString() {
@@ -906,7 +923,7 @@ class SwitchInstruction extends Instruction {
final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) }
- final Instruction getDefaultSuccessor() { result = getSuccessor(defaultEdge()) }
+ final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) }
}
/**
@@ -1211,7 +1228,7 @@ class CatchByTypeInstruction extends CatchInstruction {
CatchByTypeInstruction() {
getOpcode() instanceof Opcode::CatchByType and
- exceptionType = Construction::getInstructionExceptionType(this)
+ exceptionType = Raw::getInstructionExceptionType(this)
}
final override string getImmediateString() { result = exceptionType.toString() }
@@ -1229,10 +1246,6 @@ class CatchAnyInstruction extends CatchInstruction {
CatchAnyInstruction() { getOpcode() instanceof Opcode::CatchAny }
}
-class UnmodeledDefinitionInstruction extends Instruction {
- UnmodeledDefinitionInstruction() { getOpcode() instanceof Opcode::UnmodeledDefinition }
-}
-
/**
* An instruction that initializes all escaped memory.
*/
@@ -1247,12 +1260,6 @@ class AliasedUseInstruction extends Instruction {
AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse }
}
-class UnmodeledUseInstruction extends Instruction {
- UnmodeledUseInstruction() { getOpcode() instanceof Opcode::UnmodeledUse }
-
- override string getOperandsString() { result = "mu*" }
-}
-
/**
* An instruction representing the choice of one of multiple input values based on control flow.
*
@@ -1367,7 +1374,7 @@ class BuiltInOperationInstruction extends Instruction {
BuiltInOperationInstruction() {
getOpcode() instanceof BuiltInOperationOpcode and
- operation = Construction::getInstructionBuiltInOperation(this)
+ operation = Raw::getInstructionBuiltInOperation(this)
}
final Language::BuiltInOperation getBuiltInOperation() { result = operation }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll
index 1836f4c4b2f..f82704094c8 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
@@ -14,16 +14,8 @@ private newtype TOperand =
not Construction::isInCycle(useInstr) and
strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1
} or
- TNonPhiMemoryOperand(
- Instruction useInstr, MemoryOperandTag tag, Instruction defInstr, Overlap overlap
- ) {
- defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and
- not Construction::isInCycle(useInstr) and
- (
- strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
- or
- tag instanceof UnmodeledUseOperandTag
- )
+ TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
+ useInstr.getOpcode().hasOperand(tag)
} or
TPhiOperand(
PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
@@ -31,6 +23,57 @@ private newtype TOperand =
defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
}
+/**
+ * Base class for all register operands. This is a placeholder for the IPA union type that we will
+ * eventually use for this purpose.
+ */
+private class RegisterOperandBase extends TRegisterOperand {
+ /** Gets a textual representation of this element. */
+ abstract string toString();
+}
+
+/**
+ * Returns the register operand with the specified parameters.
+ */
+private RegisterOperandBase registerOperand(
+ Instruction useInstr, RegisterOperandTag tag, Instruction defInstr
+) {
+ result = TRegisterOperand(useInstr, tag, defInstr)
+}
+
+/**
+ * Base class for all non-Phi memory operands. This is a placeholder for the IPA union type that we
+ * will eventually use for this purpose.
+ */
+private class NonPhiMemoryOperandBase extends TNonPhiMemoryOperand {
+ /** Gets a textual representation of this element. */
+ abstract string toString();
+}
+
+/**
+ * Returns the non-Phi memory operand with the specified parameters.
+ */
+private NonPhiMemoryOperandBase nonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
+ result = TNonPhiMemoryOperand(useInstr, tag)
+}
+
+/**
+ * Base class for all Phi operands. This is a placeholder for the IPA union type that we will
+ * eventually use for this purpose.
+ */
+private class PhiOperandBase extends TPhiOperand {
+ abstract string toString();
+}
+
+/**
+ * Returns the Phi operand with the specified parameters.
+ */
+private PhiOperandBase phiOperand(
+ Instruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
+) {
+ result = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
+}
+
/**
* A source operand of an `Instruction`. The operand represents a value consumed by the instruction.
*/
@@ -104,7 +147,17 @@ class Operand extends TOperand {
* For example: `this:r3_5`
*/
final string getDumpString() {
- result = getDumpLabel() + getInexactSpecifier() + getAnyDef().getResultId()
+ result = getDumpLabel() + getInexactSpecifier() + getDefinitionId()
+ }
+
+ /**
+ * Gets a string containing the identifier of the definition of this use, or `m?` if the
+ * definition is not modeled in SSA.
+ */
+ private string getDefinitionId() {
+ result = getAnyDef().getResultId()
+ or
+ not exists(getAnyDef()) and result = "m?"
}
/**
@@ -169,8 +222,8 @@ class Operand extends TOperand {
*/
class MemoryOperand extends Operand {
MemoryOperand() {
- this = TNonPhiMemoryOperand(_, _, _, _) or
- this = TPhiOperand(_, _, _, _)
+ this instanceof NonPhiMemoryOperandBase or
+ this instanceof PhiOperandBase
}
/**
@@ -204,18 +257,15 @@ class MemoryOperand extends Operand {
*/
class NonPhiOperand extends Operand {
Instruction useInstr;
- Instruction defInstr;
OperandTag tag;
NonPhiOperand() {
- this = TRegisterOperand(useInstr, tag, defInstr) or
- this = TNonPhiMemoryOperand(useInstr, tag, defInstr, _)
+ this = registerOperand(useInstr, tag, _) or
+ this = nonPhiMemoryOperand(useInstr, tag)
}
final override Instruction getUse() { result = useInstr }
- final override Instruction getAnyDef() { result = defInstr }
-
final override string getDumpLabel() { result = tag.getLabel() }
final override int getDumpSortOrder() { result = tag.getSortOrder() }
@@ -226,8 +276,15 @@ class NonPhiOperand extends Operand {
/**
* An operand that consumes a register (non-memory) result.
*/
-class RegisterOperand extends NonPhiOperand, TRegisterOperand {
+class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
override RegisterOperandTag tag;
+ Instruction defInstr;
+
+ RegisterOperand() { this = registerOperand(useInstr, tag, defInstr) }
+
+ final override string toString() { result = tag.toString() }
+
+ final override Instruction getAnyDef() { result = defInstr }
final override Overlap getDefinitionOverlap() {
// All register results overlap exactly with their uses.
@@ -235,13 +292,25 @@ class RegisterOperand extends NonPhiOperand, TRegisterOperand {
}
}
-class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOperand {
+class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase {
override MemoryOperandTag tag;
- Overlap overlap;
- NonPhiMemoryOperand() { this = TNonPhiMemoryOperand(useInstr, tag, defInstr, overlap) }
+ NonPhiMemoryOperand() { this = nonPhiMemoryOperand(useInstr, tag) }
- final override Overlap getDefinitionOverlap() { result = overlap }
+ final override string toString() { result = tag.toString() }
+
+ final override Instruction getAnyDef() {
+ result = unique(Instruction defInstr | hasDefinition(defInstr, _))
+ }
+
+ final override Overlap getDefinitionOverlap() { hasDefinition(_, result) }
+
+ pragma[noinline]
+ private predicate hasDefinition(Instruction defInstr, Overlap overlap) {
+ defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and
+ not Construction::isInCycle(useInstr) and
+ strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
+ }
}
class TypedOperand extends NonPhiMemoryOperand {
@@ -258,8 +327,6 @@ class TypedOperand extends NonPhiMemoryOperand {
*/
class AddressOperand extends RegisterOperand {
override AddressOperandTag tag;
-
- override string toString() { result = "Address" }
}
/**
@@ -268,8 +335,6 @@ class AddressOperand extends RegisterOperand {
*/
class BufferSizeOperand extends RegisterOperand {
override BufferSizeOperandTag tag;
-
- override string toString() { result = "BufferSize" }
}
/**
@@ -278,8 +343,6 @@ class BufferSizeOperand extends RegisterOperand {
*/
class LoadOperand extends TypedOperand {
override LoadOperandTag tag;
-
- override string toString() { result = "Load" }
}
/**
@@ -287,8 +350,6 @@ class LoadOperand extends TypedOperand {
*/
class StoreValueOperand extends RegisterOperand {
override StoreValueOperandTag tag;
-
- override string toString() { result = "StoreValue" }
}
/**
@@ -296,8 +357,6 @@ class StoreValueOperand extends RegisterOperand {
*/
class UnaryOperand extends RegisterOperand {
override UnaryOperandTag tag;
-
- override string toString() { result = "Unary" }
}
/**
@@ -305,8 +364,6 @@ class UnaryOperand extends RegisterOperand {
*/
class LeftOperand extends RegisterOperand {
override LeftOperandTag tag;
-
- override string toString() { result = "Left" }
}
/**
@@ -314,8 +371,6 @@ class LeftOperand extends RegisterOperand {
*/
class RightOperand extends RegisterOperand {
override RightOperandTag tag;
-
- override string toString() { result = "Right" }
}
/**
@@ -323,18 +378,6 @@ class RightOperand extends RegisterOperand {
*/
class ConditionOperand extends RegisterOperand {
override ConditionOperandTag tag;
-
- override string toString() { result = "Condition" }
-}
-
-/**
- * An operand of the special `UnmodeledUse` instruction, representing a value
- * whose set of uses is unknown.
- */
-class UnmodeledUseOperand extends NonPhiMemoryOperand {
- override UnmodeledUseOperandTag tag;
-
- override string toString() { result = "UnmodeledUse" }
}
/**
@@ -342,8 +385,6 @@ class UnmodeledUseOperand extends NonPhiMemoryOperand {
*/
class CallTargetOperand extends RegisterOperand {
override CallTargetOperandTag tag;
-
- override string toString() { result = "CallTarget" }
}
/**
@@ -361,8 +402,6 @@ class ArgumentOperand extends RegisterOperand {
*/
class ThisArgumentOperand extends ArgumentOperand {
override ThisArgumentOperandTag tag;
-
- override string toString() { result = "ThisArgument" }
}
/**
@@ -370,34 +409,27 @@ class ThisArgumentOperand extends ArgumentOperand {
*/
class PositionalArgumentOperand extends ArgumentOperand {
override PositionalArgumentOperandTag tag;
- int argIndex;
-
- PositionalArgumentOperand() { argIndex = tag.getArgIndex() }
-
- override string toString() { result = "Arg(" + argIndex + ")" }
/**
* Gets the zero-based index of the argument.
*/
- final int getIndex() { result = argIndex }
+ final int getIndex() { result = tag.getArgIndex() }
}
class SideEffectOperand extends TypedOperand {
override SideEffectOperandTag tag;
-
- override string toString() { result = "SideEffect" }
}
/**
* An operand of a `PhiInstruction`.
*/
-class PhiInputOperand extends MemoryOperand, TPhiOperand {
+class PhiInputOperand extends MemoryOperand, PhiOperandBase {
PhiInstruction useInstr;
Instruction defInstr;
IRBlock predecessorBlock;
Overlap overlap;
- PhiInputOperand() { this = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap) }
+ PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
override string toString() { result = "Phi" }
@@ -427,8 +459,6 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
class ChiTotalOperand extends NonPhiMemoryOperand {
override ChiTotalOperandTag tag;
- override string toString() { result = "ChiTotal" }
-
final override MemoryAccessKind getMemoryAccess() { result instanceof ChiTotalMemoryAccess }
}
@@ -438,7 +468,5 @@ class ChiTotalOperand extends NonPhiMemoryOperand {
class ChiPartialOperand extends NonPhiMemoryOperand {
override ChiPartialOperandTag tag;
- override string toString() { result = "ChiPartial" }
-
final override MemoryAccessKind getMemoryAccess() { result instanceof ChiPartialMemoryAccess }
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/ConstantAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/ConstantAnalysis.qll
index 40076b817a1..76f52f8334a 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/ConstantAnalysis.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/ConstantAnalysis.qll
@@ -14,8 +14,7 @@ int getConstantValue(Instruction instr) {
or
exists(PhiInstruction phi |
phi = instr and
- result = max(Operand op | op = phi.getAnInputOperand() | getConstantValue(op.getDef())) and
- result = min(Operand op | op = phi.getAnInputOperand() | getConstantValue(op.getDef()))
+ result = unique(Operand op | op = phi.getAnInputOperand() | getConstantValue(op.getDef()))
)
}
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 13d19587135..796fb792366 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
@@ -56,7 +56,7 @@ class ValueNumber extends TValueNumber {
or
this instanceof TInitializeParameterValueNumber and result = "InitializeParameter"
or
- this instanceof TInitializeThisValueNumber and result = "InitializeThis"
+ this instanceof TConstantValueNumber and result = "Constant"
or
this instanceof TStringConstantValueNumber and result = "StringConstant"
or
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll
index 169b0ef7ccf..2467d961892 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll
@@ -7,7 +7,6 @@ newtype TValueNumber =
TInitializeParameterValueNumber(IRFunction irFunc, Language::AST var) {
initializeParameterValueNumber(_, irFunc, var)
} or
- TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or
TConstantValueNumber(IRFunction irFunc, IRType type, string value) {
constantValueNumber(_, irFunc, type, value)
} or
@@ -79,8 +78,6 @@ private predicate numberableInstruction(Instruction instr) {
or
instr instanceof InitializeParameterInstruction
or
- instr instanceof InitializeThisInstruction
- or
instr instanceof ConstantInstruction
or
instr instanceof StringConstantInstruction
@@ -132,10 +129,6 @@ private predicate initializeParameterValueNumber(
instr.getIRVariable().getAST() = var
}
-private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
- instr.getEnclosingIRFunction() = irFunc
-}
-
private predicate constantValueNumber(
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
@@ -268,9 +261,6 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
result = TInitializeParameterValueNumber(irFunc, var)
)
or
- initializeThisValueNumber(instr, irFunc) and
- result = TInitializeThisValueNumber(irFunc)
- or
exists(string value, IRType type |
constantValueNumber(instr, irFunc, type, value) and
result = TConstantValueNumber(irFunc, type, value)
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 e2d3828fc52..1612e0065b7 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
@@ -204,7 +204,7 @@ private predicate isArgumentForParameter(CallInstruction ci, Operand operand, In
init.(InitializeParameterInstruction).getParameter() =
f.getParameter(operand.(PositionalArgumentOperand).getIndex())
or
- init instanceof InitializeThisInstruction and
+ init.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable and
init.getEnclosingFunction() = f and
operand instanceof ThisArgumentOperand
) and
@@ -247,6 +247,10 @@ private predicate resultMayReachReturn(Instruction instr) { operandMayReachRetur
private predicate resultEscapesNonReturn(Instruction instr) {
// The result escapes if it has at least one use that escapes.
operandEscapesNonReturn(instr.getAUse())
+ or
+ // The result also escapes if it is not modeled in SSA, because we do not know where it might be
+ // used.
+ not instr.isResultModeled()
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll
new file mode 100644
index 00000000000..8ec63b7c1cb
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll
@@ -0,0 +1 @@
+import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRInternal.qll
index 4cc52d3bbf9..3a7a08accc0 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRInternal.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRInternal.qll
@@ -1,3 +1,4 @@
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
import SSAConstruction as Construction
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration
+import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Raw
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll
index dc02760c7c4..72bb239c153 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
@@ -6,11 +6,12 @@ private import DebugSSA
bindingset[offset]
private string getKeySuffixForOffset(int offset) {
+ offset >= 0 and
if offset % 2 = 0 then result = "" else result = "_Chi"
}
bindingset[offset]
-private int getIndexForOffset(int offset) { result = offset / 2 }
+private int getIndexForOffset(int offset) { offset >= 0 and result = offset / 2 }
/**
* Property provide that dumps the memory access of each result. Useful for debugging SSA
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConsistency.ql b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConsistency.ql
new file mode 100644
index 00000000000..25f9d5d454a
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConsistency.ql
@@ -0,0 +1,8 @@
+/**
+ * @name Unaliased SSA Consistency Check
+ * @description Performs consistency checks on the SSA construction. This query should have no results.
+ * @kind table
+ * @id cpp/unaliased-ssa-consistency-check
+ */
+
+import SSAConsistency
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSASanity.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll
similarity index 58%
rename from cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSASanity.qll
rename to cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll
index 95e8443b2a3..5686bb439eb 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSASanity.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll
@@ -1,2 +1,2 @@
private import SSAConstruction as SSA
-import SSA::SSASanity
+import SSA::SSAConsistency
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 155934689b6..ae0e03e97da 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll
@@ -1,5 +1,11 @@
import SSAConstructionInternal
-private import SSAConstructionImports
+private import SSAConstructionImports as Imports
+private import Imports::Opcode
+private import Imports::OperandTag
+private import Imports::Overlap
+private import Imports::TInstruction
+private import Imports::RawIR as RawIR
+private import SSAInstructions
private import NewIR
private class OldBlock = Reachability::ReachableBlock;
@@ -10,54 +16,47 @@ import Cached
cached
private module Cached {
+ cached
+ predicate hasPhiInstructionCached(
+ OldInstruction blockStartInstr, Alias::MemoryLocation defLocation
+ ) {
+ exists(OldBlock oldBlock |
+ definitionHasPhiNode(defLocation, oldBlock) and
+ blockStartInstr = oldBlock.getFirstInstruction()
+ )
+ }
+
+ cached
+ predicate hasChiInstructionCached(OldInstruction primaryInstruction) {
+ hasChiNode(_, primaryInstruction)
+ }
+
+ cached
+ predicate hasUnreachedInstructionCached(IRFunction irFunc) {
+ exists(OldInstruction oldInstruction |
+ irFunc = oldInstruction.getEnclosingIRFunction() and
+ Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
+ )
+ }
+
+ class TStageInstruction =
+ TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction;
+
+ cached
+ predicate hasInstruction(TStageInstruction instr) {
+ instr instanceof TRawInstruction and instr instanceof OldInstruction
+ or
+ instr instanceof TPhiInstruction
+ or
+ instr instanceof TChiInstruction
+ or
+ instr instanceof TUnreachedInstruction
+ }
+
private IRBlock getNewBlock(OldBlock oldBlock) {
result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
}
- cached
- predicate functionHasIR(Language::Function func) {
- exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func)
- }
-
- cached
- OldInstruction getOldInstruction(Instruction instr) { instr = WrappedInstruction(result) }
-
- private IRVariable getNewIRVariable(OldIR::IRVariable var) {
- // This is just a type cast. Both classes derive from the same newtype.
- result = var
- }
-
- cached
- newtype TInstruction =
- WrappedInstruction(OldInstruction oldInstruction) {
- not oldInstruction instanceof OldIR::PhiInstruction
- } or
- Phi(OldBlock block, Alias::MemoryLocation defLocation) {
- definitionHasPhiNode(defLocation, block)
- } or
- Chi(OldInstruction oldInstruction) {
- not oldInstruction instanceof OldIR::PhiInstruction and
- hasChiNode(_, oldInstruction)
- } or
- Unreached(Language::Function function) {
- exists(OldInstruction oldInstruction |
- function = oldInstruction.getEnclosingFunction() and
- Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
- )
- }
-
- cached
- predicate hasTempVariable(
- Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type
- ) {
- exists(OldIR::IRTempVariable var |
- var.getEnclosingFunction() = func and
- var.getAST() = ast and
- var.getTag() = tag and
- var.getLanguageType() = type
- )
- }
-
cached
predicate hasModeledMemoryResult(Instruction instruction) {
exists(Alias::getResultMemoryLocation(getOldInstruction(instruction))) or
@@ -67,15 +66,13 @@ private module Cached {
cached
predicate hasConflatedMemoryResult(Instruction instruction) {
- instruction instanceof UnmodeledDefinitionInstruction
- or
instruction instanceof AliasedDefinitionInstruction
or
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
or
// Chi instructions track virtual variables, and therefore a chi instruction is
// conflated if it's associated with the aliased virtual variable.
- exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) |
+ exists(OldInstruction oldInstruction | instruction = getChi(oldInstruction) |
Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof
Alias::AliasedVirtualVariable
)
@@ -83,7 +80,7 @@ private module Cached {
// Phi instructions track locations, and therefore a phi instruction is
// conflated if it's associated with a conflated location.
exists(Alias::MemoryLocation location |
- instruction = Phi(_, location) and
+ instruction = getPhi(_, location) and
not exists(location.getAllocation())
)
}
@@ -127,40 +124,13 @@ private module Cached {
oldInstruction = getOldInstruction(instruction) and
oldOperand = oldInstruction.getAnOperand() and
tag = oldOperand.getOperandTag() and
- (
- (
- if exists(Alias::getOperandMemoryLocation(oldOperand))
- then hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result)
- else (
- result = instruction.getEnclosingIRFunction().getUnmodeledDefinitionInstruction() and
- overlap instanceof MustTotallyOverlap
- )
- )
- or
- // Connect any definitions that are not being modeled in SSA to the
- // `UnmodeledUse` instruction.
- exists(OldInstruction oldDefinition |
- instruction instanceof UnmodeledUseInstruction and
- tag instanceof UnmodeledUseOperandTag and
- oldDefinition = oldOperand.getAnyDef() and
- not exists(Alias::getResultMemoryLocation(oldDefinition)) and
- result = getNewInstruction(oldDefinition) and
- overlap instanceof MustTotallyOverlap
- )
- )
+ hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result)
)
or
- instruction = Chi(getOldInstruction(result)) and
+ instruction = getChi(getOldInstruction(result)) and
tag instanceof ChiPartialOperandTag and
overlap instanceof MustExactlyOverlap
or
- exists(IRFunction f |
- tag instanceof UnmodeledUseOperandTag and
- result = f.getUnmodeledDefinitionInstruction() and
- instruction = f.getUnmodeledUseInstruction() and
- overlap instanceof MustTotallyOverlap
- )
- or
tag instanceof ChiTotalOperandTag and
result = getChiInstructionTotalOperand(instruction) and
overlap instanceof MustExactlyOverlap
@@ -201,13 +171,15 @@ private module Cached {
pragma[noopt]
cached
- Instruction getPhiOperandDefinition(Phi instr, IRBlock newPredecessorBlock, Overlap overlap) {
+ Instruction getPhiOperandDefinition(
+ PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap
+ ) {
exists(
Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock,
OldBlock predBlock, OldBlock defBlock, int defOffset, Alias::MemoryLocation actualDefLocation
|
hasPhiOperandDefinition(defLocation, useLocation, phiBlock, predBlock, defBlock, defOffset) and
- instr = Phi(phiBlock, useLocation) and
+ instr = getPhi(phiBlock, useLocation) and
newPredecessorBlock = getNewBlock(predBlock) and
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
overlap = Alias::getOverlap(actualDefLocation, useLocation)
@@ -220,7 +192,7 @@ private module Cached {
Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation,
OldBlock defBlock, int defRank, int defOffset, OldBlock useBlock, int useRank
|
- chiInstr = Chi(oldInstr) and
+ chiInstr = getChi(oldInstr) and
vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and
hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) and
hasUseAtRank(vvar, useBlock, useRank, oldInstr) and
@@ -232,21 +204,11 @@ private module Cached {
cached
Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
exists(OldBlock oldBlock |
- instr = Phi(oldBlock, _) and
+ instr = getPhi(oldBlock, _) and
result = getNewInstruction(oldBlock.getFirstInstruction())
)
}
- cached
- Language::Expr getInstructionConvertedResultExpression(Instruction instruction) {
- result = getOldInstruction(instruction).getConvertedResultExpression()
- }
-
- cached
- Language::Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
- result = getOldInstruction(instruction).getUnconvertedResultExpression()
- }
-
/*
* This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node,
* that node is its successor in the new successor relation, and the Chi node's successors are
@@ -257,20 +219,20 @@ private module Cached {
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
if hasChiNode(_, getOldInstruction(instruction))
then
- result = Chi(getOldInstruction(instruction)) and
+ result = getChi(getOldInstruction(instruction)) and
kind instanceof GotoEdge
else (
exists(OldInstruction oldInstruction |
oldInstruction = getOldInstruction(instruction) and
(
if Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind)
- then result = Unreached(instruction.getEnclosingFunction())
+ then result = unreachedInstruction(instruction.getEnclosingIRFunction())
else result = getNewInstruction(oldInstruction.getSuccessor(kind))
)
)
or
exists(OldInstruction oldInstruction |
- instruction = Chi(oldInstruction) and
+ instruction = getChi(oldInstruction) and
result = getNewInstruction(oldInstruction.getSuccessor(kind))
)
)
@@ -289,137 +251,73 @@ private module Cached {
// `oldInstruction`, in which case the back edge should come out of the
// chi node instead.
if hasChiNode(_, oldInstruction)
- then instruction = Chi(oldInstruction)
+ then instruction = getChi(oldInstruction)
else instruction = getNewInstruction(oldInstruction)
)
}
cached
- Language::AST getInstructionAST(Instruction instruction) {
- exists(OldInstruction oldInstruction |
- instruction = WrappedInstruction(oldInstruction)
- or
- instruction = Chi(oldInstruction)
- |
- result = oldInstruction.getAST()
+ Language::AST getInstructionAST(Instruction instr) {
+ result = getOldInstruction(instr).getAST()
+ or
+ exists(RawIR::Instruction blockStartInstr |
+ instr = phiInstruction(blockStartInstr, _) and
+ result = blockStartInstr.getAST()
)
or
- exists(OldBlock block |
- instruction = Phi(block, _) and
- result = block.getFirstInstruction().getAST()
+ exists(RawIR::Instruction primaryInstr |
+ instr = chiInstruction(primaryInstr) and
+ result = primaryInstr.getAST()
)
or
- instruction = Unreached(result)
+ exists(IRFunctionBase irFunc |
+ instr = unreachedInstruction(irFunc) and result = irFunc.getFunction()
+ )
}
cached
- Language::LanguageType getInstructionResultType(Instruction instruction) {
- exists(OldInstruction oldInstruction |
- instruction = WrappedInstruction(oldInstruction) and
- result = oldInstruction.getResultLanguageType()
+ Language::LanguageType getInstructionResultType(Instruction instr) {
+ result = instr.(RawIR::Instruction).getResultLanguageType()
+ or
+ exists(Alias::MemoryLocation defLocation |
+ instr = phiInstruction(_, defLocation) and
+ result = defLocation.getType()
)
or
- exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar |
- instruction = Chi(oldInstruction) and
- hasChiNode(vvar, oldInstruction) and
+ exists(Instruction primaryInstr, Alias::VirtualVariable vvar |
+ instr = chiInstruction(primaryInstr) and
+ hasChiNode(vvar, primaryInstr) and
result = vvar.getType()
)
or
- exists(Alias::MemoryLocation location |
- instruction = Phi(_, location) and
- result = location.getType()
+ instr = unreachedInstruction(_) and result = Language::getVoidType()
+ }
+
+ cached
+ Opcode getInstructionOpcode(Instruction instr) {
+ result = getOldInstruction(instr).getOpcode()
+ or
+ instr = phiInstruction(_, _) and result instanceof Opcode::Phi
+ or
+ instr = chiInstruction(_) and result instanceof Opcode::Chi
+ or
+ instr = unreachedInstruction(_) and result instanceof Opcode::Unreached
+ }
+
+ cached
+ IRFunctionBase getInstructionEnclosingIRFunction(Instruction instr) {
+ result = getOldInstruction(instr).getEnclosingIRFunction()
+ or
+ exists(OldInstruction blockStartInstr |
+ instr = phiInstruction(blockStartInstr, _) and
+ result = blockStartInstr.getEnclosingIRFunction()
)
or
- instruction = Unreached(_) and
- result = Language::getVoidType()
- }
-
- cached
- Opcode getInstructionOpcode(Instruction instruction) {
- exists(OldInstruction oldInstruction |
- instruction = WrappedInstruction(oldInstruction) and
- result = oldInstruction.getOpcode()
+ exists(OldInstruction primaryInstr |
+ instr = chiInstruction(primaryInstr) and result = primaryInstr.getEnclosingIRFunction()
)
or
- instruction instanceof Chi and
- result instanceof Opcode::Chi
- or
- instruction instanceof Phi and
- result instanceof Opcode::Phi
- or
- instruction instanceof Unreached and
- result instanceof Opcode::Unreached
- }
-
- cached
- IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
- exists(OldInstruction oldInstruction |
- instruction = WrappedInstruction(oldInstruction)
- or
- instruction = Chi(oldInstruction)
- |
- result.getFunction() = oldInstruction.getEnclosingFunction()
- )
- or
- exists(OldBlock block |
- instruction = Phi(block, _) and
- result.getFunction() = block.getEnclosingFunction()
- )
- or
- instruction = Unreached(result.getFunction())
- }
-
- cached
- IRVariable getInstructionVariable(Instruction instruction) {
- result =
- getNewIRVariable(getOldInstruction(instruction).(OldIR::VariableInstruction).getIRVariable())
- }
-
- cached
- Language::Field getInstructionField(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField()
- }
-
- cached
- int getInstructionIndex(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex()
- }
-
- cached
- Language::Function getInstructionFunction(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
- }
-
- cached
- string getInstructionConstantValue(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::ConstantValueInstruction).getValue()
- }
-
- cached
- Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
- result =
- getOldInstruction(instruction).(OldIR::BuiltInOperationInstruction).getBuiltInOperation()
- }
-
- cached
- Language::LanguageType getInstructionExceptionType(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType()
- }
-
- cached
- int getInstructionElementSize(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::PointerArithmeticInstruction).getElementSize()
- }
-
- cached
- predicate getInstructionInheritance(
- Instruction instruction, Language::Class baseClass, Language::Class derivedClass
- ) {
- exists(OldIR::InheritanceConversionInstruction oldInstr |
- oldInstr = getOldInstruction(instruction) and
- baseClass = oldInstr.getBaseClass() and
- derivedClass = oldInstr.getDerivedClass()
- )
+ instr = unreachedInstruction(result)
}
cached
@@ -430,7 +328,7 @@ private module Cached {
)
or
exists(OldIR::Instruction oldInstruction |
- instruction = Chi(oldInstruction) and
+ instruction = getChi(oldInstruction) and
result = getNewInstruction(oldInstruction)
)
}
@@ -438,6 +336,14 @@ private module Cached {
private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr }
+private OldInstruction getOldInstruction(Instruction instr) { instr = result }
+
+private ChiInstruction getChi(OldInstruction primaryInstr) { result = chiInstruction(primaryInstr) }
+
+private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) {
+ result = phiInstruction(defBlock.getFirstInstruction(), defLocation)
+}
+
/**
* Holds if instruction `def` needs to have a `Chi` instruction inserted after it, to account for a partial definition
* of a virtual variable. The `Chi` instruction provides a definition of the entire virtual variable of which the
@@ -617,7 +523,7 @@ module DefUse {
|
// An odd offset corresponds to the `Chi` instruction.
defOffset = oldOffset * 2 + 1 and
- result = Chi(oldInstr) and
+ result = getChi(oldInstr) and
(
defLocation = Alias::getResultMemoryLocation(oldInstr) or
defLocation = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable()
@@ -636,7 +542,7 @@ module DefUse {
or
defOffset = -1 and
hasDefinition(_, defLocation, defBlock, defOffset) and
- result = Phi(defBlock, defLocation) and
+ result = getPhi(defBlock, defLocation) and
actualDefLocation = defLocation
}
@@ -920,7 +826,7 @@ private module CachedForDebugging {
)
or
exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity |
- instr = Phi(phiBlock, location) and
+ instr = getPhi(phiBlock, location) and
result =
"Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and
if location instanceof Alias::VirtualVariable
@@ -930,7 +836,7 @@ private module CachedForDebugging {
else specificity = "s"
)
or
- instr = Unreached(_) and
+ instr = unreachedInstruction(_) and
result = "Unreached"
}
@@ -941,7 +847,10 @@ private module CachedForDebugging {
}
}
-module SSASanity {
+module SSAConsistency {
+ /**
+ * Holds if a `MemoryOperand` has more than one `MemoryLocation` assigned by alias analysis.
+ */
query predicate multipleOperandMemoryLocations(
OldIR::MemoryOperand operand, string message, OldIR::IRFunction func, string funcText
) {
@@ -954,6 +863,9 @@ module SSASanity {
)
}
+ /**
+ * Holds if a `MemoryLocation` does not have an associated `VirtualVariable`.
+ */
query predicate missingVirtualVariableForMemoryLocation(
Alias::MemoryLocation location, string message, OldIR::IRFunction func, string funcText
) {
@@ -962,4 +874,41 @@ module SSASanity {
funcText = Language::getIdentityString(func.getFunction()) and
message = "Memory location has no virtual variable in function '$@'."
}
+
+ /**
+ * Holds if a `MemoryLocation` is a member of more than one `VirtualVariable`.
+ */
+ query predicate multipleVirtualVariablesForMemoryLocation(
+ Alias::MemoryLocation location, string message, OldIR::IRFunction func, string funcText
+ ) {
+ exists(int vvarCount |
+ vvarCount = strictcount(location.getVirtualVariable()) and
+ vvarCount > 1 and
+ func = location.getIRFunction() and
+ funcText = Language::getIdentityString(func.getFunction()) and
+ message =
+ "Memory location has " + vvarCount.toString() + " virtual variables in function '$@': (" +
+ concat(Alias::VirtualVariable vvar |
+ vvar = location.getVirtualVariable()
+ |
+ vvar.toString(), ", "
+ ) + ")."
+ )
+ }
+}
+
+/**
+ * Provides the portion of the parameterized IR interface that is used to construct the SSA stages
+ * of the IR. The raw stage of the IR does not expose these predicates.
+ * These predicates are all just aliases for predicates defined in the `Cached` module. This ensures
+ * that all of SSA construction will be evaluated in the same stage.
+ */
+module SSA {
+ class MemoryLocation = Alias::MemoryLocation;
+
+ predicate hasPhiInstruction = Cached::hasPhiInstructionCached/2;
+
+ predicate hasChiInstruction = Cached::hasChiInstructionCached/1;
+
+ predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1;
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll
index 00f12020a29..f347df86ba1 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll
@@ -1,3 +1,5 @@
-import semmle.code.cpp.ir.implementation.Opcode
-import semmle.code.cpp.ir.implementation.internal.OperandTag
-import semmle.code.cpp.ir.internal.Overlap
+import semmle.code.cpp.ir.implementation.Opcode as Opcode
+import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
+import semmle.code.cpp.ir.internal.Overlap as Overlap
+import semmle.code.cpp.ir.implementation.internal.TInstruction as TInstruction
+import semmle.code.cpp.ir.implementation.raw.IR as RawIR
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll
index 4cfbdfe831e..73b08d1286b 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll
@@ -2,5 +2,7 @@ import semmle.code.cpp.ir.implementation.raw.IR as OldIR
import semmle.code.cpp.ir.implementation.raw.internal.reachability.ReachableBlock as Reachability
import semmle.code.cpp.ir.implementation.raw.internal.reachability.Dominance as Dominance
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as NewIR
+import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as RawStage
+import semmle.code.cpp.ir.implementation.internal.TInstruction::UnaliasedSSAInstructions as SSAInstructions
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
import SimpleSSA as Alias
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSASanity.ql b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSASanity.ql
deleted file mode 100644
index 1b5ee80b603..00000000000
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSASanity.ql
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * @name Unaliased SSA Sanity Check
- * @description Performs sanity checks on the SSA construction. This query should have no results.
- * @kind table
- * @id cpp/unaliased-ssa-sanity-check
- */
-
-import SSASanity
diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/ASTValueNumbering.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/ASTValueNumbering.qll
index 90e9b4ef920..dcc013fd387 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/internal/ASTValueNumbering.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/internal/ASTValueNumbering.qll
@@ -84,8 +84,6 @@ class GVN extends TValueNumber {
or
this instanceof TInitializeParameterValueNumber and result = "InitializeParameter"
or
- this instanceof TInitializeThisValueNumber and result = "InitializeThis"
- or
this instanceof TStringConstantValueNumber and result = "StringConstant"
or
this instanceof TFieldAddressValueNumber and result = "FieldAddress"
diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll
index 9e8044ede4f..2ce23f098a2 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll
@@ -1,7 +1,7 @@
private import cpp
private import semmle.code.cpp.Print
private import semmle.code.cpp.ir.implementation.IRType
-private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
+private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Raw
private int getPointerSize() { result = max(any(NullPointerType t).getSize()) }
@@ -86,9 +86,15 @@ predicate hasUnsignedIntegerType(int byteSize) {
}
/**
- * Holds if an `IRFloatingPointType` with the specified `byteSize` should exist.
+ * Holds if an `IRFloatingPointType` with the specified size, base, and type domain should exist.
*/
-predicate hasFloatingPointType(int byteSize) { byteSize = any(FloatingPointType type).getSize() }
+predicate hasFloatingPointType(int byteSize, int base, TypeDomain domain) {
+ exists(FloatingPointType type |
+ byteSize = type.getSize() and
+ base = type.getBase() and
+ domain = type.getDomain()
+ )
+}
private predicate isPointerIshType(Type type) {
type instanceof PointerType
@@ -137,7 +143,7 @@ private predicate isOpaqueType(Type type) {
predicate hasOpaqueType(Type tag, int byteSize) {
isOpaqueType(tag) and byteSize = getTypeSize(tag)
or
- tag instanceof UnknownType and IRConstruction::needsUnknownOpaqueType(byteSize)
+ tag instanceof UnknownType and Raw::needsUnknownOpaqueType(byteSize)
}
/**
@@ -159,8 +165,13 @@ private IRType getIRTypeForPRValue(Type type) {
isUnsignedIntegerType(unspecifiedType) and
result.(IRUnsignedIntegerType).getByteSize() = type.getSize()
or
- unspecifiedType instanceof FloatingPointType and
- result.(IRFloatingPointType).getByteSize() = type.getSize()
+ exists(FloatingPointType floatType, IRFloatingPointType irFloatType |
+ floatType = unspecifiedType and
+ irFloatType = result and
+ irFloatType.getByteSize() = floatType.getSize() and
+ irFloatType.getBase() = floatType.getBase() and
+ irFloatType.getDomain() = floatType.getDomain()
+ )
or
isPointerIshType(unspecifiedType) and result.(IRAddressType).getByteSize() = getTypeSize(type)
or
@@ -180,7 +191,7 @@ private newtype TCppType =
TPRValueType(Type type) { exists(getIRTypeForPRValue(type)) } or
TFunctionGLValueType() or
TGLValueAddressType(Type type) or
- TUnknownOpaqueType(int byteSize) { IRConstruction::needsUnknownOpaqueType(byteSize) } or
+ TUnknownOpaqueType(int byteSize) { Raw::needsUnknownOpaqueType(byteSize) } or
TUnknownType()
/**
@@ -192,6 +203,7 @@ private newtype TCppType =
* of a `VariableAddress` where the variable is of reference type)
*/
class CppType extends TCppType {
+ /** Gets a textual representation of this type. */
string toString() { none() }
/** Gets a string used in IR dumps */
@@ -213,6 +225,10 @@ class CppType extends TCppType {
*/
predicate hasType(Type type, boolean isGLValue) { none() }
+ /**
+ * Holds if this type represents the C++ type `type`. If `isGLValue` is `true`, then this type
+ * represents a glvalue of type `type`. Otherwise, it represents a prvalue of type `type`.
+ */
final predicate hasUnspecifiedType(Type type, boolean isGLValue) {
exists(Type specifiedType |
hasType(specifiedType, isGLValue) and
@@ -346,7 +362,7 @@ CppType getTypeForPRValueOrUnknown(Type type) {
/**
* Gets the `CppType` that represents a glvalue of type `type`.
*/
-CppType getTypeForGLValue(Type type) { result.hasType(type, true) }
+CppGLValueAddressType getTypeForGLValue(Type type) { result.hasType(type, true) }
/**
* Gets the `CppType` that represents a prvalue of type `int`.
@@ -438,15 +454,37 @@ CppPRValueType getCanonicalUnsignedIntegerType(int byteSize) {
}
/**
- * Gets the `CppType` that is the canonical type for an `IRFloatingPointType` with the specified
- * `byteSize`.
+ * Gets the sort priority of a `RealNumberType` base on its precision.
*/
-CppPRValueType getCanonicalFloatingPointType(int byteSize) {
+private int getPrecisionPriority(RealNumberType type) {
+ // Prefer `double`, `float`, `long double` in that order.
+ if type instanceof DoubleType
+ then result = 4
+ else
+ if type instanceof FloatType
+ then result = 3
+ else
+ if type instanceof LongDoubleType
+ then result = 2
+ else
+ // If we get this far, prefer non-extended-precision types.
+ if not type.isExtendedPrecision()
+ then result = 1
+ else result = 0
+}
+
+/**
+ * Gets the `CppType` that is the canonical type for an `IRFloatingPointType` with the specified
+ * size, base, and type domain.
+ */
+CppPRValueType getCanonicalFloatingPointType(int byteSize, int base, TypeDomain domain) {
result =
TPRValueType(max(FloatingPointType type |
- type.getSize() = byteSize
+ type.getSize() = byteSize and
+ type.getBase() = base and
+ type.getDomain() = domain
|
- type order by type.toString() desc
+ type order by getPrecisionPriority(type.getRealType()), type.toString() desc
))
}
@@ -506,7 +544,10 @@ string getOpaqueTagIdentityString(Type tag) {
result = getTypeIdentityString(tag)
}
-module LanguageTypeSanity {
+module LanguageTypeConsistency {
+ /**
+ * Consistency query to detect C++ `Type` objects which have no corresponding `CppType` object.
+ */
query predicate missingCppType(Type type, string message) {
not exists(getTypeForPRValue(type)) and
exists(type.getSize()) and
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 6e88e711711..f047d6c4753 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/internal/IRCppLanguage.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/internal/IRCppLanguage.qll
@@ -9,6 +9,14 @@ class LanguageType = CppType;
class OpaqueTypeTag = Cpp::Type;
+class TypeDomain = Cpp::TypeDomain;
+
+class RealDomain = Cpp::RealDomain;
+
+class ComplexDomain = Cpp::ComplexDomain;
+
+class ImaginaryDomain = Cpp::ImaginaryDomain;
+
class Function = Cpp::Function;
class Location = Cpp::Location;
diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/IRUtilities.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/IRUtilities.qll
index 53c15663796..6b2b4c918af 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/internal/IRUtilities.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/internal/IRUtilities.qll
@@ -23,17 +23,20 @@ Type getVariableType(Variable v) {
then
result = getDecayedType(declaredType)
or
- not exists(getDecayedType(declaredType)) and result = declaredType
+ not exists(getDecayedType(declaredType)) and result = v.getType()
else
if declaredType instanceof ArrayType and not declaredType.(ArrayType).hasArraySize()
then
- result = v.getInitializer().getExpr().getUnspecifiedType()
+ result = v.getInitializer().getExpr().getType()
or
- not exists(v.getInitializer()) and result = declaredType
- else result = declaredType
+ not exists(v.getInitializer()) and result = v.getType()
+ else result = v.getType()
)
}
+/**
+ * Holds if the database contains a `case` label with the specified minimum and maximum value.
+ */
predicate hasCaseEdge(SwitchCase switchCase, string minValue, string maxValue) {
minValue = switchCase.getExpr().getFullyConverted().getValue() and
if exists(switchCase.getEndExpr())
diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll
index 6034ebc5674..4af31745ab2 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll
@@ -1,3 +1,15 @@
+/**
+ * Provides predicates for manipulating integer constants that are tracked by constant folding and
+ * similar analyses.
+ */
+
+/**
+ * An alias used to represent the constant value of an integer, if one can be determined. If no
+ * single constant value can be determined, or if the constant value is out of the representable
+ * range, it will be represented as the special value `unknown()`. This allows `IntValue` to be used
+ * in contexts where there must always be a value for the `IntValue`, even if no constant value is
+ * known.
+ */
class IntValue = int;
/**
diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/TempVariableTag.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/TempVariableTag.qll
index 2cd44a08f9e..c3328051286 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/internal/TempVariableTag.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/internal/TempVariableTag.qll
@@ -3,7 +3,8 @@ newtype TTempVariableTag =
ReturnValueTempVar() or
ThrowTempVar() or
LambdaTempVar() or
- EllipsisTempVar()
+ EllipsisTempVar() or
+ ThisTempVar()
string getTempVariableTagId(TTempVariableTag tag) {
tag = ConditionValueTempVar() and result = "CondVal"
@@ -15,4 +16,6 @@ string getTempVariableTagId(TTempVariableTag tag) {
tag = LambdaTempVar() and result = "Lambda"
or
tag = EllipsisTempVar() and result = "Ellipsis"
+ or
+ tag = ThisTempVar() and result = "This"
}
diff --git a/cpp/ql/src/semmle/code/cpp/models/Models.qll b/cpp/ql/src/semmle/code/cpp/models/Models.qll
index f02d05be711..82ae1fdc4f0 100644
--- a/cpp/ql/src/semmle/code/cpp/models/Models.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/Models.qll
@@ -14,3 +14,4 @@ private import implementations.Strdup
private import implementations.Strftime
private import implementations.StdString
private import implementations.Swap
+private import implementations.GetDelim
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll
index f6f7ab279a6..782800d0fa2 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll
@@ -89,6 +89,18 @@ class MallocAllocationFunction extends AllocationFunction {
or
// kmem_zalloc(size, flags)
name = "kmem_zalloc" and sizeArg = 0
+ or
+ // CRYPTO_malloc(size_t num, const char *file, int line)
+ name = "CRYPTO_malloc" and sizeArg = 0
+ or
+ // CRYPTO_zalloc(size_t num, const char *file, int line)
+ name = "CRYPTO_zalloc" and sizeArg = 0
+ or
+ // CRYPTO_secure_malloc(size_t num, const char *file, int line)
+ name = "CRYPTO_secure_malloc" and sizeArg = 0
+ or
+ // CRYPTO_secure_zalloc(size_t num, const char *file, int line)
+ name = "CRYPTO_secure_zalloc" and sizeArg = 0
)
)
}
@@ -169,6 +181,9 @@ class ReallocAllocationFunction extends AllocationFunction {
or
// CoTaskMemRealloc(ptr, size)
name = "CoTaskMemRealloc" and sizeArg = 1 and reallocArg = 0
+ or
+ // CRYPTO_realloc(void *addr, size_t num, const char *file, int line);
+ name = "CRYPTO_realloc" and sizeArg = 1 and reallocArg = 0
)
)
}
@@ -255,6 +270,35 @@ class OperatorNewAllocationFunction extends AllocationFunction {
}
}
+/**
+ * Holds if `sizeExpr` is an expression consisting of a subexpression
+ * `lengthExpr` multiplied by a constant `sizeof` that is the result of a
+ * `sizeof()` expression. Alternatively if there isn't a suitable `sizeof()`
+ * expression, `lengthExpr = sizeExpr` and `sizeof = 1`. For example:
+ * ```
+ * malloc(a * 2 * sizeof(char32_t));
+ * ```
+ * In this case if the `sizeExpr` is the argument to `malloc`, the `lengthExpr`
+ * is `a * 2` and `sizeof` is `4`.
+ */
+private predicate deconstructSizeExpr(Expr sizeExpr, Expr lengthExpr, int sizeof) {
+ exists(SizeofOperator sizeofOp |
+ sizeofOp = sizeExpr.(MulExpr).getAnOperand() and
+ lengthExpr = sizeExpr.(MulExpr).getAnOperand() and
+ not lengthExpr instanceof SizeofOperator and
+ sizeof = sizeofOp.getValue().toInt()
+ )
+ or
+ not exists(SizeofOperator sizeofOp, Expr lengthOp |
+ sizeofOp = sizeExpr.(MulExpr).getAnOperand() and
+ lengthOp = sizeExpr.(MulExpr).getAnOperand() and
+ not lengthOp instanceof SizeofOperator and
+ exists(sizeofOp.getValue().toInt())
+ ) and
+ lengthExpr = sizeExpr and
+ sizeof = 1
+}
+
/**
* An allocation expression that is a function call, such as call to `malloc`.
*/
@@ -272,7 +316,17 @@ class CallAllocationExpr extends AllocationExpr, FunctionCall {
not exists(NewOrNewArrayExpr new | new.getAllocatorCall() = this)
}
- override Expr getSizeExpr() { result = getArgument(target.getSizeArg()) }
+ override Expr getSizeExpr() {
+ exists(Expr sizeExpr | sizeExpr = getArgument(target.getSizeArg()) |
+ if exists(target.getSizeMult())
+ then result = sizeExpr
+ else
+ exists(Expr lengthExpr |
+ deconstructSizeExpr(sizeExpr, lengthExpr, _) and
+ result = lengthExpr
+ )
+ )
+ }
override int getSizeMult() {
// malloc with multiplier argument that is a constant
@@ -280,13 +334,19 @@ class CallAllocationExpr extends AllocationExpr, FunctionCall {
or
// malloc with no multiplier argument
not exists(target.getSizeMult()) and
- result = 1
+ deconstructSizeExpr(getArgument(target.getSizeArg()), _, result)
}
override int getSizeBytes() { result = getSizeExpr().getValue().toInt() * getSizeMult() }
override Expr getReallocPtr() { result = getArgument(target.getReallocPtrArg()) }
+ override Type getAllocatedElementType() {
+ result =
+ this.getFullyConverted().getType().stripTopLevelSpecifiers().(PointerType).getBaseType() and
+ not result instanceof VoidType
+ }
+
override predicate requiresDealloc() { target.requiresDealloc() }
}
@@ -298,6 +358,8 @@ class NewAllocationExpr extends AllocationExpr, NewExpr {
override int getSizeBytes() { result = getAllocatedType().getSize() }
+ override Type getAllocatedElementType() { result = getAllocatedType() }
+
override predicate requiresDealloc() { not exists(getPlacementPointer()) }
}
@@ -318,6 +380,8 @@ class NewArrayAllocationExpr extends AllocationExpr, NewArrayExpr {
result = getAllocatedElementType().getSize()
}
+ override Type getAllocatedElementType() { result = NewArrayExpr.super.getAllocatedElementType() }
+
override int getSizeBytes() { result = getAllocatedType().getSize() }
override predicate requiresDealloc() { not exists(getPlacementPointer()) }
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll
index 980645df031..2ef355bf398 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll
@@ -19,6 +19,10 @@ class StandardDeallocationFunction extends DeallocationFunction {
name = "free" and freedArg = 0
or
name = "realloc" and freedArg = 0
+ or
+ name = "CRYPTO_free" and freedArg = 0
+ or
+ name = "CRYPTO_secure_free" and freedArg = 0
)
or
hasGlobalOrStdName(name) and
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Fread.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Fread.qll
index 8fdf17ead02..4813e5c0066 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Fread.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Fread.qll
@@ -1,6 +1,7 @@
import semmle.code.cpp.models.interfaces.Alias
+import semmle.code.cpp.models.interfaces.FlowSource
-class Fread extends AliasFunction {
+class Fread extends AliasFunction, RemoteFlowFunction {
Fread() { this.hasGlobalName("fread") }
override predicate parameterNeverEscapes(int n) {
@@ -11,4 +12,9 @@ class Fread extends AliasFunction {
override predicate parameterEscapesOnlyViaReturn(int n) { none() }
override predicate parameterIsAlwaysReturned(int n) { none() }
+
+ override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
+ output.isParameterDeref(0) and
+ description = "String read by " + this.getName()
+ }
}
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/GetDelim.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/GetDelim.qll
new file mode 100644
index 00000000000..3f376cf2ff0
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/GetDelim.qll
@@ -0,0 +1,40 @@
+import semmle.code.cpp.models.interfaces.Taint
+import semmle.code.cpp.models.interfaces.Alias
+import semmle.code.cpp.models.interfaces.SideEffect
+import semmle.code.cpp.models.interfaces.FlowSource
+
+/**
+ * The standard functions `getdelim`, `getwdelim` and the glibc variant `__getdelim`.
+ */
+class GetDelimFunction extends TaintFunction, AliasFunction, SideEffectFunction, RemoteFlowFunction {
+ GetDelimFunction() { hasGlobalName(["getdelim", "getwdelim", "__getdelim"]) }
+
+ override predicate hasTaintFlow(FunctionInput i, FunctionOutput o) {
+ i.isParameter(3) and o.isParameterDeref(0)
+ }
+
+ override predicate parameterNeverEscapes(int index) { index = [0, 1, 3] }
+
+ override predicate parameterEscapesOnlyViaReturn(int index) { none() }
+
+ override predicate parameterIsAlwaysReturned(int index) { none() }
+
+ override predicate hasOnlySpecificReadSideEffects() { any() }
+
+ override predicate hasOnlySpecificWriteSideEffects() { any() }
+
+ override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
+ i = [0, 1] and
+ buffer = false and
+ mustWrite = false
+ }
+
+ override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
+ i = 3 and buffer = false
+ }
+
+ override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
+ output.isParameterDeref(0) and
+ description = "String read by " + this.getName()
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll
index aa4091fd7f2..702f56931e7 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll
@@ -3,12 +3,13 @@ import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.Alias
import semmle.code.cpp.models.interfaces.SideEffect
+import semmle.code.cpp.models.interfaces.FlowSource
/**
* The standard functions `gets` and `fgets`.
*/
class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, AliasFunction,
- SideEffectFunction {
+ SideEffectFunction, RemoteFlowFunction {
GetsFunction() {
exists(string name | hasGlobalOrStdName(name) |
name = "gets" or // gets(str)
@@ -42,4 +43,22 @@ class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, Alias
buffer = true and
mustWrite = true
}
+
+ override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
+ output.isParameterDeref(0) and
+ description = "String read by " + this.getName()
+ }
+
+ override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
+ not hasGlobalOrStdName("gets") and
+ bufParam = 0 and
+ countParam = 1
+ }
+
+ override predicate hasArrayWithUnknownSize(int bufParam) {
+ hasGlobalOrStdName("gets") and
+ bufParam = 0
+ }
+
+ override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
}
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll
index 403e8fae1de..b5047c25e85 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll
@@ -19,7 +19,7 @@ class Printf extends FormattingFunction, AliasFunction {
override int getFormatParameterIndex() { result = 0 }
- override predicate isWideCharDefault() {
+ deprecated override predicate isWideCharDefault() {
hasGlobalOrStdName("wprintf") or
hasGlobalName("wprintf_s")
}
@@ -47,7 +47,7 @@ class Fprintf extends FormattingFunction {
override int getFormatParameterIndex() { result = 1 }
- override predicate isWideCharDefault() { hasGlobalOrStdName("fwprintf") }
+ deprecated override predicate isWideCharDefault() { hasGlobalOrStdName("fwprintf") }
override int getOutputParameterIndex() { result = 0 }
}
@@ -70,7 +70,7 @@ class Sprintf extends FormattingFunction {
not exists(getDefinition().getFile().getRelativePath())
}
- override predicate isWideCharDefault() {
+ deprecated override predicate isWideCharDefault() {
getParameter(getFormatParameterIndex())
.getType()
.getUnspecifiedType()
@@ -136,7 +136,7 @@ class Snprintf extends FormattingFunction {
else result = getFirstFormatArgumentIndex() - 1
}
- override predicate isWideCharDefault() {
+ deprecated override predicate isWideCharDefault() {
getParameter(getFormatParameterIndex())
.getType()
.getUnspecifiedType()
@@ -201,7 +201,7 @@ class StringCchPrintf extends FormattingFunction {
if getName().matches("%Ex") then result = 5 else result = 2
}
- override predicate isWideCharDefault() {
+ deprecated override predicate isWideCharDefault() {
getParameter(getFormatParameterIndex())
.getType()
.getUnspecifiedType()
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 c831a8bf357..8e1739fe6a7 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Pure.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Pure.qll
@@ -20,9 +20,7 @@ class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunction, SideE
name = "strpbrk" or
name = "strcmp" or
name = "strcspn" or
- name = "strlen" or
name = "strncmp" or
- name = "strnlen" or
name = "strrchr" or
name = "strspn" or
name = "strtod" or
@@ -30,16 +28,7 @@ class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunction, SideE
name = "strtol" or
name = "strtoll" or
name = "strtoq" or
- name = "strtoul" or
- name = "wcslen"
- )
- or
- hasGlobalName(name) and
- (
- name = "_mbslen" or
- name = "_mbslen_l" or
- name = "_mbstrlen" or
- name = "_mbstrlen_l"
+ name = "strtoul"
)
)
}
@@ -90,6 +79,52 @@ class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunction, SideE
}
}
+class StrLenFunction extends AliasFunction, ArrayFunction, SideEffectFunction {
+ StrLenFunction() {
+ exists(string name |
+ hasGlobalOrStdName(name) and
+ (
+ name = "strlen" or
+ name = "strnlen" or
+ name = "wcslen"
+ )
+ or
+ hasGlobalName(name) and
+ (
+ name = "_mbslen" or
+ name = "_mbslen_l" or
+ name = "_mbstrlen" or
+ name = "_mbstrlen_l"
+ )
+ )
+ }
+
+ override predicate hasArrayInput(int bufParam) {
+ getParameter(bufParam).getUnspecifiedType() instanceof PointerType
+ }
+
+ override predicate hasArrayWithNullTerminator(int bufParam) {
+ getParameter(bufParam).getUnspecifiedType() instanceof PointerType
+ }
+
+ override predicate parameterNeverEscapes(int i) {
+ getParameter(i).getUnspecifiedType() instanceof PointerType
+ }
+
+ override predicate parameterEscapesOnlyViaReturn(int i) { none() }
+
+ override predicate parameterIsAlwaysReturned(int i) { none() }
+
+ override predicate hasOnlySpecificReadSideEffects() { any() }
+
+ override predicate hasOnlySpecificWriteSideEffects() { any() }
+
+ override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
+ getParameter(i).getUnspecifiedType() instanceof PointerType and
+ buffer = true
+ }
+}
+
class PureFunction extends TaintFunction, SideEffectFunction {
PureFunction() {
exists(string name |
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll
index 80fe85e9f13..9c6ebd1a877 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll
@@ -4,6 +4,7 @@ import semmle.code.cpp.models.interfaces.Taint
* The `std::basic_string` constructor(s).
*/
class StdStringConstructor extends TaintFunction {
+ pragma[noinline]
StdStringConstructor() { this.hasQualifiedName("std", "basic_string", "basic_string") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
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 b4c7f69bde4..3e58fd8c258 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strftime.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strftime.qll
@@ -10,10 +10,7 @@ class Strftime extends TaintFunction, ArrayFunction {
input.isParameterDeref(2) or
input.isParameterDeref(3)
) and
- (
- output.isParameterDeref(0) or
- output.isReturnValue()
- )
+ output.isParameterDeref(0)
}
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 2 }
diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll
index 17aefd8d3c8..81a40cd349a 100644
--- a/cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll
@@ -72,6 +72,11 @@ abstract class AllocationExpr extends Expr {
*/
Expr getReallocPtr() { none() }
+ /**
+ * Gets the type of the elements that are allocated, if it can be determined.
+ */
+ Type getAllocatedElementType() { none() }
+
/**
* Whether or not this allocation requires a corresponding deallocation of
* some sort (most do, but `alloca` for example does not). If it is unclear,
diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/FlowSource.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/FlowSource.qll
new file mode 100644
index 00000000000..2c9effaff7c
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/FlowSource.qll
@@ -0,0 +1,21 @@
+/**
+ * Provides a class for modeling functions that return data from potentially untrusted sources. To use
+ * this QL library, create a QL class extending `DataFlowFunction` with a
+ * characteristic predicate that selects the function or set of functions you
+ * are modeling. Within that class, override the predicates provided by
+ * `RemoteFlowFunction` to match the flow within that function.
+ */
+
+import cpp
+import FunctionInputsAndOutputs
+import semmle.code.cpp.models.Models
+
+/**
+ * A library function which returns data read from a network connection.
+ */
+abstract class RemoteFlowFunction extends Function {
+ /**
+ * Holds if remote data described by `description` flows from `output` of a call to this function.
+ */
+ abstract predicate hasRemoteFlowSource(FunctionOutput output, string description);
+}
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 7227e6e9513..78153ca0ec6 100644
--- a/cpp/ql/src/semmle/code/cpp/models/interfaces/FormattingFunction.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/FormattingFunction.qll
@@ -1,6 +1,6 @@
/**
* Provides a class for modeling `printf`-style formatting functions. To use
- * this QL library, create a QL class extending `DataFlowFunction` with a
+ * this QL library, create a QL class extending `FormattingFunction` with a
* characteristic predicate that selects the function or set of functions you
* are modeling. Within that class, override the predicates provided by
* `FormattingFunction` to match the flow within that function.
diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Taint.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Taint.qll
index 02e7c8e78a1..c619f2efaa5 100644
--- a/cpp/ql/src/semmle/code/cpp/models/interfaces/Taint.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Taint.qll
@@ -15,6 +15,9 @@ import semmle.code.cpp.models.Models
* A library function for which a taint-tracking library should propagate taint
* from a parameter or qualifier to an output buffer, return value, or qualifier.
*
+ * An expression is tainted if it could be influenced by an attacker to have
+ * an unusual value.
+ *
* Note that this does not include direct copying of values; that is covered by
* DataFlowModel.qll. If a value is sometimes copied in full, and sometimes
* altered (for example copying a string with `strncpy`), this is also considered
diff --git a/cpp/ql/src/semmle/code/cpp/padding/SanityCheck.ql b/cpp/ql/src/semmle/code/cpp/padding/ConsistencyCheck.ql
similarity index 75%
rename from cpp/ql/src/semmle/code/cpp/padding/SanityCheck.ql
rename to cpp/ql/src/semmle/code/cpp/padding/ConsistencyCheck.ql
index cbd5410ffde..ac4102e6750 100644
--- a/cpp/ql/src/semmle/code/cpp/padding/SanityCheck.ql
+++ b/cpp/ql/src/semmle/code/cpp/padding/ConsistencyCheck.ql
@@ -1,14 +1,14 @@
/**
- * @name Padding Sanity Check
- * @description Performs sanity checks for the padding library. This query should have no results.
+ * @name Padding Consistency Check
+ * @description Performs consistency checks for the padding library. This query should have no results.
* @kind table
- * @id cpp/padding-sanity-check
+ * @id cpp/padding-consistency-check
*/
import Padding
/*
- * Sanity-check: Find discrepancies between computed and actual size on LP64.
+ * Consistency-check: Find discrepancies between computed and actual size on LP64.
*/
/*
diff --git a/cpp/ql/src/semmle/code/cpp/padding/Padding.qll b/cpp/ql/src/semmle/code/cpp/padding/Padding.qll
index a94c6e501a4..7446569451d 100644
--- a/cpp/ql/src/semmle/code/cpp/padding/Padding.qll
+++ b/cpp/ql/src/semmle/code/cpp/padding/Padding.qll
@@ -74,6 +74,8 @@ abstract class Architecture extends string {
or
t instanceof WideCharType and result = wideCharSize()
or
+ t instanceof Char8Type and result = 8
+ or
t instanceof Char16Type and result = 16
or
t instanceof Char32Type and result = 32
@@ -155,6 +157,8 @@ abstract class Architecture extends string {
or
t instanceof WideCharType and result = wideCharSize()
or
+ t instanceof Char8Type and result = 8
+ or
t instanceof Char16Type and result = 16
or
t instanceof Char32Type and result = 32
diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/Bound.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/Bound.qll
index fe0e211087c..9e56794233f 100644
--- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/Bound.qll
+++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/Bound.qll
@@ -68,7 +68,7 @@ class ValueNumberBound extends Bound, TBoundValueNumber {
ValueNumberBound() { this = TBoundValueNumber(vn) }
- /** Gets the SSA variable that equals this bound. */
+ /** Gets an `Instruction` that equals this bound. */
override Instruction getInstruction(int delta) {
this = TBoundValueNumber(valueNumber(result)) and delta = 0
}
@@ -76,4 +76,7 @@ class ValueNumberBound extends Bound, TBoundValueNumber {
override string toString() { result = vn.getExampleInstruction().toString() }
override Location getLocation() { result = vn.getLocation() }
+
+ /** Gets the value number that equals this bound. */
+ ValueNumber getValueNumber() { result = vn }
}
diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll
index af42bb755e3..a79df3c37d7 100644
--- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll
+++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll
@@ -241,38 +241,6 @@ class CondReason extends Reason, TCondReason {
override string toString() { result = getCond().toString() }
}
-/**
- * Holds if a cast from `fromtyp` to `totyp` can be ignored for the purpose of
- * range analysis.
- */
-pragma[inline]
-private predicate safeCast(IntegralType fromtyp, IntegralType totyp) {
- fromtyp.getSize() < totyp.getSize() and
- (
- fromtyp.isUnsigned()
- or
- totyp.isSigned()
- )
- or
- fromtyp.getSize() <= totyp.getSize() and
- (
- fromtyp.isSigned() and
- totyp.isSigned()
- or
- fromtyp.isUnsigned() and
- totyp.isUnsigned()
- )
-}
-
-private class SafeCastInstruction extends ConvertInstruction {
- SafeCastInstruction() {
- safeCast(getUnary().getResultType(), getResultType())
- or
- getResultType() instanceof PointerType and
- getUnary().getResultType() instanceof PointerType
- }
-}
-
/**
* Holds if `typ` is a small integral type with the given lower and upper bounds.
*/
diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeUtils.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeUtils.qll
index 8935420da8a..ee790404559 100644
--- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeUtils.qll
+++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeUtils.qll
@@ -80,3 +80,55 @@ predicate backEdge(PhiInstruction phi, PhiInputOperand op) {
phi.getAnOperand() = op and
phi.getBlock() = op.getPredecessorBlock().getBackEdgeSuccessor(_)
}
+
+/**
+ * Holds if a cast from `fromtyp` to `totyp` can be ignored for the purpose of
+ * range analysis.
+ */
+pragma[inline]
+private predicate safeCast(IntegralType fromtyp, IntegralType totyp) {
+ fromtyp.getSize() < totyp.getSize() and
+ (
+ fromtyp.isUnsigned()
+ or
+ totyp.isSigned()
+ )
+ or
+ fromtyp.getSize() <= totyp.getSize() and
+ (
+ fromtyp.isSigned() and
+ totyp.isSigned()
+ or
+ fromtyp.isUnsigned() and
+ totyp.isUnsigned()
+ )
+}
+
+/**
+ * A `ConvertInstruction` which casts from one pointer type to another.
+ */
+class PtrToPtrCastInstruction extends ConvertInstruction {
+ PtrToPtrCastInstruction() {
+ getResultType() instanceof PointerType and
+ getUnary().getResultType() instanceof PointerType
+ }
+}
+
+/**
+ * A `ConvertInstruction` which casts from one integer type to another in a way
+ * that cannot overflow or underflow.
+ */
+class SafeIntCastInstruction extends ConvertInstruction {
+ SafeIntCastInstruction() { safeCast(getUnary().getResultType(), getResultType()) }
+}
+
+/**
+ * A `ConvertInstruction` which does not invalidate bounds determined by
+ * range analysis.
+ */
+class SafeCastInstruction extends ConvertInstruction {
+ SafeCastInstruction() {
+ this instanceof PtrToPtrCastInstruction or
+ this instanceof SafeIntCastInstruction
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SignAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SignAnalysis.qll
index ca641f826ef..37e2ac46386 100644
--- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SignAnalysis.qll
+++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SignAnalysis.qll
@@ -278,8 +278,6 @@ private predicate unknownSign(Instruction i) {
// non-positive, non-zero}, which would mean that the representation of the sign of an unknown
// value would be the empty set.
(
- i instanceof UnmodeledDefinitionInstruction
- or
i instanceof UninitializedInstruction
or
i instanceof InitializeParameterInstruction
diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll
index 22e5f5ac83e..867d96b804c 100644
--- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll
+++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll
@@ -118,36 +118,95 @@ private string getValue(Expr e) {
)
}
+/**
+ * A bitwise `&` expression in which both operands are unsigned, or are effectively
+ * unsigned due to being a non-negative constant.
+ */
+private class UnsignedBitwiseAndExpr extends BitwiseAndExpr {
+ UnsignedBitwiseAndExpr() {
+ (
+ getLeftOperand().getFullyConverted().getType().getUnderlyingType().(IntegralType).isUnsigned() or
+ getLeftOperand().getFullyConverted().getValue().toInt() >= 0
+ ) and
+ (
+ getRightOperand()
+ .getFullyConverted()
+ .getType()
+ .getUnderlyingType()
+ .(IntegralType)
+ .isUnsigned() or
+ getRightOperand().getFullyConverted().getValue().toInt() >= 0
+ )
+ }
+}
+
+/**
+ * Gets the floor of `v`, with additional logic to work around issues with
+ * large numbers.
+ */
+bindingset[v]
+float safeFloor(float v) {
+ // return the floor of v
+ v.abs() < 2.pow(31) and
+ result = v.floor()
+ or
+ // `floor()` doesn't work correctly on large numbers (since it returns an integer),
+ // so fall back to unrounded numbers at this scale.
+ not v.abs() < 2.pow(31) and
+ result = v
+}
+
/** Set of expressions which we know how to analyze. */
private predicate analyzableExpr(Expr e) {
// The type of the expression must be arithmetic. We reuse the logic in
// `exprMinVal` to check this.
exists(exprMinVal(e)) and
(
- exists(getValue(e).toFloat()) or
- e instanceof UnaryPlusExpr or
- e instanceof UnaryMinusExpr or
- e instanceof MinExpr or
- e instanceof MaxExpr or
- e instanceof ConditionalExpr or
- e instanceof AddExpr or
- e instanceof SubExpr or
- e instanceof AssignExpr or
- e instanceof AssignAddExpr or
- e instanceof AssignSubExpr or
- e instanceof CrementOperation or
- e instanceof RemExpr or
- e instanceof CommaExpr or
- e instanceof StmtExpr or
+ exists(getValue(e).toFloat())
+ or
+ e instanceof UnaryPlusExpr
+ or
+ e instanceof UnaryMinusExpr
+ or
+ e instanceof MinExpr
+ or
+ e instanceof MaxExpr
+ or
+ e instanceof ConditionalExpr
+ or
+ e instanceof AddExpr
+ or
+ e instanceof SubExpr
+ or
+ e instanceof AssignExpr
+ or
+ e instanceof AssignAddExpr
+ or
+ e instanceof AssignSubExpr
+ or
+ e instanceof CrementOperation
+ or
+ e instanceof RemExpr
+ or
+ e instanceof CommaExpr
+ or
+ e instanceof StmtExpr
+ or
// A conversion is analyzable, provided that its child has an arithmetic
// type. (Sometimes the child is a reference type, and so does not get
// any bounds.) Rather than checking whether the type of the child is
// arithmetic, we reuse the logic that is already encoded in
// `exprMinVal`.
- exists(exprMinVal(e.(Conversion).getExpr())) or
+ exists(exprMinVal(e.(Conversion).getExpr()))
+ or
// Also allow variable accesses, provided that they have SSA
// information.
exists(RangeSsaDefinition def, StackVariable v | e = def.getAUse(v))
+ or
+ e instanceof UnsignedBitwiseAndExpr
+ or
+ // `>>` by a constant
+ exists(e.(RShiftExpr).getRightOperand().getValue())
)
}
@@ -245,6 +304,19 @@ private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, StackVaria
or
exists(Conversion convExpr | e = convExpr | exprDependsOnDef(convExpr.getExpr(), srcDef, srcVar))
or
+ // unsigned `&`
+ exists(UnsignedBitwiseAndExpr andExpr |
+ andExpr = e and
+ exprDependsOnDef(andExpr.getAnOperand(), srcDef, srcVar)
+ )
+ or
+ // `>>` by a constant
+ exists(RShiftExpr rs |
+ rs = e and
+ exists(rs.getRightOperand().getValue()) and
+ exprDependsOnDef(rs.getLeftOperand(), srcDef, srcVar)
+ )
+ or
e = srcDef.getAUse(srcVar)
}
@@ -641,6 +713,20 @@ private float getLowerBoundsImpl(Expr expr) {
exists(RangeSsaDefinition def, StackVariable v | expr = def.getAUse(v) |
result = getDefLowerBounds(def, v)
)
+ or
+ // unsigned `&` (tighter bounds may exist)
+ exists(UnsignedBitwiseAndExpr andExpr |
+ andExpr = expr and
+ result = 0.0
+ )
+ or
+ // `>>` by a constant
+ exists(RShiftExpr rsExpr, float left, int right |
+ rsExpr = expr and
+ left = getFullyConvertedLowerBounds(rsExpr.getLeftOperand()) and
+ right = rsExpr.getRightOperand().getValue().toInt() and
+ result = safeFloor(left / 2.pow(right))
+ )
}
/** Only to be called by `getTruncatedUpperBounds`. */
@@ -794,6 +880,22 @@ private float getUpperBoundsImpl(Expr expr) {
exists(RangeSsaDefinition def, StackVariable v | expr = def.getAUse(v) |
result = getDefUpperBounds(def, v)
)
+ or
+ // unsigned `&` (tighter bounds may exist)
+ exists(UnsignedBitwiseAndExpr andExpr, float left, float right |
+ andExpr = expr and
+ left = getFullyConvertedUpperBounds(andExpr.getLeftOperand()) and
+ right = getFullyConvertedUpperBounds(andExpr.getRightOperand()) and
+ result = left.minimum(right)
+ )
+ or
+ // `>>` by a constant
+ exists(RShiftExpr rsExpr, float left, int right |
+ rsExpr = expr and
+ left = getFullyConvertedUpperBounds(rsExpr.getLeftOperand()) and
+ right = rsExpr.getRightOperand().getValue().toInt() and
+ result = safeFloor(left / 2.pow(right))
+ )
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/security/CommandExecution.qll b/cpp/ql/src/semmle/code/cpp/security/CommandExecution.qll
index 48fb60442c1..5a24184e1a2 100644
--- a/cpp/ql/src/semmle/code/cpp/security/CommandExecution.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/CommandExecution.qll
@@ -2,21 +2,44 @@
import cpp
import semmle.code.cpp.security.FunctionWithWrappers
+import semmle.code.cpp.models.interfaces.SideEffect
/**
* A function for running a command using a command interpreter.
*/
-class SystemFunction extends FunctionWithWrappers {
+class SystemFunction extends FunctionWithWrappers, ArrayFunction, AliasFunction, SideEffectFunction {
SystemFunction() {
- hasGlobalOrStdName("system") or
- hasGlobalName("popen") or
+ hasGlobalOrStdName("system") or // system(command)
+ hasGlobalName("popen") or // popen(command, mode)
// Windows variants
- hasGlobalName("_popen") or
- hasGlobalName("_wpopen") or
- hasGlobalName("_wsystem")
+ hasGlobalName("_popen") or // _popen(command, mode)
+ hasGlobalName("_wpopen") or // _wpopen(command, mode)
+ hasGlobalName("_wsystem") // _wsystem(command)
}
override predicate interestingArg(int arg) { arg = 0 }
+
+ override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 or bufParam = 1 }
+
+ override predicate hasArrayInput(int bufParam) { bufParam = 0 or bufParam = 1 }
+
+ override predicate parameterNeverEscapes(int index) { index = 0 or index = 1 }
+
+ override predicate parameterEscapesOnlyViaReturn(int index) { none() }
+
+ override predicate parameterIsAlwaysReturned(int index) { none() }
+
+ override predicate hasOnlySpecificReadSideEffects() { any() }
+
+ override predicate hasOnlySpecificWriteSideEffects() {
+ hasGlobalOrStdName("system") or
+ hasGlobalName("_wsystem")
+ }
+
+ override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
+ (i = 0 or i = 1) and
+ buffer = true
+ }
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/security/FlowSources.qll b/cpp/ql/src/semmle/code/cpp/security/FlowSources.qll
new file mode 100644
index 00000000000..eff40572c02
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/security/FlowSources.qll
@@ -0,0 +1,44 @@
+/**
+ * Provides classes representing various flow sources for taint tracking.
+ */
+
+import cpp
+import semmle.code.cpp.ir.dataflow.DataFlow
+private import semmle.code.cpp.ir.IR
+import semmle.code.cpp.models.interfaces.FlowSource
+
+/** A data flow source of remote user input. */
+abstract class RemoteFlowSource extends DataFlow::Node {
+ /** Gets a string that describes the type of this remote flow source. */
+ abstract string getSourceType();
+}
+
+private class TaintedReturnSource extends RemoteFlowSource {
+ string sourceType;
+
+ TaintedReturnSource() {
+ exists(RemoteFlowFunction func, CallInstruction instr, FunctionOutput output |
+ asInstruction() = instr and
+ instr.getStaticCallTarget() = func and
+ func.hasRemoteFlowSource(output, sourceType) and
+ output.isReturnValue()
+ )
+ }
+
+ override string getSourceType() { result = sourceType }
+}
+
+private class TaintedParameterSource extends RemoteFlowSource {
+ string sourceType;
+
+ TaintedParameterSource() {
+ exists(RemoteFlowFunction func, WriteSideEffectInstruction instr, FunctionOutput output |
+ asInstruction() = instr and
+ instr.getPrimaryInstruction().(CallInstruction).getStaticCallTarget() = func and
+ func.hasRemoteFlowSource(output, sourceType) and
+ output.isParameterDeref(instr.getIndex())
+ )
+ }
+
+ override string getSourceType() { result = sourceType }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll b/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll
index 8047bc384b2..23dda0ddb1e 100644
--- a/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll
@@ -19,7 +19,7 @@ private predicate wrapperFunctionStep(
) {
not target.isVirtual() and
not source.isVirtual() and
- source.isDefined() and
+ source.hasDefinition() and
exists(Call call, Expr arg, Parameter sourceParam |
// there is a 'call' to 'target' with argument 'arg' at index 'targetParamIndex'
target = resolveCall(call) and
diff --git a/cpp/ql/src/semmle/code/cpp/security/TaintTracking.qll b/cpp/ql/src/semmle/code/cpp/security/TaintTracking.qll
index e20dfd83efd..65836d285ad 100644
--- a/cpp/ql/src/semmle/code/cpp/security/TaintTracking.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/TaintTracking.qll
@@ -1,5 +1,7 @@
/*
* Support for tracking tainted data through the program.
+ *
+ * Prefer to use `semmle.code.cpp.dataflow.TaintTracking` when designing new queries.
*/
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking
diff --git a/cpp/ql/src/semmle/code/cpp/security/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/security/TaintTrackingImpl.qll
index a24820b277f..06cf4c456ce 100644
--- a/cpp/ql/src/semmle/code/cpp/security/TaintTrackingImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/TaintTrackingImpl.qll
@@ -1,4 +1,8 @@
/**
+ * DEPRECATED: we now use `semmle.code.cpp.ir.dataflow.DefaultTaintTracking`,
+ * which is based on the IR but designed to behave similarly to this old
+ * libarary.
+ *
* Provides the implementation of `semmle.code.cpp.security.TaintTracking`. Do
* not import this file directly.
*/
diff --git a/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll b/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll
index 1640bee0f35..989d36a0a9d 100644
--- a/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll
+++ b/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll
@@ -1455,7 +1455,7 @@ class SwitchStmt extends ConditionalStmt, @stmt_switch {
/**
* Gets the body statement of this 'switch' statement.
*
- * In almost all cases the result will be a `BlockStmt`, but there are
+ * In almost all cases the result will be a `Block`, but there are
* other syntactically valid constructions.
*
* For example, for
diff --git a/cpp/ql/src/semmlecode.cpp.dbscheme b/cpp/ql/src/semmlecode.cpp.dbscheme
index d6ca4ebb768..282c13bfdbc 100644
--- a/cpp/ql/src/semmlecode.cpp.dbscheme
+++ b/cpp/ql/src/semmlecode.cpp.dbscheme
@@ -616,6 +616,7 @@ enumconstants(
| 48 = _Float64x
| 49 = _Float128
| 50 = _Float128x
+ | 51 = char8_t
;
*/
builtintypes(
@@ -1142,6 +1143,13 @@ conversionkinds(
int kind: int ref
);
+@conversion = @cast
+ | @array_to_pointer
+ | @parexpr
+ | @reference_to
+ | @ref_indirect
+ ;
+
/*
case @funbindexpr.kind of
0 = @normal_call // a normal call
@@ -1640,6 +1648,67 @@ case @expr.kind of
| 326 = @spaceshipexpr
;
+@var_args_expr = @vastartexpr
+ | @vaendexpr
+ | @vaargexpr
+ | @vacopyexpr
+ ;
+
+@builtin_op = @var_args_expr
+ | @noopexpr
+ | @offsetofexpr
+ | @intaddrexpr
+ | @hasassignexpr
+ | @hascopyexpr
+ | @hasnothrowassign
+ | @hasnothrowconstr
+ | @hasnothrowcopy
+ | @hastrivialassign
+ | @hastrivialconstr
+ | @hastrivialcopy
+ | @hastrivialdestructor
+ | @hasuserdestr
+ | @hasvirtualdestr
+ | @isabstractexpr
+ | @isbaseofexpr
+ | @isclassexpr
+ | @isconvtoexpr
+ | @isemptyexpr
+ | @isenumexpr
+ | @ispodexpr
+ | @ispolyexpr
+ | @isunionexpr
+ | @typescompexpr
+ | @builtinshufflevector
+ | @builtinconvertvector
+ | @builtinaddressof
+ | @istriviallyconstructibleexpr
+ | @isdestructibleexpr
+ | @isnothrowdestructibleexpr
+ | @istriviallydestructibleexpr
+ | @istriviallyassignableexpr
+ | @isnothrowassignableexpr
+ | @isstandardlayoutexpr
+ | @istriviallycopyableexpr
+ | @isliteraltypeexpr
+ | @hastrivialmoveconstructorexpr
+ | @hastrivialmoveassignexpr
+ | @hasnothrowmoveassignexpr
+ | @isconstructibleexpr
+ | @isnothrowconstructibleexpr
+ | @hasfinalizerexpr
+ | @isdelegateexpr
+ | @isinterfaceclassexpr
+ | @isrefarrayexpr
+ | @isrefclassexpr
+ | @issealedexpr
+ | @issimplevalueclassexpr
+ | @isvalueclassexpr
+ | @isfinalexpr
+ | @builtinchooseexpr
+ | @builtincomplex
+ ;
+
new_allocated_type(
unique int expr: @new_expr ref,
int type_id: @type ref
@@ -1738,6 +1807,8 @@ lambda_capture(
@addressable = @function | @variable ;
@accessible = @addressable | @enumconstant ;
+@access = @varaccess | @routineexpr ;
+
fold(
int expr: @foldexpr ref,
string operator: string ref,
diff --git a/cpp/ql/src/semmlecode.cpp.dbscheme.stats b/cpp/ql/src/semmlecode.cpp.dbscheme.stats
index 193ba78890d..49f84494fd7 100644
--- a/cpp/ql/src/semmlecode.cpp.dbscheme.stats
+++ b/cpp/ql/src/semmlecode.cpp.dbscheme.stats
@@ -1,7 +1,7 @@
@compilation
-9552
+9550
@externalDataElement
@@ -25,7 +25,7 @@
@location_default
-8813337
+8811984
@location_stmt
@@ -37,43 +37,43 @@
@diagnostic
-68696
+68684
@file
-60022
+60011
@folder
-10945
+10943
@macroinvocation
-35579091
+35573465
@function
-3468206
+3467587
@fun_decl
-3540511
+3539879
@var_decl
-5359926
+5359112
@type_decl
-1332120
+1331883
@namespace_decl
-136867
+136842
@using
-291435
+291383
@static_assert
@@ -81,11 +81,11 @@
@parameter
-4627839
+4627013
@membervariable
-305681
+305626
@globalvariable
@@ -101,23 +101,23 @@
@builtintype
-504
+515
@derivedtype
-4417712
+4416924
@decltype
-47004
+46995
@usertype
-4193822
+4193074
@mangledname
-483674
+483588
@type_mention
@@ -125,19 +125,19 @@
@routinetype
-430474
+430397
@ptrtomember
-12633
+12631
@specifier
-493
+504
@gnuattribute
-413159
+413730
@stdattribute
@@ -145,7 +145,7 @@
@declspec
-57808
+57828
@msattribute
@@ -153,7 +153,7 @@
@alignas
-252
+1743
@attribute_arg_empty
@@ -165,7 +165,7 @@
@attribute_arg_constant
-145712
+146276
@attribute_arg_type
@@ -173,19 +173,19 @@
@derivation
-390258
+390188
@frienddecl
-240340
+240297
@comment
-1580291
+1580009
@namespace
-7687
+7686
@specialnamequalifyingelement
@@ -205,7 +205,7 @@
@errorexpr
-48715
+48706
@address_of
@@ -213,7 +213,7 @@
@reference_to
-1057881
+1058087
@indirect
@@ -221,7 +221,7 @@
@ref_indirect
-1253827
+1253933
@array_to_pointer
@@ -229,7 +229,7 @@
@vacuous_destructor_call
-5121
+5120
@assume
@@ -277,11 +277,11 @@
@preincrexpr
-62522
+62511
@predecrexpr
-24796
+24791
@conditionalexpr
@@ -341,7 +341,7 @@
@pdiffexpr
-25081
+25076
@lshiftexpr
@@ -377,7 +377,7 @@
@ltexpr
-51742
+51732
@geexpr
@@ -409,7 +409,7 @@
@assignmulexpr
-6909
+6907
@assigndivexpr
@@ -457,7 +457,7 @@
@commaexpr
-10725
+10734
@subscriptexpr
@@ -469,7 +469,7 @@
@callexpr
-226829
+226788
@vastartexpr
@@ -477,7 +477,7 @@
@vaargexpr
-987
+986
@vaendexpr
@@ -497,19 +497,19 @@
@new_expr
-32133
+32127
@delete_expr
-5966
+5964
@throw_expr
-22317
+22313
@condition_decl
-7007
+7028
@braced_init_list
@@ -517,7 +517,7 @@
@type_id
-4419
+4418
@runtime_sizeof
@@ -537,7 +537,7 @@
@routineexpr
-2265747
+2265935
@type_operand
@@ -661,15 +661,15 @@
@ctordirectinit
-90192
+90175
@ctorvirtualinit
-6229
+6228
@ctorfieldinit
-196044
+196009
@ctordelegatinginit
@@ -677,7 +677,7 @@
@dtordirectdestruct
-29139
+29133
@dtorvirtualdestruct
@@ -685,11 +685,11 @@
@dtorfielddestruct
-29906
+29901
@static_cast
-214754
+214716
@reinterpret_cast
@@ -701,7 +701,7 @@
@dynamic_cast
-987
+986
@c_style_cast
@@ -713,7 +713,7 @@
@param_ref
-85893
+85877
@noopexpr
@@ -817,7 +817,7 @@
@noexceptexpr
-17525
+17522
@builtinshufflevector
@@ -829,7 +829,7 @@
@builtinaddressof
-3970
+3969
@vec_fill
@@ -861,7 +861,7 @@
@stmt_while
-30992
+30997
@stmt_goto
@@ -873,11 +873,11 @@
@stmt_return
-1130186
+1130006
@stmt_block
-1325091
+1324997
@stmt_end_test_while
@@ -901,7 +901,7 @@
@stmt_try_block
-17963
+17960
@stmt_microsoft_try
@@ -909,7 +909,7 @@
@stmt_decl
-613073
+613063
@stmt_set_vla_size
@@ -949,39 +949,39 @@
@ppd_if
-156092
+156064
@ppd_ifdef
-61085
+61074
@ppd_ifndef
-83326
+83311
@ppd_elif
-20628
+20625
@ppd_else
-57664
+57653
@ppd_endif
-300505
+300451
@ppd_plain_include
-290755
+290703
@ppd_define
-318063
+318006
@ppd_undef
-19246
+19243
@ppd_line
@@ -1038,11 +1038,11 @@
compilations
-9552
+9550
id
-9552
+9550
cwd
@@ -1060,7 +1060,7 @@
1
2
-9552
+9550
@@ -1382,11 +1382,11 @@
compilation_compiling_files
-9552
+9550
id
-9552
+9550
num
@@ -1394,7 +1394,7 @@
file
-4847
+4846
@@ -1408,7 +1408,7 @@
1
2
-9552
+9550
@@ -1424,7 +1424,7 @@
1
2
-9552
+9550
@@ -1477,7 +1477,7 @@
2
3
-4551
+4550
3
@@ -1498,7 +1498,7 @@
1
2
-4847
+4846
@@ -1508,11 +1508,11 @@
compilation_time
-38121
+38114
id
-9530
+9528
num
@@ -1524,7 +1524,7 @@
seconds
-13522
+12094
@@ -1538,7 +1538,7 @@
1
2
-9530
+9528
@@ -1554,7 +1554,7 @@
4
5
-9530
+9528
@@ -1568,14 +1568,19 @@
12
+2
+3
+10
+
+
3
4
-3158
+2675
4
5
-6371
+6842
@@ -1621,8 +1626,8 @@
12
-1233
-1234
+1103
+1104
10
@@ -1669,8 +1674,8 @@
12
-8
-9
+7
+8
10
@@ -1679,13 +1684,13 @@
10
-646
-647
+579
+580
10
-735
-736
+670
+671
10
@@ -1702,22 +1707,22 @@
1
2
-9409
+7949
2
3
-2972
+2401
3
-9
-1019
+4
+932
-9
-615
-120
+4
+627
+811
@@ -1733,7 +1738,7 @@
1
2
-13522
+12094
@@ -1749,12 +1754,12 @@
1
2
-11679
+10285
2
3
-1831
+1798
3
@@ -1769,15 +1774,15 @@
diagnostic_for
-847086
+846935
diagnostic
-68696
+68684
compilation
-9223
+9221
file_number
@@ -1785,7 +1790,7 @@
file_number_diagnostic_number
-6503
+6502
@@ -1799,12 +1804,12 @@
1
2
-9256
+9254
2
3
-56742
+56732
254
@@ -1825,7 +1830,7 @@
1
2
-68696
+68684
@@ -1841,7 +1846,7 @@
1
2
-68696
+68684
@@ -1862,7 +1867,7 @@
7
8
-5812
+5811
8
@@ -1898,7 +1903,7 @@
1
2
-9223
+9221
@@ -1919,7 +1924,7 @@
7
8
-5812
+5811
8
@@ -2013,7 +2018,7 @@
5
6
-954
+953
7
@@ -2064,7 +2069,7 @@
10
11
-954
+953
14
@@ -2099,7 +2104,7 @@
254
255
-2621
+2620
309
@@ -2120,7 +2125,7 @@
1
2
-6503
+6502
@@ -2130,19 +2135,19 @@
compilation_finished
-9552
+9550
id
-9552
+9550
cpu_seconds
-8587
+8157
elapsed_seconds
-219
+186
@@ -2156,7 +2161,7 @@
1
2
-9552
+9550
@@ -2172,7 +2177,7 @@
1
2
-9552
+9550
@@ -2188,17 +2193,17 @@
1
2
-7797
+7160
2
3
-668
+756
3
-7
-120
+6
+241
@@ -2214,12 +2219,12 @@
1
2
-8389
+7675
2
3
-197
+482
@@ -2235,71 +2240,66 @@
1
2
-54
+32
2
3
+10
+
+
+3
+4
32
-5
-6
+7
+8
10
-6
-7
+8
+9
10
-9
-10
+21
+22
10
-11
-12
+26
+27
10
-16
-17
+31
+32
10
-37
-38
+104
+105
10
-52
-53
+137
+138
10
-78
-79
+144
+145
10
-91
-92
+173
+174
10
-156
-157
-10
-
-
-166
-167
-10
-
-
-233
-234
+206
+207
10
@@ -2316,66 +2316,66 @@
1
2
-54
+32
2
3
+10
+
+
+3
+4
32
-5
-6
+7
+8
10
-6
-7
+8
+9
10
-9
-10
+21
+22
10
-11
-12
+25
+26
10
-16
-17
+29
+30
10
-36
-37
+84
+85
10
-51
-52
+122
+123
10
-74
-75
-21
-
-
-147
-148
+132
+133
10
-157
-158
+150
+151
10
-204
-205
+196
+197
10
@@ -5279,11 +5279,11 @@
header_to_external_package
-8532
+8530
fileid
-8532
+8530
package
@@ -5301,7 +5301,7 @@
1
2
-8532
+8530
@@ -6570,31 +6570,31 @@
locations_default
-8813337
+8811984
id
-8813337
+8811984
container
-70967
+70954
startLine
-150630
+150604
startColumn
-5461
+5460
endLine
-150455
+150428
endColumn
-10626
+10625
@@ -6608,7 +6608,7 @@
1
2
-8813337
+8811984
@@ -6624,7 +6624,7 @@
1
2
-8813337
+8811984
@@ -6640,7 +6640,7 @@
1
2
-8813337
+8811984
@@ -6656,7 +6656,7 @@
1
2
-8813337
+8811984
@@ -6672,7 +6672,7 @@
1
2
-8813337
+8811984
@@ -6688,62 +6688,62 @@
1
2
-11526
+11524
2
19
-6097
+6096
19
25
-5483
+5482
25
31
-5516
+5515
31
41
-5823
+5822
41
54
-5527
+5526
54
72
-5604
+5603
72
99
-5417
+5416
99
137
-5384
+5383
137
220
-5329
+5328
220
430
-5329
+5328
430
20913
-3926
+3925
@@ -6759,57 +6759,57 @@
1
2
-11526
+11524
2
15
-6009
+6008
15
20
-6141
+6140
20
25
-5549
+5548
25
32
-6097
+6096
32
41
-5680
+5679
41
53
-5724
+5723
53
71
-5593
+5592
71
99
-5450
+5449
99
158
-5329
+5328
158
351
-5362
+5361
351
@@ -6830,57 +6830,57 @@
1
2
-11526
+11524
2
4
-6009
+6008
4
8
-6547
+6546
8
11
-5395
+5394
11
14
-5779
+5778
14
18
-6251
+6250
18
23
-5757
+5756
23
29
-5834
+5833
29
37
-5691
+5690
37
50
-5669
+5668
50
78
-5384
+5383
78
@@ -6901,62 +6901,62 @@
1
2
-11526
+11524
2
15
-5987
+5986
15
20
-6163
+6162
20
25
-5494
+5493
25
32
-6108
+6107
32
41
-5669
+5668
41
53
-5724
+5723
53
70
-5384
+5383
70
96
-5340
+5339
96
153
-5395
+5394
153
333
-5351
+5350
333
9356
-2818
+2817
@@ -6972,57 +6972,57 @@
1
2
-11526
+11524
2
14
-5735
+5734
14
19
-6119
+6118
19
23
-5713
+5712
23
28
-6415
+6414
28
33
-5516
+5515
33
40
-5955
+5953
40
47
-5351
+5350
47
57
-5604
+5603
57
69
-5637
+5635
69
91
-5329
+5328
91
@@ -7043,52 +7043,52 @@
1
2
-30849
+30844
2
3
-18029
+18026
3
4
-17952
+17949
4
5
-9716
+9714
5
7
-13708
+13706
7
9
-13412
+13410
9
13
-13204
+13201
13
32
-11570
+11568
32
127
-11306
+11304
127
6472
-10879
+10877
@@ -7104,42 +7104,42 @@
1
2
-55657
+55647
2
3
-33438
+33432
3
4
-9903
+9901
4
5
-8433
+8432
5
8
-12908
+12905
8
27
-11493
+11491
27
123
-11339
+11337
123
6472
-7457
+7456
@@ -7155,52 +7155,52 @@
1
2
-31957
+31951
2
3
-17865
+17861
3
4
-19795
+19791
4
5
-9585
+9583
5
7
-13917
+13914
7
9
-13829
+13826
9
13
-12710
+12708
13
27
-11482
+11480
27
62
-11350
+11348
62
153
-8137
+8136
@@ -7216,22 +7216,22 @@
1
2
-112970
+112950
2
3
-17415
+17412
3
7
-12535
+12532
7
184
-7709
+7708
@@ -7247,52 +7247,52 @@
1
2
-31804
+31798
2
3
-17777
+17774
3
4
-18643
+18640
4
5
-9870
+9868
5
7
-13730
+13728
7
9
-13774
+13772
9
13
-12732
+12730
13
29
-11657
+11655
29
74
-11350
+11348
74
258
-9288
+9287
@@ -7613,52 +7613,52 @@
1
2
-30564
+30559
2
3
-18062
+18059
3
4
-17810
+17807
4
5
-9870
+9868
5
7
-13774
+13772
7
9
-13456
+13454
9
13
-13050
+13048
13
31
-11460
+11458
31
124
-11295
+11293
124
6472
-11109
+11107
@@ -7674,42 +7674,42 @@
1
2
-55372
+55362
2
3
-33339
+33333
3
4
-9870
+9868
4
5
-8543
+8541
5
8
-13105
+13103
8
27
-11328
+11326
27
121
-11295
+11293
121
6472
-7600
+7598
@@ -7725,22 +7725,22 @@
1
2
-112180
+112160
2
3
-17250
+17247
3
7
-12074
+12072
7
46
-8949
+8947
@@ -7756,57 +7756,57 @@
1
2
-31650
+31644
2
3
-17941
+17938
3
4
-19740
+19736
4
5
-9607
+9605
5
6
-7863
+7861
6
7
-6130
+6129
7
9
-13840
+13837
9
13
-12710
+12708
13
27
-11493
+11491
27
62
-11350
+11348
62
153
-8126
+8125
@@ -7822,52 +7822,52 @@
1
2
-31551
+31546
2
3
-17744
+17741
3
4
-18632
+18629
4
5
-9936
+9934
5
7
-13818
+13815
7
9
-13774
+13772
9
13
-12787
+12785
13
29
-11515
+11513
29
74
-11361
+11359
74
258
-9332
+9331
@@ -7883,7 +7883,7 @@
1
2
-4211
+4210
2
@@ -7898,7 +7898,7 @@
4
5
-647
+646
5
@@ -7921,7 +7921,7 @@
800
-7760
+7763
25755
526
@@ -7939,17 +7939,17 @@
1
2
-4836
+4835
2
3
-1283
+1282
3
4
-954
+953
4
@@ -7990,7 +7990,7 @@
1
2
-4244
+4243
2
@@ -8005,7 +8005,7 @@
4
5
-636
+635
5
@@ -8046,7 +8046,7 @@
1
2
-4869
+4868
2
@@ -8056,7 +8056,7 @@
3
4
-943
+942
4
@@ -8097,7 +8097,7 @@
1
2
-4244
+4243
2
@@ -8112,7 +8112,7 @@
4
5
-636
+635
5
@@ -11616,23 +11616,23 @@
numlines
-499247
+499158
element_id
-492283
+492196
num_lines
-9343
+9342
num_code
-7293
+7291
num_comment
-3937
+3936
@@ -11646,12 +11646,12 @@
1
2
-485396
+485310
2
7
-6887
+6886
@@ -11667,12 +11667,12 @@
1
2
-485451
+485364
2
7
-6832
+6831
@@ -11688,7 +11688,7 @@
1
2
-492207
+492119
2
@@ -11709,7 +11709,7 @@
1
2
-4255
+4254
2
@@ -11765,7 +11765,7 @@
2
3
-1261
+1260
3
@@ -11811,12 +11811,12 @@
1
2
-4310
+4309
2
3
-1261
+1260
3
@@ -11857,7 +11857,7 @@
1
2
-3180
+3179
2
@@ -11892,7 +11892,7 @@
101
7978
-329
+328
@@ -11908,7 +11908,7 @@
1
2
-3202
+3201
2
@@ -11918,7 +11918,7 @@
3
4
-636
+635
4
@@ -11928,7 +11928,7 @@
6
10
-636
+635
10
@@ -11959,7 +11959,7 @@
1
2
-3191
+3190
2
@@ -11969,7 +11969,7 @@
3
4
-636
+635
4
@@ -11994,7 +11994,7 @@
27
34
-318
+317
@@ -12010,7 +12010,7 @@
1
2
-1886
+1885
2
@@ -12025,12 +12025,12 @@
4
7
-329
+328
7
13
-329
+328
14
@@ -12061,7 +12061,7 @@
1
2
-1897
+1896
2
@@ -12081,7 +12081,7 @@
7
13
-329
+328
13
@@ -12112,7 +12112,7 @@
1
2
-1897
+1896
2
@@ -12127,7 +12127,7 @@
4
7
-329
+328
7
@@ -12157,11 +12157,11 @@
diagnostics
-68696
+68684
id
-68696
+68684
severity
@@ -12177,7 +12177,7 @@
full_error_message
-59484
+59474
location
@@ -12195,7 +12195,7 @@
1
2
-68696
+68684
@@ -12211,7 +12211,7 @@
1
2
-68696
+68684
@@ -12227,7 +12227,7 @@
1
2
-68696
+68684
@@ -12243,7 +12243,7 @@
1
2
-68696
+68684
@@ -12259,7 +12259,7 @@
1
2
-68696
+68684
@@ -12675,7 +12675,7 @@
1
2
-59473
+59463
841
@@ -12696,7 +12696,7 @@
1
2
-59484
+59474
@@ -12712,7 +12712,7 @@
1
2
-59484
+59474
@@ -12728,7 +12728,7 @@
1
2
-59484
+59474
@@ -12744,7 +12744,7 @@
1
2
-59484
+59474
@@ -12854,19 +12854,19 @@
files
-60022
+60011
id
-60022
+60011
name
-60022
+60011
simple
-41060
+41052
ext
@@ -12888,7 +12888,7 @@
1
2
-60022
+60011
@@ -12904,7 +12904,7 @@
1
2
-60022
+60011
@@ -12920,7 +12920,7 @@
1
2
-60022
+60011
@@ -12936,7 +12936,7 @@
1
2
-60022
+60011
@@ -12952,7 +12952,7 @@
1
2
-60022
+60011
@@ -12968,7 +12968,7 @@
1
2
-60022
+60011
@@ -12984,7 +12984,7 @@
1
2
-60022
+60011
@@ -13000,7 +13000,7 @@
1
2
-60022
+60011
@@ -13016,17 +13016,17 @@
1
2
-31179
+31173
2
3
-6196
+6195
3
7
-3235
+3234
7
@@ -13047,17 +13047,17 @@
1
2
-31179
+31173
2
3
-6196
+6195
3
7
-3235
+3234
7
@@ -13078,12 +13078,12 @@
1
2
-36706
+36699
2
3
-3783
+3782
3
@@ -13104,7 +13104,7 @@
1
2
-41060
+41052
@@ -13362,19 +13362,19 @@
folders
-10945
+10943
id
-10945
+10943
name
-10945
+10943
simple
-3136
+3135
@@ -13388,7 +13388,7 @@
1
2
-10945
+10943
@@ -13404,7 +13404,7 @@
1
2
-10945
+10943
@@ -13420,7 +13420,7 @@
1
2
-10945
+10943
@@ -13436,7 +13436,7 @@
1
2
-10945
+10943
@@ -13518,15 +13518,15 @@
containerparent
-70945
+70932
parent
-10945
+10943
child
-70945
+70932
@@ -13591,7 +13591,7 @@
1
2
-70945
+70932
@@ -13601,11 +13601,11 @@
fileannotations
-5150534
+5149615
id
-4825
+4824
kind
@@ -13613,11 +13613,11 @@
name
-54626
+54616
value
-44646
+44638
@@ -13852,42 +13852,42 @@
1
2
-8927
+8925
2
3
-6031
+6030
3
6
-4518
+4517
6
8
-4452
+4451
8
14
-4408
+4407
14
18
-3915
+3914
18
21
-4244
+4243
21
34
-4397
+4396
34
@@ -13897,12 +13897,12 @@
129
236
-4156
+4155
236
395
-4112
+4111
395
@@ -13923,7 +13923,7 @@
1
2
-54626
+54616
@@ -13939,12 +13939,12 @@
1
2
-9979
+9978
2
3
-8049
+8048
3
@@ -13954,42 +13954,42 @@
4
6
-3959
+3958
6
10
-4880
+4879
10
14
-3487
+3486
14
18
-4496
+4495
18
23
-4200
+4199
23
44
-4397
+4396
44
97
-4156
+4155
97
405
-4101
+4100
421
@@ -14010,7 +14010,7 @@
1
2
-6898
+6896
2
@@ -14020,7 +14020,7 @@
5
8
-3235
+3234
8
@@ -14040,17 +14040,17 @@
25
40
-3235
+3234
40
195
-3520
+3519
195
207
-3509
+3508
207
@@ -14065,12 +14065,12 @@
328
407
-3838
+3837
407
441
-1283
+1282
@@ -14086,7 +14086,7 @@
1
2
-44635
+44627
2
@@ -14107,12 +14107,12 @@
1
2
-6920
+6918
2
5
-2511
+2510
5
@@ -14122,7 +14122,7 @@
8
16
-3498
+3497
16
@@ -14137,17 +14137,17 @@
21
31
-3882
+3881
31
41
-3531
+3530
41
54
-3531
+3530
54
@@ -14458,19 +14458,19 @@
macroinvocations
-35579091
+35573465
id
-35579091
+35573465
macro_id
-81232
+81217
location
-761215
+761079
kind
@@ -14488,7 +14488,7 @@
1
2
-35579091
+35573465
@@ -14504,7 +14504,7 @@
1
2
-35579091
+35573465
@@ -14520,7 +14520,7 @@
1
2
-35579091
+35573465
@@ -14536,52 +14536,52 @@
1
2
-17020
+17017
2
3
-16560
+16557
3
4
-3575
+3574
4
6
-7194
+7193
6
11
-6898
+6896
11
19
-6174
+6173
19
40
-6174
+6173
40
105
-6196
+6195
105
487
-6108
+6107
488
196960
-5329
+5328
@@ -14597,32 +14597,32 @@
1
2
-43220
+43212
2
3
-10747
+10745
3
4
-5340
+5339
4
6
-6974
+6973
6
13
-6722
+6721
13
66
-6108
+6107
66
@@ -14643,12 +14643,12 @@
1
2
-75090
+75077
2
3
-6141
+6140
@@ -14664,42 +14664,42 @@
1
2
-284263
+284332
2
3
-177631
+177479
3
4
-43451
+43443
4
5
-58596
+58585
5
8
-63575
+63563
8
17
-61162
+61151
17
80
-57159
+57149
80
258137
-15375
+15372
@@ -14715,12 +14715,12 @@
1
2
-712510
+712383
2
354
-48704
+48695
@@ -14736,7 +14736,7 @@
1
2
-761215
+761079
@@ -14755,8 +14755,8 @@
10
-3216528
-3216529
+3216594
+3216595
10
@@ -14809,15 +14809,15 @@
macroparent
-31702256
+31697444
id
-31702256
+31697444
parent_id
-24679337
+24675437
@@ -14831,7 +14831,7 @@
1
2
-31702256
+31697444
@@ -14847,17 +14847,17 @@
1
2
-18989054
+18985994
2
3
-4852979
+4852266
3
88
-837303
+837176
@@ -14945,11 +14945,11 @@
macro_argument_unexpanded
-92645581
+92630091
invocation
-27418856
+27414544
argument_index
@@ -14957,7 +14957,7 @@
text
-312667
+312611
@@ -14971,22 +14971,22 @@
1
2
-7656282
+7655047
2
3
-11466261
+11464654
3
4
-6261232
+6260126
4
67
-2035079
+2034716
@@ -15002,22 +15002,22 @@
1
2
-7724233
+7722986
2
3
-11620160
+11618525
3
4
-6089819
+6088743
4
67
-1984642
+1984288
@@ -15033,7 +15033,7 @@
50787
50788
-636
+635
50989
@@ -15041,8 +15041,8 @@
54
-756484
-2500139
+756485
+2500192
32
@@ -15059,7 +15059,7 @@
2
3
-636
+635
13
@@ -15085,57 +15085,57 @@
1
2
-37901
+37894
2
3
-61162
+61118
3
4
-14092
+14089
4
5
-42124
+42138
5
8
-25300
+25296
8
12
-15112
+15120
12
16
-21780
+21765
16
21
-23940
+23936
21
41
-24971
+24967
41
121
-23787
+23794
121
567376
-22493
+22489
@@ -15151,17 +15151,17 @@
1
2
-226061
+226021
2
3
-76307
+76294
3
9
-10297
+10296
@@ -15171,11 +15171,11 @@
macro_argument_expanded
-92645581
+92630091
invocation
-27418856
+27414544
argument_index
@@ -15183,7 +15183,7 @@
text
-189442
+189409
@@ -15197,22 +15197,22 @@
1
2
-7656282
+7655047
2
3
-11466261
+11464654
3
4
-6261232
+6260126
4
67
-2035079
+2034716
@@ -15228,22 +15228,22 @@
1
2
-11164682
+11162952
2
3
-9894184
+9892725
3
4
-5269679
+5268749
4
9
-1090310
+1090116
@@ -15259,7 +15259,7 @@
50787
50788
-636
+635
50989
@@ -15267,8 +15267,8 @@
54
-756484
-2500139
+756485
+2500192
32
@@ -15311,62 +15311,62 @@
1
2
-22866
+22861
2
3
-38219
+38180
3
4
-6240
+6239
4
5
-15167
+15186
5
6
-2763
+2752
6
7
-22032
+22039
7
9
-15463
+15471
9
15
-16494
+16480
15
28
-14257
+14254
28
77
-14322
+14331
77
337
-14322
+14320
338
-1133296
-7293
+1133308
+7291
@@ -15382,17 +15382,17 @@
1
2
-95631
+95614
2
3
-79488
+79474
3
6
-14246
+14243
6
@@ -15407,15 +15407,15 @@
functions
-3468206
+3467587
id
-3468206
+3467587
name
-288386
+288335
kind
@@ -15433,7 +15433,7 @@
1
2
-3468206
+3467587
@@ -15449,7 +15449,7 @@
1
2
-3468206
+3467587
@@ -15465,27 +15465,27 @@
1
2
-195397
+195363
2
3
-28316
+28311
3
5
-26079
+26074
5
13
-22164
+22160
13
123517
-16428
+16425
@@ -15501,7 +15501,7 @@
1
2
-286927
+286876
2
@@ -15608,15 +15608,15 @@
function_entry_point
-1008530
+1008372
id
-1005602
+1005444
entry_point
-1008530
+1008372
@@ -15630,7 +15630,7 @@
1
2
-1002948
+1002791
2
@@ -15651,7 +15651,7 @@
1
2
-1008530
+1008372
@@ -15661,15 +15661,15 @@
function_return_type
-3478372
+3477751
id
-3467734
+3467115
return_type
-1023555
+1023372
@@ -15683,12 +15683,12 @@
1
2
-3457590
+3456973
2
6
-10144
+10142
@@ -15704,17 +15704,17 @@
1
2
-299156
+299102
2
3
-662688
+662569
3
84263
-61710
+61699
@@ -15735,49 +15735,49 @@
function_deleted
-56183
+56173
id
-56183
+56173
function_defaulted
-12930
+12927
id
-12930
+12927
fun_decls
-3543779
+3543146
id
-3540511
+3539879
function
-3374756
+3374154
type_id
-1010482
+1010302
name
-256834
+256788
location
-795431
+795290
@@ -15791,7 +15791,7 @@
1
2
-3540511
+3539879
@@ -15807,12 +15807,12 @@
1
2
-3537528
+3536896
2
4
-2983
+2982
@@ -15828,7 +15828,7 @@
1
2
-3540511
+3539879
@@ -15844,7 +15844,7 @@
1
2
-3540511
+3539879
@@ -15860,12 +15860,12 @@
1
2
-3238733
+3238156
2
9
-136022
+135998
@@ -15881,12 +15881,12 @@
1
2
-3358098
+3357498
2
6
-16658
+16655
@@ -15902,7 +15902,7 @@
1
2
-3374756
+3374154
@@ -15918,12 +15918,12 @@
1
2
-3287120
+3286533
2
9
-87636
+87621
@@ -15939,17 +15939,17 @@
1
2
-280325
+280275
2
3
-661065
+660947
3
89606
-69091
+69079
@@ -15965,17 +15965,17 @@
1
2
-292159
+292107
2
3
-657413
+657295
3
83378
-60910
+60899
@@ -15991,12 +15991,12 @@
1
2
-943946
+943777
2
7512
-66536
+66524
@@ -16012,17 +16012,17 @@
1
2
-913085
+912922
2
6
-80146
+80132
6
22467
-17250
+17247
@@ -16038,32 +16038,32 @@
1
2
-153548
+153520
2
3
-29303
+29298
3
4
-15934
+15932
4
6
-19521
+19517
6
13
-20201
+20197
13
123766
-18325
+18322
@@ -16079,32 +16079,32 @@
1
2
-164175
+164145
2
3
-28481
+28476
3
4
-14454
+14451
4
7
-21637
+21633
7
25
-19641
+19638
25
123501
-8444
+8443
@@ -16120,17 +16120,17 @@
1
2
-224328
+224288
2
5
-20870
+20866
5
63265
-11635
+11633
@@ -16146,27 +16146,27 @@
1
2
-164668
+164639
2
3
-43461
+43454
3
4
-16439
+16436
4
8
-20573
+20570
8
8921
-11690
+11688
@@ -16182,27 +16182,27 @@
1
2
-522750
+522656
2
3
-143886
+143860
3
5
-64858
+64846
5
125
-59693
+59682
125
3043
-4244
+4243
@@ -16218,22 +16218,22 @@
1
2
-537676
+537580
2
3
-156849
+156821
3
9
-64343
+64331
9
3043
-36563
+36557
@@ -16249,17 +16249,17 @@
1
2
-701785
+701660
2
4
-61787
+61776
4
1522
-31858
+31853
@@ -16275,12 +16275,12 @@
1
2
-770427
+770289
2
134
-25004
+25000
@@ -16290,11 +16290,11 @@
fun_def
-1230764
+1230544
id
-1230764
+1230544
@@ -16517,26 +16517,26 @@
fun_decl_empty_throws
-1420942
+1420688
fun_decl
-1420942
+1420688
fun_decl_noexcept
-32911
+32905
fun_decl
-32122
+32116
constant
-32791
+32785
@@ -16550,7 +16550,7 @@
1
2
-31332
+31326
2
@@ -16571,7 +16571,7 @@
1
2
-32670
+32664
2
@@ -16586,11 +16586,11 @@
fun_decl_empty_noexcept
-391947
+391877
fun_decl
-391947
+391877
@@ -16645,11 +16645,11 @@
param_decl_bind
-4651385
+4650555
id
-4651385
+4650555
index
@@ -16657,7 +16657,7 @@
fun_decl
-3071663
+3071115
@@ -16671,7 +16671,7 @@
1
2
-4651385
+4650555
@@ -16687,7 +16687,7 @@
1
2
-4651385
+4650555
@@ -16785,22 +16785,22 @@
1
2
-2195218
+2194827
2
3
-478564
+478478
3
4
-242709
+242665
4
65
-155171
+155143
@@ -16816,22 +16816,22 @@
1
2
-2195218
+2194827
2
3
-478564
+478478
3
4
-242709
+242665
4
65
-155171
+155143
@@ -16841,27 +16841,27 @@
var_decls
-5368776
+5367960
id
-5359926
+5359112
variable
-5132044
+5131248
type_id
-2007706
+2007358
name
-126306
+126283
location
-1232409
+1232189
@@ -16875,7 +16875,7 @@
1
2
-5359926
+5359112
@@ -16891,12 +16891,12 @@
1
2
-5351262
+5350449
2
4
-8663
+8662
@@ -16912,7 +16912,7 @@
1
2
-5359926
+5359112
@@ -16928,7 +16928,7 @@
1
2
-5359882
+5359068
2
@@ -16949,12 +16949,12 @@
1
2
-4944542
+4943758
2
9
-187501
+187490
@@ -16970,12 +16970,12 @@
1
2
-5096697
+5095886
2
7
-35346
+35362
@@ -16991,12 +16991,12 @@
1
2
-5114475
+5113682
2
3
-17569
+17565
@@ -17012,12 +17012,12 @@
1
2
-5023833
+5023057
2
9
-108210
+108191
@@ -17033,22 +17033,22 @@
1
2
-1580818
+1580525
2
3
-228879
+228861
3
11
-156958
+156941
11
5924
-41049
+41030
@@ -17064,22 +17064,22 @@
1
2
-1605011
+1604713
2
3
-219645
+219628
3
13
-151135
+151108
13
5424
-31913
+31908
@@ -17095,17 +17095,17 @@
1
2
-1832849
+1832533
2
5
-151245
+151218
5
772
-23611
+23607
@@ -17121,17 +17121,17 @@
1
2
-1758460
+1758157
2
4
-154469
+154441
4
3608
-94776
+94759
@@ -17147,42 +17147,42 @@
1
2
-52158
+52083
2
3
-19236
+19276
3
4
-10890
+10910
4
5
-7687
+7686
5
8
-10517
+10515
8
15
-9508
+9506
15
47
-9530
+9528
47
165630
-6777
+6776
@@ -17198,37 +17198,37 @@
1
2
-54977
+54901
2
3
-18786
+18826
3
4
-11789
+11809
4
6
-11197
+11195
6
11
-10736
+10734
11
27
-9486
+9484
27
164602
-9332
+9331
@@ -17244,32 +17244,32 @@
1
2
-76307
+76294
2
3
-16889
+16886
3
4
-8861
+8848
4
7
-10473
+10482
7
27
-9519
+9517
27
125807
-4255
+4254
@@ -17285,32 +17285,32 @@
1
2
-72820
+72807
2
3
-19027
+19024
3
4
-6974
+6973
4
7
-11186
+11184
7
21
-9749
+9747
21
10073
-6547
+6546
@@ -17326,22 +17326,22 @@
1
2
-892478
+892231
2
3
-149062
+149123
3
6
-113420
+113388
6
128450
-77448
+77445
@@ -17357,22 +17357,22 @@
1
2
-941237
+940981
2
3
-114483
+114551
3
6
-102661
+102632
6
128224
-74026
+74024
@@ -17388,17 +17388,17 @@
1
2
-1055370
+1055160
2
3
-85092
+85099
3
118388
-91946
+91930
@@ -17414,12 +17414,12 @@
1
2
-1223438
+1223220
2
52
-8970
+8969
@@ -17429,11 +17429,11 @@
var_def
-2437456
+2437164
id
-2437456
+2437164
@@ -17503,19 +17503,19 @@
type_decls
-1332120
+1331883
id
-1332120
+1331883
type_id
-1300371
+1300139
location
-1086812
+1086618
@@ -17529,7 +17529,7 @@
1
2
-1332120
+1331883
@@ -17545,7 +17545,7 @@
1
2
-1332120
+1331883
@@ -17561,12 +17561,12 @@
1
2
-1277275
+1277047
2
24
-23096
+23092
@@ -17582,12 +17582,12 @@
1
2
-1278569
+1278341
2
24
-21802
+21798
@@ -17603,12 +17603,12 @@
1
2
-1031352
+1031168
2
506
-55459
+55449
@@ -17624,12 +17624,12 @@
1
2
-1032833
+1032648
2
506
-53979
+53969
@@ -17639,45 +17639,45 @@
type_def
-937716
+937549
id
-937716
+937549
type_decl_top
-268689
+268642
type_decl
-268689
+268642
namespace_decls
-136867
+136842
id
-136867
+136842
namespace_id
-7676
+7675
location
-122248
+122226
bodylocation
-122577
+122555
@@ -17691,7 +17691,7 @@
1
2
-136867
+136842
@@ -17707,7 +17707,7 @@
1
2
-136867
+136842
@@ -17723,7 +17723,7 @@
1
2
-136867
+136842
@@ -17739,7 +17739,7 @@
1
2
-3619
+3618
2
@@ -17754,7 +17754,7 @@
4
7
-647
+646
7
@@ -17790,7 +17790,7 @@
1
2
-3619
+3618
2
@@ -17805,7 +17805,7 @@
4
7
-647
+646
7
@@ -17841,7 +17841,7 @@
1
2
-3619
+3618
2
@@ -17856,7 +17856,7 @@
4
7
-647
+646
7
@@ -17892,12 +17892,12 @@
1
2
-113869
+113849
2
8
-8378
+8377
@@ -17913,12 +17913,12 @@
1
2
-113869
+113849
2
8
-8378
+8377
@@ -17934,7 +17934,7 @@
1
2
-121502
+121481
2
@@ -17955,12 +17955,12 @@
1
2
-114560
+114540
2
11
-8016
+8015
@@ -17976,12 +17976,12 @@
1
2
-114560
+114540
2
9
-8016
+8015
@@ -17997,7 +17997,7 @@
1
2
-122182
+122160
2
@@ -18012,19 +18012,19 @@
usings
-291435
+291383
id
-291435
+291383
element_id
-46401
+46392
location
-23864
+23859
@@ -18038,7 +18038,7 @@
1
2
-291435
+291383
@@ -18054,7 +18054,7 @@
1
2
-291435
+291383
@@ -18070,7 +18070,7 @@
1
2
-39393
+39386
2
@@ -18080,7 +18080,7 @@
4
127
-3257
+3256
@@ -18096,7 +18096,7 @@
1
2
-39393
+39386
2
@@ -18106,7 +18106,7 @@
4
127
-3257
+3256
@@ -18122,12 +18122,12 @@
1
2
-18062
+18059
2
3
-2237
+2236
3
@@ -18153,12 +18153,12 @@
1
2
-18062
+18059
2
3
-2237
+2236
3
@@ -18178,15 +18178,15 @@
using_container
-458088
+458007
parent
-11230
+11228
child
-291435
+291383
@@ -18200,12 +18200,12 @@
1
2
-3213
+3212
2
4
-943
+942
4
@@ -18230,7 +18230,7 @@
178
179
-1261
+1260
179
@@ -18256,17 +18256,17 @@
1
2
-215851
+215812
2
3
-50371
+50362
3
11
-23140
+23136
13
@@ -18647,15 +18647,15 @@
params
-4644903
+4644074
id
-4627839
+4627013
function
-3043840
+3043297
index
@@ -18663,7 +18663,7 @@
type_id
-1856790
+1856458
@@ -18677,7 +18677,7 @@
1
2
-4627170
+4626344
2
@@ -18698,7 +18698,7 @@
1
2
-4627839
+4627013
@@ -18714,12 +18714,12 @@
1
2
-4612792
+4611969
2
4
-15046
+15043
@@ -18735,22 +18735,22 @@
1
2
-2166606
+2166219
2
3
-475109
+475024
3
4
-244453
+244409
4
65
-157671
+157643
@@ -18766,22 +18766,22 @@
1
2
-2166606
+2166219
2
3
-475109
+475024
3
4
-244453
+244409
4
65
-157671
+157643
@@ -18797,22 +18797,22 @@
1
2
-2283184
+2282777
2
3
-470964
+470880
3
5
-254323
+254277
5
20
-35368
+35362
@@ -18951,22 +18951,22 @@
1
2
-1504718
+1504450
2
3
-186712
+186678
3
14
-139488
+139463
14
5175
-25871
+25866
@@ -18982,22 +18982,22 @@
1
2
-1524579
+1524307
2
3
-179846
+179814
3
23
-139718
+139693
23
4690
-12644
+12642
@@ -19013,12 +19013,12 @@
1
2
-1745102
+1744791
2
65
-111687
+111667
@@ -19116,19 +19116,19 @@
membervariables
-310298
+310243
id
-305681
+305626
type_id
-132919
+132895
name
-53222
+53213
@@ -19142,12 +19142,12 @@
1
2
-301217
+301164
2
7
-4463
+4462
@@ -19163,7 +19163,7 @@
1
2
-305681
+305626
@@ -19179,22 +19179,22 @@
1
2
-108068
+108048
2
3
-12272
+12269
3
9
-10056
+10054
9
1712
-2522
+2521
@@ -19210,17 +19210,17 @@
1
2
-115119
+115099
2
3
-9091
+9089
3
266
-8707
+8706
@@ -19236,17 +19236,17 @@
1
2
-28097
+28092
2
3
-8170
+8168
3
4
-5373
+5372
4
@@ -19256,12 +19256,12 @@
6
15
-4090
+4089
15
1967
-3476
+3475
@@ -19277,12 +19277,12 @@
1
2
-34293
+34287
2
3
-6854
+6853
3
@@ -19292,7 +19292,7 @@
4
7
-4299
+4298
7
@@ -20844,19 +20844,19 @@
builtintypes
-504
+515
id
-504
+515
name
-504
+515
kind
-504
+515
size
@@ -20882,7 +20882,7 @@
1
2
-504
+515
@@ -20898,7 +20898,7 @@
1
2
-504
+515
@@ -20914,7 +20914,7 @@
1
2
-504
+515
@@ -20930,7 +20930,7 @@
1
2
-504
+515
@@ -20946,7 +20946,7 @@
1
2
-504
+515
@@ -20962,7 +20962,7 @@
1
2
-504
+515
@@ -20978,7 +20978,7 @@
1
2
-504
+515
@@ -20994,7 +20994,7 @@
1
2
-504
+515
@@ -21010,7 +21010,7 @@
1
2
-504
+515
@@ -21026,7 +21026,7 @@
1
2
-504
+515
@@ -21042,7 +21042,7 @@
1
2
-504
+515
@@ -21058,7 +21058,7 @@
1
2
-504
+515
@@ -21074,7 +21074,7 @@
1
2
-504
+515
@@ -21090,7 +21090,7 @@
1
2
-504
+515
@@ -21106,7 +21106,7 @@
1
2
-504
+515
@@ -21135,8 +21135,8 @@
10
-6
-7
+7
+8
10
@@ -21181,8 +21181,8 @@
10
-6
-7
+7
+8
10
@@ -21227,8 +21227,8 @@
10
-6
-7
+7
+8
10
@@ -21310,8 +21310,8 @@
10
-28
-29
+29
+30
10
@@ -21336,8 +21336,8 @@
10
-28
-29
+29
+30
10
@@ -21362,8 +21362,8 @@
10
-28
-29
+29
+30
10
@@ -21420,8 +21420,8 @@
10
-7
-8
+8
+9
10
@@ -21456,8 +21456,8 @@
10
-7
-8
+8
+9
10
@@ -21492,8 +21492,8 @@
10
-7
-8
+8
+9
10
@@ -21556,15 +21556,15 @@
derivedtypes
-4417712
+4416924
id
-4417712
+4416924
name
-2172605
+2172217
kind
@@ -21572,7 +21572,7 @@
type_id
-2606150
+2605685
@@ -21586,7 +21586,7 @@
1
2
-4417712
+4416924
@@ -21602,7 +21602,7 @@
1
2
-4417712
+4416924
@@ -21618,7 +21618,7 @@
1
2
-4417712
+4416924
@@ -21634,17 +21634,17 @@
1
2
-1570509
+1570229
2
3
-487524
+487437
3
45177
-114571
+114551
@@ -21660,7 +21660,7 @@
1
2
-2172572
+2172184
2
@@ -21681,17 +21681,17 @@
1
2
-1570750
+1570470
2
3
-487293
+487206
3
45159
-114560
+114540
@@ -21860,22 +21860,22 @@
1
2
-1546239
+1545963
2
3
-372700
+372633
3
4
-633867
+633753
4
202
-53343
+53333
@@ -21891,22 +21891,22 @@
1
2
-1547456
+1547180
2
3
-372546
+372480
3
4
-632803
+632690
4
198
-53343
+53333
@@ -21922,22 +21922,22 @@
1
2
-1547774
+1547498
2
3
-373829
+373763
3
4
-632759
+632646
4
7
-51785
+51776
@@ -21947,11 +21947,11 @@
pointerishsize
-3331733
+3331139
id
-3331733
+3331139
size
@@ -21973,7 +21973,7 @@
1
2
-3331733
+3331139
@@ -21989,7 +21989,7 @@
1
2
-3331733
+3331139
@@ -22068,11 +22068,11 @@
arraysizes
-17196
+17193
id
-17196
+17193
num_elements
@@ -22080,7 +22080,7 @@
bytesize
-2511
+2510
alignment
@@ -22098,7 +22098,7 @@
1
2
-17196
+17193
@@ -22114,7 +22114,7 @@
1
2
-17196
+17193
@@ -22130,7 +22130,7 @@
1
2
-17196
+17193
@@ -22151,7 +22151,7 @@
2
3
-1272
+1271
3
@@ -22192,7 +22192,7 @@
1
2
-1590
+1589
2
@@ -22228,7 +22228,7 @@
1
2
-1590
+1589
2
@@ -22315,7 +22315,7 @@
1
2
-1908
+1907
2
@@ -22346,7 +22346,7 @@
1
2
-1952
+1951
2
@@ -22504,15 +22504,15 @@
typedefbase
-1819645
+1819320
id
-1819645
+1819320
type_id
-847360
+847209
@@ -22526,7 +22526,7 @@
1
2
-1819645
+1819320
@@ -22542,22 +22542,22 @@
1
2
-656634
+656517
2
3
-87910
+87895
3
6
-69596
+69583
6
5503
-33218
+33212
@@ -22567,19 +22567,19 @@
decltypes
-47004
+46995
id
-47004
+46995
expr
-43440
+43432
base_type
-8630
+8629
parentheses_would_change_meaning
@@ -22597,7 +22597,7 @@
1
2
-47004
+46995
@@ -22613,7 +22613,7 @@
1
2
-47004
+46995
@@ -22629,7 +22629,7 @@
1
2
-47004
+46995
@@ -22645,12 +22645,12 @@
1
2
-40160
+40153
2
4
-3279
+3278
@@ -22666,12 +22666,12 @@
1
2
-40160
+40153
2
4
-3279
+3278
@@ -22687,7 +22687,7 @@
1
2
-43440
+43432
@@ -22703,12 +22703,12 @@
1
2
-5823
+5822
2
3
-2522
+2521
3
@@ -22729,12 +22729,12 @@
1
2
-2314
+2313
2
3
-5702
+5701
3
@@ -22755,7 +22755,7 @@
1
2
-8630
+8629
@@ -22828,15 +22828,15 @@
usertypes
-4193822
+4193074
id
-4193822
+4193074
name
-879383
+879226
kind
@@ -22854,7 +22854,7 @@
1
2
-4193822
+4193074
@@ -22870,7 +22870,7 @@
1
2
-4193822
+4193074
@@ -22886,22 +22886,22 @@
1
2
-577453
+577350
2
3
-194794
+194759
3
7
-69311
+69298
7
32744
-37824
+37818
@@ -22917,12 +22917,12 @@
1
2
-826742
+826595
2
10
-52641
+52631
@@ -23059,11 +23059,11 @@
usertypesize
-1386571
+1386324
id
-1386571
+1386324
size
@@ -23085,7 +23085,7 @@
1
2
-1386571
+1386324
@@ -23101,7 +23101,7 @@
1
2
-1386571
+1386324
@@ -23354,15 +23354,15 @@
mangled_name
-4190543
+4189795
id
-4190543
+4189795
mangled_name
-483674
+483588
@@ -23376,7 +23376,7 @@
1
2
-4190543
+4189795
@@ -23392,32 +23392,32 @@
1
2
-292082
+292030
2
3
-62292
+62281
3
4
-33339
+33333
4
7
-36969
+36962
7
24
-37068
+37061
24
8580
-21922
+21918
@@ -23427,59 +23427,59 @@
is_pod_class
-589209
+589104
id
-589209
+589104
is_standard_layout_class
-1159183
+1158976
id
-1159183
+1158976
is_complete
-1365065
+1364822
id
-1365065
+1364822
is_class_template
-225348
+225308
id
-225348
+225308
class_instantiation
-1157790
+1157583
to
-1156222
+1156015
from
-68027
+68015
@@ -23493,7 +23493,7 @@
1
2
-1154741
+1154535
2
@@ -23514,47 +23514,47 @@
1
2
-19959
+19956
2
3
-11997
+11995
3
4
-6854
+6853
4
5
-4617
+4616
5
7
-5637
+5635
7
11
-6053
+6052
11
20
-5198
+5197
20
84
-5110
+5109
84
4845
-2599
+2598
@@ -23564,11 +23564,11 @@
class_template_argument
-3035999
+3035457
type_id
-1392811
+1392563
index
@@ -23576,7 +23576,7 @@
arg_type
-860904
+860750
@@ -23590,27 +23590,27 @@
1
2
-567111
+567010
2
3
-433731
+433654
3
4
-244979
+244935
4
7
-122807
+122785
7
113
-24182
+24177
@@ -23626,22 +23626,22 @@
1
2
-593344
+593238
2
3
-445959
+445879
3
4
-258435
+258389
4
113
-95072
+95055
@@ -23749,27 +23749,27 @@
1
2
-522278
+522185
2
3
-187435
+187402
3
4
-56710
+56699
4
11
-67391
+67379
11
11852
-27088
+27083
@@ -23785,17 +23785,17 @@
1
2
-746453
+746320
2
3
-95543
+95526
3
22
-18906
+18903
@@ -23805,11 +23805,11 @@
class_template_argument_value
-345798
+345736
type_id
-223846
+223806
index
@@ -23817,7 +23817,7 @@
arg_value
-328240
+328181
@@ -23831,17 +23831,17 @@
1
2
-201484
+201448
2
3
-13708
+13706
3
14
-8652
+8651
@@ -23857,17 +23857,17 @@
1
2
-189859
+189825
2
3
-16921
+16918
3
37
-16790
+16787
44
@@ -24010,12 +24010,12 @@
1
2
-311000
+310944
2
4
-17240
+17236
@@ -24031,7 +24031,7 @@
1
2
-328240
+328181
@@ -24041,15 +24041,15 @@
is_proxy_class_for
-46379
+46370
id
-46379
+46370
templ_param_id
-46379
+46370
@@ -24063,7 +24063,7 @@
1
2
-46379
+46370
@@ -24079,7 +24079,7 @@
1
2
-46379
+46370
@@ -24395,26 +24395,26 @@
is_function_template
-983756
+983580
id
-983756
+983580
function_instantiation
-708310
+708184
to
-708310
+708184
from
-129333
+129310
@@ -24428,7 +24428,7 @@
1
2
-708310
+708184
@@ -24444,37 +24444,37 @@
1
2
-61009
+60998
2
3
-30839
+30833
3
4
-7347
+7346
4
5
-8817
+8815
5
10
-10001
+10000
10
71
-9705
+9704
71
653
-1612
+1611
@@ -24484,11 +24484,11 @@
function_template_argument
-1910528
+1910187
function_id
-1054361
+1054173
index
@@ -24496,7 +24496,7 @@
arg_type
-338450
+338390
@@ -24510,22 +24510,22 @@
1
2
-583583
+583479
2
3
-291007
+290955
3
4
-127764
+127742
4
21
-52005
+51995
@@ -24541,22 +24541,22 @@
1
2
-598005
+597898
2
3
-288693
+288642
3
4
-110941
+110921
4
21
-56720
+56710
@@ -24794,27 +24794,27 @@
1
2
-226127
+226086
2
3
-45117
+45109
3
6
-27669
+27664
6
19
-25607
+25603
19
2030
-13928
+13925
@@ -24830,12 +24830,12 @@
1
2
-314827
+314771
2
12
-23622
+23618
@@ -24845,11 +24845,11 @@
function_template_argument_value
-198062
+198027
function_id
-107081
+107062
index
@@ -24857,7 +24857,7 @@
arg_value
-170601
+170571
@@ -24871,12 +24871,12 @@
1
2
-101564
+101546
2
14
-5516
+5515
@@ -24892,17 +24892,17 @@
1
2
-84960
+84945
2
3
-16176
+16173
3
113
-5944
+5943
@@ -25030,12 +25030,12 @@
1
2
-143644
+143619
2
3
-26452
+26447
3
@@ -25056,7 +25056,7 @@
1
2
-170601
+170571
@@ -25066,26 +25066,26 @@
is_variable_template
-17810
+17807
id
-17810
+17807
variable_instantiation
-35807
+35800
to
-35807
+35800
from
-6470
+6469
@@ -25099,7 +25099,7 @@
1
2
-35807
+35800
@@ -25115,12 +25115,12 @@
1
2
-2237
+2236
2
3
-1908
+1907
3
@@ -25507,15 +25507,15 @@
routinetypes
-430474
+430397
id
-430474
+430397
return_type
-177565
+177533
@@ -25529,7 +25529,7 @@
1
2
-430474
+430397
@@ -25545,17 +25545,17 @@
1
2
-143107
+143082
2
3
-18128
+18125
3
9
-13324
+13322
9
@@ -25570,11 +25570,11 @@
routinetypeargs
-719836
+719708
routine
-351841
+351778
index
@@ -25582,7 +25582,7 @@
type_id
-205564
+205527
@@ -25596,27 +25596,27 @@
1
2
-161926
+161897
2
3
-95949
+95932
3
4
-53935
+53925
4
6
-32330
+32324
6
33
-7698
+7697
@@ -25632,22 +25632,22 @@
1
2
-186745
+186711
2
3
-96706
+96689
3
4
-47662
+47653
4
22
-20727
+20723
@@ -25805,27 +25805,27 @@
1
2
-122412
+122391
2
3
-40840
+40833
3
4
-13215
+13212
4
7
-16636
+16633
7
1349
-12458
+12456
@@ -25841,17 +25841,17 @@
1
2
-154107
+154079
2
3
-39437
+39430
3
33
-12019
+12017
@@ -25861,19 +25861,19 @@
ptrtomembers
-12633
+12631
id
-12633
+12631
type_id
-9398
+9396
class_id
-6360
+6359
@@ -25887,7 +25887,7 @@
1
2
-12633
+12631
@@ -25903,7 +25903,7 @@
1
2
-12633
+12631
@@ -25919,7 +25919,7 @@
1
2
-9036
+9035
2
@@ -25940,7 +25940,7 @@
1
2
-9036
+9035
2
@@ -25961,7 +25961,7 @@
1
2
-5340
+5339
2
@@ -25987,7 +25987,7 @@
1
2
-5340
+5339
2
@@ -26007,15 +26007,15 @@
specifiers
-493
+504
id
-493
+504
str
-493
+504
@@ -26029,7 +26029,7 @@
1
2
-493
+504
@@ -26045,7 +26045,7 @@
1
2
-493
+504
@@ -26055,11 +26055,11 @@
typespecifiers
-1331934
+1331696
type_id
-1325069
+1324832
spec_id
@@ -26077,12 +26077,12 @@
1
2
-1318203
+1317968
2
3
-6865
+6864
@@ -26138,11 +26138,11 @@
funspecifiers
-11102993
+11101011
func_id
-3417758
+3417148
spec_id
@@ -26160,27 +26160,27 @@
1
2
-342311
+342249
2
3
-436988
+436910
3
4
-842710
+842560
4
5
-1677316
+1677016
5
8
-118431
+118410
@@ -26384,11 +26384,11 @@
attributes
-413159
+413730
id
-413159
+413730
kind
@@ -26404,7 +26404,7 @@
location
-89984
+90510
@@ -26418,7 +26418,7 @@
1
2
-413159
+413730
@@ -26434,7 +26434,7 @@
1
2
-413159
+413730
@@ -26450,7 +26450,7 @@
1
2
-413159
+413730
@@ -26466,7 +26466,7 @@
1
2
-413159
+413730
@@ -26480,8 +26480,8 @@
12
-253807
-253808
+254158
+254159
1
@@ -26528,8 +26528,8 @@
12
-55278
-55279
+55601
+55602
1
@@ -26579,12 +26579,12 @@
4
-645
+648
780
4
-806
+807
1148
4
@@ -26594,7 +26594,7 @@
4
-25877
+26224
35247
4
@@ -26678,11 +26678,11 @@
440
-627
+628
4
-642
+645
776
4
@@ -26718,8 +26718,8 @@
12
-253807
-253808
+254158
+254159
1
@@ -26766,8 +26766,8 @@
12
-55278
-55279
+55601
+55602
1
@@ -26784,12 +26784,12 @@
1
2
-26208
+26730
2
3
-5310
+5313
3
@@ -26798,13 +26798,8 @@
4
-7
-6755
-
-
-7
8
-631
+7387
8
@@ -26830,7 +26825,7 @@
1
2
-89984
+90510
@@ -26846,7 +26841,7 @@
1
2
-27889
+28415
2
@@ -26882,7 +26877,7 @@
1
2
-89984
+90510
@@ -26892,11 +26887,11 @@
attribute_args
-152050
+152615
id
-152050
+152615
kind
@@ -26904,7 +26899,7 @@
attribute
-150751
+151316
index
@@ -26912,7 +26907,7 @@
location
-56842
+57362
@@ -26926,7 +26921,7 @@
1
2
-152050
+152615
@@ -26942,7 +26937,7 @@
1
2
-152050
+152615
@@ -26958,7 +26953,7 @@
1
2
-152050
+152615
@@ -26974,7 +26969,7 @@
1
2
-152050
+152615
@@ -26993,8 +26988,8 @@
1
-89512
-89513
+89859
+89860
1
@@ -27014,8 +27009,8 @@
1
-89030
-89031
+89377
+89378
1
@@ -27056,8 +27051,8 @@
1
-34672
-34673
+34991
+34992
1
@@ -27074,7 +27069,7 @@
1
2
-149973
+150538
2
@@ -27095,7 +27090,7 @@
1
2
-150261
+150826
2
@@ -27116,7 +27111,7 @@
1
2
-149973
+150538
2
@@ -27137,7 +27132,7 @@
1
2
-149978
+150543
2
@@ -27171,8 +27166,8 @@
1
-92593
-92594
+92940
+92941
1
@@ -27223,8 +27218,8 @@
1
-92608
-92609
+92955
+92956
1
@@ -27254,8 +27249,8 @@
1
-34262
-34263
+34581
+34582
1
@@ -27272,17 +27267,17 @@
1
2
-26779
+27295
2
3
-8446
+8429
3
5
-2865
+2886
5
@@ -27308,7 +27303,7 @@
1
2
-51047
+51567
2
@@ -27329,17 +27324,17 @@
1
2
-26765
+27281
2
3
-8463
+8445
3
5
-2863
+2884
5
@@ -27365,7 +27360,7 @@
1
2
-56837
+57357
3
@@ -27380,11 +27375,11 @@
attribute_arg_value
-152026
+152591
arg
-152026
+152591
value
@@ -27402,7 +27397,7 @@
1
2
-152026
+152591
@@ -27432,7 +27427,7 @@
10
-23973
+23976
133
@@ -27549,15 +27544,15 @@
typeattributes
-19323
+19320
type_id
-17941
+17938
spec_id
-19323
+19320
@@ -27571,7 +27566,7 @@
1
2
-17240
+17236
2
@@ -27592,7 +27587,7 @@
1
2
-19323
+19320
@@ -27602,15 +27597,15 @@
funcattributes
-304387
+304333
func_id
-164240
+164211
spec_id
-304387
+304333
@@ -27624,17 +27619,17 @@
1
2
-89687
+89671
2
3
-12579
+12576
3
4
-59967
+59956
4
@@ -27655,7 +27650,7 @@
1
2
-304387
+304333
@@ -27665,15 +27660,15 @@
varattributes
-371203
+371224
var_id
-322400
+322421
spec_id
-371203
+371224
@@ -27687,7 +27682,7 @@
1
2
-273633
+273654
2
@@ -27713,7 +27708,7 @@
1
2
-371203
+371224
@@ -27771,15 +27766,15 @@
unspecifiedtype
-9070084
+9068477
type_id
-9070084
+9068477
unspecified_type_id
-4976818
+4975940
@@ -27793,7 +27788,7 @@
1
2
-9070084
+9068477
@@ -27809,17 +27804,17 @@
1
2
-2709392
+2708920
2
3
-1952849
+1952501
3
7950
-314575
+314519
@@ -27829,11 +27824,11 @@
member
-4921643
+4920765
parent
-814481
+814336
index
@@ -27841,7 +27836,7 @@
child
-4906081
+4905205
@@ -27855,47 +27850,47 @@
1
2
-42321
+42313
2
3
-223780
+223740
3
4
-204621
+204584
4
5
-86923
+86908
5
7
-65900
+65888
7
9
-61612
+61601
9
15
-62050
+62039
15
47
-61184
+61173
47
245
-6086
+6085
@@ -27911,47 +27906,47 @@
1
2
-41663
+41655
2
3
-223604
+223564
3
4
-199378
+199343
4
5
-89731
+89715
5
7
-66371
+66360
7
9
-61689
+61678
9
15
-62972
+62960
15
42
-61272
+61261
42
281
-7797
+7796
@@ -28109,7 +28104,7 @@
1
2
-4906081
+4905205
@@ -28125,12 +28120,12 @@
1
2
-4890727
+4889854
2
7
-15353
+15350
@@ -28140,15 +28135,15 @@
enclosingfunction
-125077
+125055
child
-125077
+125055
parent
-71405
+71392
@@ -28162,7 +28157,7 @@
1
2
-125077
+125055
@@ -28178,22 +28173,22 @@
1
2
-38340
+38333
2
3
-21078
+21074
3
4
-6536
+6535
4
7
-5373
+5372
7
@@ -28208,15 +28203,15 @@
derivations
-390258
+390188
derivation
-390258
+390188
sub
-364442
+364377
index
@@ -28224,11 +28219,11 @@
super
-235701
+235659
location
-86978
+86963
@@ -28242,7 +28237,7 @@
1
2
-390258
+390188
@@ -28258,7 +28253,7 @@
1
2
-390258
+390188
@@ -28274,7 +28269,7 @@
1
2
-390258
+390188
@@ -28290,7 +28285,7 @@
1
2
-390258
+390188
@@ -28306,12 +28301,12 @@
1
2
-341872
+341811
2
7
-22569
+22565
@@ -28327,12 +28322,12 @@
1
2
-351490
+351427
2
7
-12951
+12949
@@ -28348,12 +28343,12 @@
1
2
-341883
+341822
2
7
-22558
+22554
@@ -28369,12 +28364,12 @@
1
2
-351479
+351416
2
7
-12962
+12960
@@ -28554,12 +28549,12 @@
1
2
-220917
+220878
2
1142
-14783
+14780
@@ -28575,12 +28570,12 @@
1
2
-220928
+220889
2
1142
-14772
+14769
@@ -28596,7 +28591,7 @@
1
2
-235251
+235209
2
@@ -28617,12 +28612,12 @@
1
2
-228342
+228301
2
439
-7358
+7357
@@ -28638,22 +28633,22 @@
1
2
-66349
+66338
2
3
-8389
+8388
3
7
-6613
+6611
7
795
-5626
+5625
@@ -28669,17 +28664,17 @@
1
2
-68587
+68574
2
3
-6371
+6370
3
8
-7040
+7039
8
@@ -28700,7 +28695,7 @@
1
2
-86956
+86941
2
@@ -28721,22 +28716,22 @@
1
2
-69289
+69276
2
3
-8203
+8201
3
9
-6525
+6524
9
795
-2961
+2960
@@ -28746,11 +28741,11 @@
derspecifiers
-392638
+392568
der_id
-390225
+390155
spec_id
@@ -28768,7 +28763,7 @@
1
2
-387812
+387743
2
@@ -28814,11 +28809,11 @@
direct_base_offsets
-310550
+310495
der_id
-310550
+310495
offset
@@ -28836,7 +28831,7 @@
1
2
-310550
+310495
@@ -28922,11 +28917,11 @@
virtual_base_offsets
-6338
+6337
sub
-3509
+3508
super
@@ -28979,7 +28974,7 @@
1
2
-2961
+2960
2
@@ -29213,23 +29208,23 @@
frienddecls
-240340
+240297
id
-240340
+240297
type_id
-27285
+27280
decl_id
-49044
+49035
location
-7303
+7302
@@ -29243,7 +29238,7 @@
1
2
-240340
+240297
@@ -29259,7 +29254,7 @@
1
2
-240340
+240297
@@ -29275,7 +29270,7 @@
1
2
-240340
+240297
@@ -29291,17 +29286,17 @@
1
2
-6218
+6217
2
3
-10232
+10230
3
5
-1985
+1984
5
@@ -29311,7 +29306,7 @@
6
8
-2314
+2313
8
@@ -29342,17 +29337,17 @@
1
2
-6218
+6217
2
3
-10232
+10230
3
5
-1985
+1984
5
@@ -29362,7 +29357,7 @@
6
8
-2314
+2313
8
@@ -29393,12 +29388,12 @@
1
2
-25706
+25701
2
31
-1579
+1578
@@ -29414,7 +29409,7 @@
1
2
-33690
+33684
2
@@ -29429,12 +29424,12 @@
7
23
-3805
+3804
23
394
-2533
+2532
@@ -29450,7 +29445,7 @@
1
2
-33690
+33684
2
@@ -29465,12 +29460,12 @@
7
23
-3805
+3804
23
394
-2533
+2532
@@ -29486,7 +29481,7 @@
1
2
-48506
+48498
2
@@ -29507,12 +29502,12 @@
1
2
-6240
+6239
2
3
-943
+942
3
@@ -29533,7 +29528,7 @@
1
2
-6865
+6864
2
@@ -29554,7 +29549,7 @@
1
2
-6251
+6250
2
@@ -29574,19 +29569,19 @@
comments
-1580291
+1580009
id
-1580291
+1580009
contents
-784157
+784018
location
-1580291
+1580009
@@ -29600,7 +29595,7 @@
1
2
-1580291
+1580009
@@ -29616,7 +29611,7 @@
1
2
-1580291
+1580009
@@ -29632,17 +29627,17 @@
1
2
-663938
+663819
2
3
-75101
+75088
3
10738
-45117
+45109
@@ -29658,17 +29653,17 @@
1
2
-663938
+663819
2
3
-75101
+75088
3
10738
-45117
+45109
@@ -29684,7 +29679,7 @@
1
2
-1580291
+1580009
@@ -29700,7 +29695,7 @@
1
2
-1580291
+1580009
@@ -29710,15 +29705,15 @@
commentbinding
-713289
+713206
id
-618370
+618260
element
-684512
+684434
@@ -29732,17 +29727,17 @@
1
2
-557010
+556867
2
3
-49044
+49079
3
97
-12315
+12313
@@ -29758,12 +29753,12 @@
1
2
-655735
+655661
2
3
-28777
+28772
@@ -29826,22 +29821,22 @@
compgenerated
-6707729
+6708045
id
-6707729
+6708045
synthetic_destructor_call
-59594
+59605
element
-46379
+46392
i
@@ -29849,7 +29844,7 @@
destructor_call
-49449
+49463
@@ -29863,12 +29858,12 @@
1
2
-36936
+36951
2
3
-6766
+6765
3
@@ -29889,12 +29884,12 @@
1
2
-36936
+36951
2
3
-6766
+6765
3
@@ -29933,8 +29928,8 @@
21
-4229
-4230
+4231
+4232
10
@@ -29969,8 +29964,8 @@
21
-3563
-3564
+3565
+3566
10
@@ -29987,17 +29982,17 @@
1
2
-43604
+43618
2
3
-3619
+3618
3
26
-2226
+2225
@@ -30013,7 +30008,7 @@
1
2
-49449
+49463
@@ -30023,15 +30018,15 @@
namespaces
-7687
+7686
id
-7687
+7686
name
-4134
+4133
@@ -30045,7 +30040,7 @@
1
2
-7687
+7686
@@ -30061,7 +30056,7 @@
1
2
-3476
+3475
2
@@ -30092,15 +30087,15 @@
namespacembrs
-1603322
+1603036
parentid
-7161
+7160
memberid
-1603322
+1603036
@@ -30174,7 +30169,7 @@
778
39485
-318
+317
@@ -30190,7 +30185,7 @@
1
2
-1603322
+1603036
@@ -30490,11 +30485,11 @@
iscall
-2320176
+2320354
caller
-2320176
+2320354
kind
@@ -30512,7 +30507,7 @@
1
2
-2320176
+2320354
@@ -30531,13 +30526,13 @@
10
-6428
-6429
+6429
+6430
10
-203747
-203748
+203800
+203801
10
@@ -30548,11 +30543,11 @@
numtemplatearguments
-164712
+164694
expr_id
-164712
+164694
num
@@ -30570,7 +30565,7 @@
1
2
-164712
+164694
@@ -30599,8 +30594,8 @@
10
-14306
-14307
+14307
+14308
10
@@ -31146,11 +31141,11 @@
expr_allocator
-30334
+30329
expr
-30334
+30329
func
@@ -31172,7 +31167,7 @@
1
2
-30334
+30329
@@ -31188,7 +31183,7 @@
1
2
-30334
+30329
@@ -31297,11 +31292,11 @@
expr_deallocator
-33306
+33300
expr
-33306
+33300
func
@@ -31323,7 +31318,7 @@
1
2
-33306
+33300
@@ -31339,7 +31334,7 @@
1
2
-33306
+33300
@@ -32525,15 +32520,15 @@
expr_ancestor
-66075
+66085
exp
-65373
+65384
ancestor
-47092
+47105
@@ -32547,12 +32542,12 @@
1
2
-64737
+64748
2
4
-636
+635
@@ -32568,12 +32563,12 @@
1
2
-34929
+34945
2
3
-9705
+9704
3
@@ -32600,7 +32595,7 @@
location
-3623125
+3622478
@@ -32665,7 +32660,7 @@
306
-471
+472
87
@@ -32694,18 +32689,18 @@
87
-6722
+6723
13441
87
17876
-114329
+114359
87
-192875
-428313
+192896
+428379
43
@@ -32798,37 +32793,37 @@
1
2
-1680540
+1679637
2
3
-738546
+738677
3
4
-319883
+319727
4
5
-276871
+277074
5
9
-301722
+301811
9
53
-272089
+272074
53
144742
-33471
+33476
@@ -32844,17 +32839,17 @@
1
2
-2587089
+2586627
2
3
-806881
+806737
3
30
-229154
+229113
@@ -32864,15 +32859,15 @@
expr_types
-18573253
+18573393
id
-18430420
+18430421
typeid
-1322513
+1322332
value_category
@@ -32890,12 +32885,12 @@
1
2
-18289045
+18288907
2
4
-141374
+141514
@@ -32911,7 +32906,7 @@
1
2
-18430420
+18430421
@@ -32927,42 +32922,42 @@
1
2
-513779
+513731
2
3
-252118
+252041
3
4
-108364
+108355
4
5
-86046
+86009
5
8
-114220
+114200
8
14
-106050
+106042
14
45
-99744
+99759
45
-126302
-42189
+126323
+42193
@@ -32978,17 +32973,17 @@
1
2
-1170972
+1170807
2
3
-143140
+143125
3
4
-8400
+8399
@@ -33007,13 +33002,13 @@
10
-370445
-370446
+370541
+370542
10
-1304652
-1304653
+1304856
+1304857
10
@@ -33033,13 +33028,13 @@
10
-30956
-30957
+30957
+30958
10
-102766
-102767
+102771
+102772
10
@@ -33050,15 +33045,15 @@
new_allocated_type
-32133
+32127
expr
-32133
+32127
type_id
-16516
+16513
@@ -33072,7 +33067,7 @@
1
2
-32133
+32127
@@ -33088,7 +33083,7 @@
1
2
-10341
+10339
2
@@ -33713,15 +33708,15 @@
condition_decl_bind
-7007
+7028
expr
-7007
+7028
decl
-7007
+7028
@@ -33735,7 +33730,7 @@
1
2
-7007
+7028
@@ -33751,7 +33746,7 @@
1
2
-7007
+7028
@@ -33761,11 +33756,11 @@
typeid_bind
-4419
+4418
expr
-4419
+4418
type_id
@@ -33783,7 +33778,7 @@
1
2
-4419
+4418
@@ -35478,11 +35473,11 @@
stmts
-4688881
+4688614
id
-4688881
+4688614
kind
@@ -35490,7 +35485,7 @@
location
-1194069
+1193856
@@ -35504,7 +35499,7 @@
1
2
-4688881
+4688614
@@ -35520,7 +35515,7 @@
1
2
-4688881
+4688614
@@ -35554,8 +35549,8 @@
10
-735
-736
+736
+737
10
@@ -35569,13 +35564,13 @@
10
-2235
-2236
+2237
+2238
10
-2266
-2267
+2267
+2268
10
@@ -35584,13 +35579,13 @@
10
-2826
-2827
+2827
+2828
10
-3119
-3120
+3121
+3122
10
@@ -35599,33 +35594,33 @@
10
-4772
-4773
+4775
+4776
10
-30477
-30478
+30484
+30485
10
-55902
-55903
+55911
+55912
10
-90767
-90768
+90778
+90779
10
-103054
-103055
+103056
+103057
10
-120826
-120827
+120839
+120840
10
@@ -35748,32 +35743,32 @@
1
2
-677987
+677438
2
3
-181579
+181952
3
4
-107870
+107851
4
6
-102135
+102116
6
22
-101531
+101535
22
5041
-22964
+22960
@@ -35789,12 +35784,12 @@
1
2
-1170479
+1170270
2
9
-23589
+23585
@@ -36092,15 +36087,15 @@
while_body
-30992
+30997
while_stmt
-30992
+30997
body_id
-30992
+30997
@@ -36114,7 +36109,7 @@
1
2
-30992
+30997
@@ -36130,7 +36125,7 @@
1
2
-30992
+30997
@@ -37427,15 +37422,15 @@
blockscope
-1325069
+1324975
block
-1325069
+1324975
enclosing
-1186907
+1186772
@@ -37449,7 +37444,7 @@
1
2
-1325069
+1324975
@@ -37465,12 +37460,12 @@
1
2
-1106552
+1106388
2
509
-80354
+80384
@@ -37666,11 +37661,11 @@
preprocdirects
-1323588
+1323352
id
-1323588
+1323352
kind
@@ -37678,7 +37673,7 @@
location
-1317271
+1317036
@@ -37692,7 +37687,7 @@
1
2
-1323588
+1323352
@@ -37708,7 +37703,7 @@
1
2
-1323588
+1323352
@@ -37876,12 +37871,12 @@
1
2
-1316942
+1316707
2
235
-329
+328
@@ -37897,7 +37892,7 @@
1
2
-1317271
+1317036
@@ -37907,15 +37902,15 @@
preprocpair
-378798
+378730
begin
-300505
+300451
elseelifend
-378798
+378730
@@ -37929,17 +37924,17 @@
1
2
-238618
+238576
2
3
-54549
+54539
3
53
-7336
+7335
@@ -37955,7 +37950,7 @@
1
2
-378798
+378730
@@ -37965,41 +37960,41 @@
preproctrue
-166565
+166536
branch
-166565
+166536
preprocfalse
-119122
+119101
branch
-119122
+119101
preproctext
-965408
+965236
id
-965408
+965236
head
-463561
+463478
body
-175580
+175549
@@ -38013,7 +38008,7 @@
1
2
-965408
+965236
@@ -38029,7 +38024,7 @@
1
2
-965408
+965236
@@ -38045,17 +38040,17 @@
1
2
-345963
+345901
2
3
-78172
+78158
3
19
-34776
+34769
19
@@ -38076,12 +38071,12 @@
1
2
-441879
+441800
2
38
-21681
+21677
@@ -38097,12 +38092,12 @@
1
2
-165008
+164979
2
64816
-10572
+10570
@@ -38118,12 +38113,12 @@
1
2
-166489
+166459
2
21810
-9091
+9089
@@ -38133,15 +38128,15 @@
includes
-290843
+290791
id
-290843
+290791
included
-54604
+54594
@@ -38155,7 +38150,7 @@
1
2
-290843
+290791
@@ -38171,37 +38166,37 @@
1
2
-26890
+26886
2
3
-8959
+8958
3
4
-4617
+4616
4
6
-4847
+4846
6
11
-4189
+4188
11
41
-4112
+4111
41
763
-987
+986
@@ -38259,11 +38254,11 @@
link_parent
-18153176
+18149936
element
-4992577
+4991686
link_target
@@ -38281,32 +38276,32 @@
1
2
-1493422
+1493156
2
3
-1883187
+1882851
3
4
-718838
+718710
4
6
-400863
+400791
6
29
-398143
+398072
29
45
-98121
+98103
diff --git a/cpp/ql/test/examples/expressions/PrintAST.expected b/cpp/ql/test/examples/expressions/PrintAST.expected
index 9a782825164..585ebae6ff4 100644
--- a/cpp/ql/test/examples/expressions/PrintAST.expected
+++ b/cpp/ql/test/examples/expressions/PrintAST.expected
@@ -430,28 +430,28 @@ DynamicCast.cpp:
#-----| Conversion = [BaseClassConversion] base class conversion
#-----| Type = [PointerType] Base *
#-----| ValueCategory = prvalue
-#-----| expr: [ThisExpr] this
-#-----| Type = [PointerType] Derived *
-#-----| ValueCategory = prvalue(load)
+# 4| expr: [ThisExpr] this
+# 4| Type = [PointerType] Derived *
+# 4| ValueCategory = prvalue(load)
#-----| 0: [ReferenceToExpr] (reference to)
#-----| Type = [LValueReferenceType] const Base &
#-----| ValueCategory = prvalue
-#-----| expr: [PointerDereferenceExpr] * ...
-#-----| Type = [SpecifiedType] const Base
-#-----| ValueCategory = lvalue
+# 4| expr: [PointerDereferenceExpr] * ...
+# 4| Type = [SpecifiedType] const Base
+# 4| ValueCategory = lvalue
#-----| 0: [CStyleCast] (const Base *)...
#-----| Conversion = [BaseClassConversion] base class conversion
#-----| Type = [PointerType] const Base *
#-----| ValueCategory = prvalue
-#-----| expr: [AddressOfExpr] & ...
-#-----| Type = [PointerType] const Derived *
-#-----| ValueCategory = prvalue
+# 4| expr: [AddressOfExpr] & ...
+# 4| Type = [PointerType] const Derived *
+# 4| ValueCategory = prvalue
#-----| 0: [ReferenceDereferenceExpr] (reference dereference)
#-----| Type = [SpecifiedType] const Derived
#-----| ValueCategory = lvalue
-#-----| expr: [VariableAccess] p#0
-#-----| Type = [LValueReferenceType] const Derived &
-#-----| ValueCategory = prvalue(load)
+# 4| expr: [VariableAccess] p#0
+# 4| Type = [LValueReferenceType] const Derived &
+# 4| ValueCategory = prvalue(load)
#-----| 1: [ReturnStmt] return ...
#-----| 0: [ReferenceToExpr] (reference to)
#-----| Type = [LValueReferenceType] Derived &
@@ -1248,9 +1248,9 @@ union_etc.cpp:
# 6| 0: [PointerFieldAccess] x
# 6| Type = [IntType] int
# 6| ValueCategory = lvalue
-#-----| -1: [ThisExpr] this
-#-----| Type = [PointerType] S *
-#-----| ValueCategory = prvalue(load)
+# 6| -1: [ThisExpr] this
+# 6| Type = [PointerType] S *
+# 6| ValueCategory = prvalue(load)
# 6| 1: [VariableAccess] val
# 6| Type = [IntType] int
# 6| ValueCategory = prvalue(load)
@@ -1431,9 +1431,9 @@ union_etc.cpp:
# 33| 0: [PointerFieldAccess] q
# 33| Type = [IntType] int
# 33| ValueCategory = lvalue
-#-----| -1: [ThisExpr] this
-#-----| Type = [PointerType] T *
-#-----| ValueCategory = prvalue(load)
+# 33| -1: [ThisExpr] this
+# 33| Type = [PointerType] T *
+# 33| ValueCategory = prvalue(load)
# 33| 1: [VariableAccess] val
# 33| Type = [IntType] int
# 33| ValueCategory = prvalue(load)
diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/arraylengthanalysis/ArrayLengthAnalysisTest.expected b/cpp/ql/test/experimental/library-tests/rangeanalysis/arraylengthanalysis/ArrayLengthAnalysisTest.expected
new file mode 100644
index 00000000000..b5e61ed55c5
--- /dev/null
+++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/arraylengthanalysis/ArrayLengthAnalysisTest.expected
@@ -0,0 +1,23 @@
+| test.cpp:15:8:15:11 | Load: aptr | VNLength(InitializeParameter: count) | 0 | ZeroOffset | 0 |
+| test.cpp:19:8:19:8 | Load: a | VNLength(Chi: ptr) | 0 | ZeroOffset | 0 |
+| test.cpp:21:8:21:8 | Load: a | VNLength(Chi: ptr) | -1 | ZeroOffset | 0 |
+| test.cpp:23:8:23:8 | Load: a | VNLength(Chi: ptr) | 1 | ZeroOffset | 0 |
+| test.cpp:27:8:27:8 | Load: c | VNLength(Chi: ptr) | 0 | ZeroOffset | 0 |
+| test.cpp:28:8:28:24 | Convert: (unsigned char *)... | VNLength(Chi: ptr) | 0 | ZeroOffset | 0 |
+| test.cpp:30:8:30:8 | Load: v | VNLength(Chi: ptr) | 0 | ZeroOffset | 0 |
+| test.cpp:34:8:34:12 | Convert: array to pointer conversion | ZeroLength | 100 | ZeroOffset | 0 |
+| test.cpp:37:10:37:10 | Load: b | VNLength(Chi: ptr) | 0 | ZeroOffset | 0 |
+| test.cpp:44:8:44:8 | Load: a | VNLength(InitializeParameter: count) | 0 | ZeroOffset | 2 |
+| test.cpp:53:10:53:10 | Load: a | VNLength(InitializeParameter: count) | 0 | ZeroOffset | 2 |
+| test.cpp:56:10:56:10 | Load: a | VNLength(InitializeParameter: count) | 0 | ZeroOffset | 3 |
+| test.cpp:63:10:63:14 | CopyValue: & ... | VNLength(InitializeParameter: count) | 0 | OpOffset(Load: i) | 0 |
+| test.cpp:66:8:66:8 | Load: a | VNLength(InitializeParameter: count) | 0 | ZeroOffset | 0 |
+| test.cpp:68:8:68:8 | Load: a | VNLength(InitializeParameter: count) | 0 | ZeroOffset | 3 |
+| test.cpp:70:8:70:8 | Load: a | VNLength(InitializeParameter: count) | 0 | ZeroOffset | 2 |
+| test.cpp:72:8:72:8 | Load: a | VNLength(InitializeParameter: count) | 0 | ZeroOffset | 0 |
+| test.cpp:74:8:74:8 | Load: a | VNLength(InitializeParameter: count) | 0 | ZeroOffset | -10 |
+| test.cpp:76:8:76:8 | Load: a | VNLength(InitializeParameter: count) | 1 | ZeroOffset | 0 |
+| test.cpp:78:8:78:8 | Load: a | VNLength(InitializeParameter: count) | 1 | OpOffset(Load: count) | 0 |
+| test.cpp:80:8:80:8 | Load: a | VNLength(InitializeParameter: count) | 1 | OpOffset(Load: count) | 1 |
+| test.cpp:85:8:85:8 | Load: a | VNLength(InitializeParameter: count) | 1 | OpOffset(Add: ... + ...) | 0 |
+| test.cpp:87:8:87:8 | Load: a | VNLength(InitializeParameter: count) | 1 | OpOffset(Add: ... + ...) | 1 |
diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/arraylengthanalysis/ArrayLengthAnalysisTest.ql b/cpp/ql/test/experimental/library-tests/rangeanalysis/arraylengthanalysis/ArrayLengthAnalysisTest.ql
new file mode 100644
index 00000000000..020c91cd2d5
--- /dev/null
+++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/arraylengthanalysis/ArrayLengthAnalysisTest.ql
@@ -0,0 +1,8 @@
+import cpp
+import experimental.semmle.code.cpp.rangeanalysis.ArrayLengthAnalysis
+
+from Instruction array, Length length, int delta, Offset offset, int offsetDelta
+where
+ knownArrayLength(array, length, delta, offset, offsetDelta) and
+ array.getAUse() instanceof ArgumentOperand
+select array, length, delta, offset, offsetDelta
diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/arraylengthanalysis/test.cpp b/cpp/ql/test/experimental/library-tests/rangeanalysis/arraylengthanalysis/test.cpp
new file mode 100644
index 00000000000..b1d9b9a45ba
--- /dev/null
+++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/arraylengthanalysis/test.cpp
@@ -0,0 +1,95 @@
+void *malloc(unsigned long);
+void sink(...);
+
+typedef struct A {
+ int a;
+ int b;
+ char * c;
+} A;
+
+void test1(unsigned int count) {
+ if (count < 1) {
+ return;
+ }
+ A* aptr = (A *) malloc(sizeof(A) * count);
+ sink(aptr); // (count, 0, Zero, 0)
+ unsigned int* ptr = &count;
+ sink(ptr); // (Zero, 1, Zero, 0) TODO none, as the feature is not implemented
+ int* a = (int *) malloc(sizeof(int) * count);
+ sink(a); // (count, 0, Zero, 0)
+ a = (int *) malloc(sizeof(int) * (count - 1));
+ sink(a); // (count, -1, Zero, 0)
+ a = (int *) malloc(sizeof(int) * (count + 1));
+ sink(a); // (count, 1, Zero, 0)
+ a = (int *) malloc(sizeof(int) * (2 * count));
+ sink(a); // none, as the size expression is too complicated
+ char* c = (char *)malloc(count);
+ sink(c); // /count, 0, Zero, 0)
+ sink((unsigned char*)c); // (count, 0, Zero, 0)
+ void* v = c;
+ sink(v); // (count, 0, Zero, 0)
+ v = malloc(count);
+ sink((char *)v); // none, as we don't track void* allocations
+ int stack[100];
+ sink(stack); // (Zero, 100, Zero, 0)
+ for(unsigned int i = 0; i < count; ++i) {
+ int* b = (int*) malloc(sizeof(int) * count);
+ sink(b); // (count, 0, Zero, 0)
+ }
+}
+
+void test2(unsigned int count, bool b) {
+ int* a = (int *) malloc(sizeof(int) * count);
+ a = a + 2;
+ sink(a); // (count, 0, Zero, 2)
+ for(unsigned int i = 2; i < count; ++i) {
+ sink(a); // none
+ a++;
+ sink(a); // none
+ }
+ a = (int*) malloc(sizeof(int) * count);
+ if (b) {
+ a += 2;
+ sink(a); // (count, 0, Zero, 2)
+ } else {
+ a += 3;
+ sink(a); // (count, 0, Zero, 2)
+ }
+ sink(a); // none
+ a -= 2;
+ sink(a); // none
+ a = (int*) malloc(sizeof(int) * count);
+ for(unsigned int i = 0; i < count; i++) {
+ sink(&a[i]); // (count, 0, i, 0)
+ }
+ a = (int*) malloc(sizeof(int) * count);
+ sink(a); // (count, 0, Zero, 0)
+ a += 3;
+ sink(a); // (count, 0, Zero, 3)
+ a -= 1;
+ sink(a); // (count, 0, Zero, 2)
+ a -= 2;
+ sink(a); // (count, 0, Zero, 0)
+ a -= 10;
+ sink(a); // (count, 0, Zero, -10)
+ a = (int*) malloc(sizeof(int) * (count + 1));
+ sink(a); // (count, 1, Zero, 0)
+ a += count;
+ sink(a); // (count, 1, count, 0);
+ a += 1;
+ sink(a); // (count, 1, count, 1);
+ a -= count;
+ sink(a); // none
+ a = (int*) malloc(sizeof(int) * (count + 1));
+ a += count + 1;
+ sink(a); // TODO, should be (count, 1, count, 1), but is (count, 1, count + 1, 0)
+ a += 1;
+ sink(a); // TODO, should be (count, 1, count, 2), but is (count, 1, count + 1, 1)
+ a = (int*) malloc(sizeof(int) * (1024 - count));
+ sink(a); // none, as the size expression is too complicated
+}
+
+void test3(unsigned int object) {
+ unsigned int* ptr = &object;
+ sink(ptr); // TODO, none, but should be (Zero, 1, Zero, 0)
+}
diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/inboundsptr/InBounds.expected b/cpp/ql/test/experimental/library-tests/rangeanalysis/inboundsptr/InBounds.expected
new file mode 100644
index 00000000000..dc772630430
--- /dev/null
+++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/inboundsptr/InBounds.expected
@@ -0,0 +1,12 @@
+| test.cpp:14:18:14:18 | FieldAddress: a |
+| test.cpp:15:18:15:18 | FieldAddress: b |
+| test.cpp:26:5:26:12 | Store: ... = ... |
+| test.cpp:27:13:27:16 | Load: access to array |
+| test.cpp:33:5:33:12 | Store: ... = ... |
+| test.cpp:48:5:48:16 | Store: ... = ... |
+| test.cpp:61:7:61:14 | Store: ... = ... |
+| test.cpp:70:7:70:14 | Store: ... = ... |
+| test.cpp:81:11:81:14 | Load: access to array |
+| test.cpp:85:5:85:12 | Store: ... = ... |
+| test.cpp:87:3:87:11 | Store: ... = ... |
+| test.cpp:91:3:91:18 | Store: ... = ... |
diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/inboundsptr/InBounds.ql b/cpp/ql/test/experimental/library-tests/rangeanalysis/inboundsptr/InBounds.ql
new file mode 100644
index 00000000000..7e5c09ed5d1
--- /dev/null
+++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/inboundsptr/InBounds.ql
@@ -0,0 +1,6 @@
+import cpp
+import experimental.semmle.code.cpp.rangeanalysis.InBoundsPointerDeref
+
+from PointerDereferenceInstruction ptrAccess
+where inBounds(ptrAccess)
+select ptrAccess
diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/inboundsptr/test.cpp b/cpp/ql/test/experimental/library-tests/rangeanalysis/inboundsptr/test.cpp
new file mode 100644
index 00000000000..b4368da115c
--- /dev/null
+++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/inboundsptr/test.cpp
@@ -0,0 +1,126 @@
+void *malloc(unsigned long);
+
+typedef struct A {
+ int a;
+ int b;
+ char * c;
+} A;
+
+void test1(unsigned int count) {
+ if (count < 1) {
+ return;
+ }
+ A* ptr = (A*) malloc(sizeof(A) * count);
+ ptr[count - 1].a = 1000; // in-bounds
+ ptr[count - 1].b = 1001; // in-bounds
+ ptr[1].c = 0; // unknown
+ ptr[count - 2].a = 1002; // dependant on call-site
+ ptr[count].b = 1003; // out-of-bounds
+ ptr[-1].c = 0; // out-of-bounds
+}
+
+void test2(unsigned int count) {
+ int* a = (int*) malloc(sizeof(int) * count);
+
+ for(unsigned int i = 0; i < count; ++i) {
+ a[i] = 0; // in-bounds
+ int l = a[i]; // in-bounds
+ }
+
+ a = (int*) malloc(sizeof(int) * count);
+ a = a + 2;
+ for(unsigned int i = 0; i < count - 2; ++i) {
+ a[i] = 0; // in-bounds
+ }
+
+ for(unsigned int i = 0; i < count; ++i) {
+ *a = 1; // in-bounds but not detected, array length tracking is not advanced enough for this
+ a++;
+ }
+
+ void* v = malloc(count);
+ for(unsigned int i = 0; i < count; ++i) {
+ ((char *)v)[i] = 0; // in-bounds, but due to void-allocation not detected
+ }
+
+ int stack[100];
+ for(unsigned int i = 0; i < 100; ++i) {
+ stack[i] = 0; // in-bounds
+ }
+
+ for(unsigned int i = 0; i < count; ++i) {
+ a = (int*) malloc(sizeof(int) * count);
+ for (int j = 0; j < count; ++j) {
+ a[j] = 0; // in-bounds, but not detected due to RangeAnalysis shortcomings
+ }
+ }
+
+ for(unsigned int i = 0; i < 10; ++i) {
+ a = (int*) malloc(sizeof(int) * i);
+ for (unsigned int j = 0; j < i; ++j) {
+ a[j] = 0; // in-bounds
+ }
+ }
+
+}
+void test3(int count) {
+ for(int i = 0; i < count; ++i) {
+ int * a = (int*) malloc(sizeof(int) * i);
+ for (int j = 0; j < i; ++j) {
+ a[j] = 0; // in-bounds
+ }
+ }
+}
+
+
+void test4(unsigned long count) {
+ if (count < 1) {
+ return;
+ }
+ int* a = (int*) malloc(sizeof(int) * count);
+ int b = a[0] + 3; // in-bounds
+ a = a + 2;
+ unsigned int i = 0;
+ for(; i < count - 2; ++i) {
+ a[i] = 0; // in-bounds
+ }
+ a[-2] = 0; // in-bounds
+ a[-3] = 0; // out-of-bounds
+ a[i] = 0; // out-of-bounds
+ a[count - 2] = 0; // out-of-bounds
+ a[count - 3] = 0; // in-bounds
+}
+
+void test5(unsigned int count) {
+ int* a = (int*) malloc(sizeof(int) * count);
+ a[0] = 0; // unknown, call-site dependant
+}
+
+
+void test6(unsigned int count, bool b) {
+ if(count < 4) {
+ return;
+ }
+ int* a = (int*) malloc(sizeof(int) * count);
+ if (b) {
+ a += 2;
+ } else {
+ a += 3;
+ } // we lose all information about a after the phi-node here
+ a[-2] = 0; // unknown
+ a[-3] = 0; // unknown
+ a[-4] = 0; // unknown
+ a[0] = 0; // unknown
+}
+
+void test7(unsigned int object) {
+ unsigned int* ptr = &object;
+ *ptr = 0; // in-bounds, but needs ArrayLengthAnalysis improvements.
+}
+
+void test8() {
+ void (*foo)(unsigned int);
+ foo = &test7;
+ foo(4); // in-bounds, but needs ArrayLengthAnalysis improvements.
+}
+
diff --git a/cpp/ql/test/header-variant-tests/deduplication/classes.ql b/cpp/ql/test/header-variant-tests/deduplication/classes.ql
index 2fe0098c9cf..ed44ab124dc 100644
--- a/cpp/ql/test/header-variant-tests/deduplication/classes.ql
+++ b/cpp/ql/test/header-variant-tests/deduplication/classes.ql
@@ -1,4 +1,4 @@
-import default
+import cpp
from Class c, string n
where n = count(Class x | x.getName() = c.getName()) + " distinct class(es) called " + c.getName()
diff --git a/cpp/ql/test/library-tests/allocators/allocators.cpp b/cpp/ql/test/library-tests/allocators/allocators.cpp
index 92241b9c9e3..3e4a6cec8df 100644
--- a/cpp/ql/test/library-tests/allocators/allocators.cpp
+++ b/cpp/ql/test/library-tests/allocators/allocators.cpp
@@ -149,3 +149,16 @@ void directOperatorCall() {
ptr = operator new(sizeof(int));
operator delete(ptr);
}
+
+void *malloc(size_t);
+typedef int* ptr_int;
+
+void testMalloc(size_t count) {
+ const volatile int *i = (const volatile int *) malloc(5);
+ ptr_int i2 = (ptr_int) malloc(5 * sizeof(int));
+ volatile long *l = (long *) malloc(count);
+ l = (long *) malloc(count * sizeof(int));
+ const char* c = (const char *) malloc(count * sizeof(int) + 1);
+ void * v = (void *) malloc(((int) count) * sizeof(void *));
+ malloc(sizeof(void *) * sizeof(int));
+}
diff --git a/cpp/ql/test/library-tests/allocators/allocators.expected b/cpp/ql/test/library-tests/allocators/allocators.expected
index 2f9b4413e8c..5fdf7e50b90 100644
--- a/cpp/ql/test/library-tests/allocators/allocators.expected
+++ b/cpp/ql/test/library-tests/allocators/allocators.expected
@@ -55,35 +55,43 @@ allocationFunctions
| allocators.cpp:122:7:122:20 | operator new[] | getPlacementArgument = 1, getSizeArg = 0 |
| allocators.cpp:123:7:123:18 | operator new | getSizeArg = 0, requiresDealloc |
| allocators.cpp:124:7:124:20 | operator new[] | getSizeArg = 0, requiresDealloc |
+| allocators.cpp:153:7:153:12 | malloc | getSizeArg = 0, requiresDealloc |
| file://:0:0:0:0 | operator new | getSizeArg = 0, requiresDealloc |
| file://:0:0:0:0 | operator new | getSizeArg = 0, requiresDealloc |
| file://:0:0:0:0 | operator new[] | getSizeArg = 0, requiresDealloc |
| file://:0:0:0:0 | operator new[] | getSizeArg = 0, requiresDealloc |
allocationExprs
-| allocators.cpp:49:3:49:9 | new | getSizeBytes = 4, requiresDealloc |
-| allocators.cpp:50:3:50:15 | new | getSizeBytes = 4, requiresDealloc |
-| allocators.cpp:51:3:51:11 | new | getSizeBytes = 4, requiresDealloc |
-| allocators.cpp:52:3:52:14 | new | getSizeBytes = 8, requiresDealloc |
-| allocators.cpp:53:3:53:27 | new | getSizeBytes = 8, requiresDealloc |
-| allocators.cpp:54:3:54:17 | new | getSizeBytes = 256, requiresDealloc |
-| allocators.cpp:55:3:55:25 | new | getSizeBytes = 256, requiresDealloc |
-| allocators.cpp:68:3:68:12 | new[] | getSizeExpr = n, getSizeMult = 4, requiresDealloc |
-| allocators.cpp:69:3:69:18 | new[] | getSizeExpr = n, getSizeMult = 4, requiresDealloc |
-| allocators.cpp:70:3:70:15 | new[] | getSizeExpr = n, getSizeMult = 8, requiresDealloc |
-| allocators.cpp:71:3:71:20 | new[] | getSizeExpr = n, getSizeMult = 256, requiresDealloc |
-| allocators.cpp:72:3:72:16 | new[] | getSizeBytes = 80, requiresDealloc |
-| allocators.cpp:107:3:107:18 | new | getSizeBytes = 1, requiresDealloc |
-| allocators.cpp:108:3:108:19 | new[] | getSizeExpr = n, getSizeMult = 1, requiresDealloc |
-| allocators.cpp:109:3:109:35 | new | getSizeBytes = 128, requiresDealloc |
-| allocators.cpp:110:3:110:37 | new[] | getSizeBytes = 1280, requiresDealloc |
-| allocators.cpp:129:3:129:21 | new | getSizeBytes = 4 |
-| allocators.cpp:132:3:132:17 | new[] | getSizeBytes = 4 |
-| allocators.cpp:135:3:135:26 | new | getSizeBytes = 4, requiresDealloc |
-| allocators.cpp:136:3:136:26 | new[] | getSizeBytes = 8, requiresDealloc |
-| allocators.cpp:142:13:142:27 | new[] | getSizeExpr = x, getSizeMult = 10, requiresDealloc |
-| allocators.cpp:143:13:143:28 | new[] | getSizeBytes = 400, requiresDealloc |
-| allocators.cpp:144:13:144:31 | new[] | getSizeExpr = x, getSizeMult = 900, requiresDealloc |
+| allocators.cpp:49:3:49:9 | new | getAllocatedElementType = int, getSizeBytes = 4, requiresDealloc |
+| allocators.cpp:50:3:50:15 | new | getAllocatedElementType = int, getSizeBytes = 4, requiresDealloc |
+| allocators.cpp:51:3:51:11 | new | getAllocatedElementType = int, getSizeBytes = 4, requiresDealloc |
+| allocators.cpp:52:3:52:14 | new | getAllocatedElementType = String, getSizeBytes = 8, requiresDealloc |
+| allocators.cpp:53:3:53:27 | new | getAllocatedElementType = String, getSizeBytes = 8, requiresDealloc |
+| allocators.cpp:54:3:54:17 | new | getAllocatedElementType = Overaligned, getSizeBytes = 256, requiresDealloc |
+| allocators.cpp:55:3:55:25 | new | getAllocatedElementType = Overaligned, getSizeBytes = 256, requiresDealloc |
+| allocators.cpp:68:3:68:12 | new[] | getAllocatedElementType = int, getSizeExpr = n, getSizeMult = 4, requiresDealloc |
+| allocators.cpp:69:3:69:18 | new[] | getAllocatedElementType = int, getSizeExpr = n, getSizeMult = 4, requiresDealloc |
+| allocators.cpp:70:3:70:15 | new[] | getAllocatedElementType = String, getSizeExpr = n, getSizeMult = 8, requiresDealloc |
+| allocators.cpp:71:3:71:20 | new[] | getAllocatedElementType = Overaligned, getSizeExpr = n, getSizeMult = 256, requiresDealloc |
+| allocators.cpp:72:3:72:16 | new[] | getAllocatedElementType = String, getSizeBytes = 80, requiresDealloc |
+| allocators.cpp:107:3:107:18 | new | getAllocatedElementType = FailedInit, getSizeBytes = 1, requiresDealloc |
+| allocators.cpp:108:3:108:19 | new[] | getAllocatedElementType = FailedInit, getSizeExpr = n, getSizeMult = 1, requiresDealloc |
+| allocators.cpp:109:3:109:35 | new | getAllocatedElementType = FailedInitOveraligned, getSizeBytes = 128, requiresDealloc |
+| allocators.cpp:110:3:110:37 | new[] | getAllocatedElementType = FailedInitOveraligned, getSizeBytes = 1280, requiresDealloc |
+| allocators.cpp:129:3:129:21 | new | getAllocatedElementType = int, getSizeBytes = 4 |
+| allocators.cpp:132:3:132:17 | new[] | getAllocatedElementType = int, getSizeBytes = 4 |
+| allocators.cpp:135:3:135:26 | new | getAllocatedElementType = int, getSizeBytes = 4, requiresDealloc |
+| allocators.cpp:136:3:136:26 | new[] | getAllocatedElementType = int, getSizeBytes = 8, requiresDealloc |
+| allocators.cpp:142:13:142:27 | new[] | getAllocatedElementType = char[10], getSizeExpr = x, getSizeMult = 10, requiresDealloc |
+| allocators.cpp:143:13:143:28 | new[] | getAllocatedElementType = char[20], getSizeBytes = 400, requiresDealloc |
+| allocators.cpp:144:13:144:31 | new[] | getAllocatedElementType = char[30][30], getSizeExpr = x, getSizeMult = 900, requiresDealloc |
| allocators.cpp:149:8:149:19 | call to operator new | getSizeBytes = 4, getSizeExpr = sizeof(int), getSizeMult = 1, requiresDealloc |
+| allocators.cpp:157:50:157:55 | call to malloc | getAllocatedElementType = const volatile int, getSizeBytes = 5, getSizeExpr = 5, getSizeMult = 1, requiresDealloc |
+| allocators.cpp:158:26:158:31 | call to malloc | getAllocatedElementType = int, getSizeBytes = 20, getSizeExpr = 5, getSizeMult = 4, requiresDealloc |
+| allocators.cpp:159:31:159:36 | call to malloc | getAllocatedElementType = volatile long, getSizeExpr = count, getSizeMult = 1, requiresDealloc |
+| allocators.cpp:160:16:160:21 | call to malloc | getAllocatedElementType = volatile long, getSizeExpr = count, getSizeMult = 4, requiresDealloc |
+| allocators.cpp:161:34:161:39 | call to malloc | getAllocatedElementType = const char, getSizeExpr = ... + ..., getSizeMult = 1, requiresDealloc |
+| allocators.cpp:162:23:162:28 | call to malloc | getSizeExpr = count, getSizeMult = 8, requiresDealloc |
+| allocators.cpp:163:3:163:8 | call to malloc | getSizeBytes = 32, getSizeExpr = ... * ..., getSizeMult = 1, requiresDealloc |
deallocationFunctions
| allocators.cpp:11:6:11:20 | operator delete | getFreedArg = 0 |
| allocators.cpp:12:6:12:22 | operator delete[] | getFreedArg = 0 |
diff --git a/cpp/ql/test/library-tests/allocators/allocators.ql b/cpp/ql/test/library-tests/allocators/allocators.ql
index e45667fbf01..a2126cdfbce 100644
--- a/cpp/ql/test/library-tests/allocators/allocators.ql
+++ b/cpp/ql/test/library-tests/allocators/allocators.ql
@@ -1,4 +1,4 @@
-import default
+import cpp
import semmle.code.cpp.models.implementations.Allocation
query predicate newExprs(
@@ -138,6 +138,8 @@ string describeAllocationExpr(AllocationExpr e) {
or
result = "getReallocPtr = " + e.getReallocPtr().toString()
or
+ result = "getAllocatedElementType = " + e.getAllocatedElementType().toString()
+ or
e.requiresDealloc() and
result = "requiresDealloc"
}
diff --git a/cpp/ql/test/library-tests/attributes/field_attributes/field_attributes.cpp b/cpp/ql/test/library-tests/attributes/field_attributes/field_attributes.cpp
new file mode 100644
index 00000000000..d9e94faf5f0
--- /dev/null
+++ b/cpp/ql/test/library-tests/attributes/field_attributes/field_attributes.cpp
@@ -0,0 +1,9 @@
+struct S1 {
+ [[deprecated]] int a;
+ int b;
+};
+
+struct S2 {
+ int x;
+ [[deprecated, gnu::unused]] int b;
+};
diff --git a/cpp/ql/test/library-tests/attributes/field_attributes/field_attributes.expected b/cpp/ql/test/library-tests/attributes/field_attributes/field_attributes.expected
new file mode 100644
index 00000000000..1a8add6d745
--- /dev/null
+++ b/cpp/ql/test/library-tests/attributes/field_attributes/field_attributes.expected
@@ -0,0 +1,3 @@
+| field_attributes.cpp:2:22:2:22 | a | field_attributes.cpp:2:5:2:14 | deprecated |
+| field_attributes.cpp:8:35:8:35 | b | field_attributes.cpp:8:5:8:14 | deprecated |
+| field_attributes.cpp:8:35:8:35 | b | field_attributes.cpp:8:17:8:27 | unused |
diff --git a/cpp/ql/test/library-tests/attributes/field_attributes/field_attributes.ql b/cpp/ql/test/library-tests/attributes/field_attributes/field_attributes.ql
new file mode 100644
index 00000000000..e1814bb6b87
--- /dev/null
+++ b/cpp/ql/test/library-tests/attributes/field_attributes/field_attributes.ql
@@ -0,0 +1,3 @@
+import cpp
+
+select any(Variable v) as v, v.getAnAttribute()
diff --git a/cpp/ql/test/library-tests/attributes/var_attributes/var_attributes.expected b/cpp/ql/test/library-tests/attributes/var_attributes/var_attributes.expected
index 2ef69dc6c44..501d699758a 100644
--- a/cpp/ql/test/library-tests/attributes/var_attributes/var_attributes.expected
+++ b/cpp/ql/test/library-tests/attributes/var_attributes/var_attributes.expected
@@ -3,6 +3,7 @@
| ms_var_attributes.cpp:7:5:7:10 | myInt2 | ms_var_attributes.h:4:1:4:9 | dllexport |
| ms_var_attributes.cpp:8:15:8:20 | myInt4 | ms_var_attributes.cpp:8:1:8:9 | dllexport |
| ms_var_attributes.cpp:9:5:9:10 | myInt5 | ms_var_attributes.h:7:1:7:9 | dllexport |
+| ms_var_attributes.cpp:12:42:12:46 | field | ms_var_attributes.cpp:12:14:12:21 | property |
| ms_var_attributes.h:5:22:5:27 | myInt3 | ms_var_attributes.h:5:1:5:9 | dllexport |
| var_attributes.c:1:12:1:19 | weak_var | var_attributes.c:1:36:1:39 | weak |
| var_attributes.c:2:12:2:22 | weakref_var | var_attributes.c:2:39:2:45 | weakref |
diff --git a/cpp/ql/test/library-tests/blocks/cpp/exprs.expected b/cpp/ql/test/library-tests/blocks/cpp/exprs.expected
index f81990f2c4b..5771a100263 100644
--- a/cpp/ql/test/library-tests/blocks/cpp/exprs.expected
+++ b/cpp/ql/test/library-tests/blocks/cpp/exprs.expected
@@ -79,4 +79,4 @@
| blocks.cpp:57:12:57:32 | call to expression |
| blocks.cpp:57:14:57:30 | ^ { ... } |
| blocks.cpp:57:23:57:26 | four |
-| file://:0:0:0:0 | this |
+| blocks.cpp:57:23:57:26 | this |
diff --git a/cpp/ql/test/library-tests/builtins/edg/expr.expected b/cpp/ql/test/library-tests/builtins/edg/expr.expected
index 7b4a51f022b..0969dc1e217 100644
--- a/cpp/ql/test/library-tests/builtins/edg/expr.expected
+++ b/cpp/ql/test/library-tests/builtins/edg/expr.expected
@@ -1,5 +1,8 @@
| edg.c:12:14:12:51 | (int)... | 0 | 0 |
| edg.c:12:14:12:51 | __builtin_offsetof | 1 | 1 |
+| edg.c:12:14:12:51 | mystruct | 0 | 0 |
+| edg.c:12:49:12:50 | 0 | 0 | 0 |
+| edg.c:12:49:12:50 | * ... | 0 | 0 |
| 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 |
@@ -10,6 +13,3 @@
| edg.c:13:14:13:45 | (size_t)... | 0 | 0 |
| edg.c:13:14:13:45 | __INTADDR__ | 1 | 1 |
| edg.c:13:43:13:44 | f2 | 0 | 0 |
-| file://:0:0:0:0 | 0 | 0 | 0 |
-| file://:0:0:0:0 | * ... | 0 | 0 |
-| file://:0:0:0:0 | mystruct | 0 | 0 |
diff --git a/cpp/ql/test/library-tests/builtins/type_traits/expr.expected b/cpp/ql/test/library-tests/builtins/type_traits/expr.expected
index 6166de5a80d..47918496198 100644
--- a/cpp/ql/test/library-tests/builtins/type_traits/expr.expected
+++ b/cpp/ql/test/library-tests/builtins/type_traits/expr.expected
@@ -1,298 +1,298 @@
| file://:0:0:0:0 | 0 | | 0 |
| file://:0:0:0:0 | 1 | | 1 |
| file://:0:0:0:0 | 2 | | 2 |
-| file://:0:0:0:0 | C1 | | |
-| file://:0:0:0:0 | C1 | | |
-| file://:0:0:0:0 | C1 | | |
-| file://:0:0:0:0 | C1 | | |
-| file://:0:0:0:0 | C1 | | |
-| file://:0:0:0:0 | C1 | | |
-| file://:0:0:0:0 | C1 | | |
-| file://:0:0:0:0 | C1 | | |
-| file://:0:0:0:0 | C1 | | |
-| file://:0:0:0:0 | C1 | | |
-| file://:0:0:0:0 | C1 | | |
-| file://:0:0:0:0 | C1 | | |
-| file://:0:0:0:0 | C2 | | |
-| file://:0:0:0:0 | C2 | | |
-| file://:0:0:0:0 | C2 | | |
-| file://:0:0:0:0 | C2 | | |
-| file://:0:0:0:0 | C2 | | |
-| file://:0:0:0:0 | C2 | | |
-| file://:0:0:0:0 | C3 | | |
-| file://:0:0:0:0 | C3 | | |
-| file://:0:0:0:0 | C3 | | |
-| file://:0:0:0:0 | C3 | | |
-| file://:0:0:0:0 | C3 | | |
-| file://:0:0:0:0 | C3 | | |
-| file://:0:0:0:0 | C4 | | |
-| file://:0:0:0:0 | C4 | | |
-| file://:0:0:0:0 | C5 | | |
-| file://:0:0:0:0 | C5 | | |
-| file://:0:0:0:0 | a_final_struct | | |
-| file://:0:0:0:0 | a_struct | | |
-| file://:0:0:0:0 | a_struct | | |
-| file://:0:0:0:0 | a_struct | | |
-| file://:0:0:0:0 | a_struct | | |
-| file://:0:0:0:0 | a_struct | | |
-| file://:0:0:0:0 | a_struct | | |
-| file://:0:0:0:0 | a_struct | | |
-| file://:0:0:0:0 | a_struct | | |
-| file://:0:0:0:0 | a_struct | | |
-| file://:0:0:0:0 | a_struct | | |
-| file://:0:0:0:0 | a_struct | | |
-| file://:0:0:0:0 | a_struct | | |
-| file://:0:0:0:0 | a_struct | | |
-| file://:0:0:0:0 | a_struct | | |
-| file://:0:0:0:0 | a_struct | | |
-| file://:0:0:0:0 | a_struct | | |
-| file://:0:0:0:0 | a_struct_plus | | |
-| file://:0:0:0:0 | a_union | | |
-| file://:0:0:0:0 | a_union | | |
-| file://:0:0:0:0 | a_union | | |
-| file://:0:0:0:0 | abstract | | |
-| file://:0:0:0:0 | abstract | | |
-| file://:0:0:0:0 | abstract | | |
-| file://:0:0:0:0 | an_enum | | |
-| file://:0:0:0:0 | an_enum | | |
-| file://:0:0:0:0 | an_enum | | |
-| file://:0:0:0:0 | data | | |
-| file://:0:0:0:0 | double | | |
-| file://:0:0:0:0 | double | | |
-| file://:0:0:0:0 | double | | |
-| file://:0:0:0:0 | double | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | empty | | |
-| file://:0:0:0:0 | float | | |
-| file://:0:0:0:0 | float | | |
-| file://:0:0:0:0 | float | | |
-| file://:0:0:0:0 | float | | |
-| file://:0:0:0:0 | float | | |
-| file://:0:0:0:0 | float | | |
-| file://:0:0:0:0 | has_assign | | |
-| file://:0:0:0:0 | has_assign | | |
-| file://:0:0:0:0 | has_assign | | |
-| file://:0:0:0:0 | has_assign | | |
-| file://:0:0:0:0 | has_assign | | |
-| file://:0:0:0:0 | has_copy | | |
-| file://:0:0:0:0 | has_copy | | |
-| file://:0:0:0:0 | has_copy | | |
-| file://:0:0:0:0 | has_copy | | |
-| file://:0:0:0:0 | has_copy | | |
-| file://:0:0:0:0 | has_copy | | |
-| file://:0:0:0:0 | has_copy | | |
-| file://:0:0:0:0 | has_copy | | |
-| file://:0:0:0:0 | has_copy | | |
-| file://:0:0:0:0 | has_copy | | |
-| file://:0:0:0:0 | has_copy | | |
-| file://:0:0:0:0 | has_noexcept_destructor | | |
-| file://:0:0:0:0 | has_noexcept_destructor | | |
-| file://:0:0:0:0 | has_nothrow_assign | | |
-| file://:0:0:0:0 | has_nothrow_assign | | |
-| file://:0:0:0:0 | has_nothrow_constructor | | |
-| file://:0:0:0:0 | has_nothrow_constructor | | |
-| file://:0:0:0:0 | has_nothrow_copy | | |
-| file://:0:0:0:0 | has_user_destructor | | |
-| file://:0:0:0:0 | has_user_destructor | | |
-| file://:0:0:0:0 | has_user_destructor | | |
-| file://:0:0:0:0 | has_user_destructor | | |
-| file://:0:0:0:0 | has_user_destructor | | |
-| file://:0:0:0:0 | has_user_destructor | | |
-| file://:0:0:0:0 | has_user_destructor | | |
-| file://:0:0:0:0 | has_virtual_destructor | | |
-| file://:0:0:0:0 | has_virtual_destructor | | |
-| file://:0:0:0:0 | int | | |
-| file://:0:0:0:0 | int | | |
-| file://:0:0:0:0 | int | | |
-| file://:0:0:0:0 | int | | |
-| file://:0:0:0:0 | int | | |
-| file://:0:0:0:0 | int | | |
-| file://:0:0:0:0 | int | | |
-| file://:0:0:0:0 | int | | |
-| file://:0:0:0:0 | int | | |
-| file://:0:0:0:0 | int | | |
-| file://:0:0:0:0 | int | | |
-| file://:0:0:0:0 | int | | |
-| file://:0:0:0:0 | int | | |
-| file://:0:0:0:0 | int | | |
-| file://:0:0:0:0 | int | | |
-| file://:0:0:0:0 | int | | |
-| file://:0:0:0:0 | int | | |
-| file://:0:0:0:0 | int | | |
-| file://:0:0:0:0 | int | | |
-| file://:0:0:0:0 | int | | |
-| file://:0:0:0:0 | int | | |
-| file://:0:0:0:0 | int | | |
-| file://:0:0:0:0 | long | | |
-| file://:0:0:0:0 | long | | |
-| file://:0:0:0:0 | long | | |
-| file://:0:0:0:0 | long | | |
-| file://:0:0:0:0 | method | | |
-| file://:0:0:0:0 | method | | |
-| file://:0:0:0:0 | method | | |
-| file://:0:0:0:0 | method_data | | |
-| file://:0:0:0:0 | no_has_nothrow_constructor | | |
-| file://:0:0:0:0 | no_has_nothrow_constructor | | |
| ms.cpp:38:41:38:45 | 0 | | 0 |
| ms.cpp:88:27:88:45 | __has_assign | empty | 0 |
+| ms.cpp:88:27:88:45 | empty | | |
| ms.cpp:89:27:89:50 | __has_assign | has_assign | 1 |
+| ms.cpp:89:27:89:50 | has_assign | | |
| ms.cpp:91:25:91:41 | __has_copy | empty | 0 |
+| ms.cpp:91:25:91:41 | empty | | |
| ms.cpp:92:25:92:44 | __has_copy | has_copy | 1 |
+| ms.cpp:92:25:92:44 | has_copy | | |
| ms.cpp:94:35:94:61 | __has_nothrow_assign | empty | 1 |
+| ms.cpp:94:35:94:61 | empty | | |
| ms.cpp:95:35:95:66 | __has_nothrow_assign | has_assign | 0 |
+| ms.cpp:95:35:95:66 | has_assign | | |
| ms.cpp:96:35:96:74 | __has_nothrow_assign | has_nothrow_assign | 1 |
+| ms.cpp:96:35:96:74 | has_nothrow_assign | | |
| ms.cpp:98:40:98:71 | __has_nothrow_constructor | empty | 1 |
+| ms.cpp:98:40:98:71 | empty | | |
| ms.cpp:99:40:99:92 | __has_nothrow_constructor | no_has_nothrow_constructor | 0 |
+| ms.cpp:99:40:99:92 | no_has_nothrow_constructor | | |
| ms.cpp:100:40:100:89 | __has_nothrow_constructor | has_nothrow_constructor | 1 |
+| ms.cpp:100:40:100:89 | has_nothrow_constructor | | |
| ms.cpp:102:33:102:57 | __has_nothrow_copy | empty | 1 |
+| ms.cpp:102:33:102:57 | empty | | |
| ms.cpp:103:33:103:60 | __has_nothrow_copy | has_copy | 0 |
+| ms.cpp:103:33:103:60 | has_copy | | |
| ms.cpp:104:33:104:68 | __has_nothrow_copy | has_nothrow_copy | 1 |
+| ms.cpp:104:33:104:68 | has_nothrow_copy | | |
| ms.cpp:106:35:106:61 | __has_trivial_assign | empty | 1 |
+| ms.cpp:106:35:106:61 | empty | | |
| ms.cpp:107:35:107:66 | __has_trivial_assign | has_assign | 0 |
+| ms.cpp:107:35:107:66 | has_assign | | |
| ms.cpp:109:40:109:71 | __has_trivial_constructor | empty | 1 |
+| ms.cpp:109:40:109:71 | empty | | |
| ms.cpp:110:40:110:92 | __has_trivial_constructor | no_has_nothrow_constructor | 0 |
+| ms.cpp:110:40:110:92 | no_has_nothrow_constructor | | |
| ms.cpp:111:40:111:89 | __has_trivial_constructor | has_nothrow_constructor | 0 |
+| ms.cpp:111:40:111:89 | has_nothrow_constructor | | |
| ms.cpp:113:33:113:57 | __has_trivial_copy | empty | 1 |
+| ms.cpp:113:33:113:57 | empty | | |
| ms.cpp:114:33:114:60 | __has_trivial_copy | has_copy | 0 |
+| ms.cpp:114:33:114:60 | has_copy | | |
| ms.cpp:116:36:116:63 | __has_user_destructor | empty | 0 |
+| ms.cpp:116:36:116:63 | empty | | |
| ms.cpp:117:36:117:77 | __has_user_destructor | has_user_destructor | 1 |
+| ms.cpp:117:36:117:77 | has_user_destructor | | |
| ms.cpp:118:36:118:80 | __has_user_destructor | has_virtual_destructor | 1 |
+| ms.cpp:118:36:118:80 | has_virtual_destructor | | |
| ms.cpp:120:39:120:69 | __has_virtual_destructor | empty | 0 |
+| ms.cpp:120:39:120:69 | empty | | |
| ms.cpp:121:39:121:83 | __has_virtual_destructor | has_user_destructor | 0 |
+| ms.cpp:121:39:121:83 | has_user_destructor | | |
| ms.cpp:122:39:122:86 | __has_virtual_destructor | has_virtual_destructor | 1 |
+| ms.cpp:122:39:122:86 | has_virtual_destructor | | |
| ms.cpp:124:28:124:47 | __is_abstract | empty | 0 |
+| ms.cpp:124:28:124:47 | empty | | |
| ms.cpp:125:28:125:50 | __is_abstract | abstract | 1 |
+| ms.cpp:125:28:125:50 | abstract | | |
| ms.cpp:126:28:126:48 | __is_abstract | method | 0 |
+| ms.cpp:126:28:126:48 | method | | |
+| ms.cpp:128:27:128:45 | C1 | | |
+| ms.cpp:128:27:128:45 | C1 | | |
| ms.cpp:128:27:128:45 | __is_base_of | C1,C1 | 1 |
+| ms.cpp:129:27:129:45 | C1 | | |
+| ms.cpp:129:27:129:45 | C2 | | |
| ms.cpp:129:27:129:45 | __is_base_of | C1,C2 | 1 |
+| ms.cpp:130:27:130:45 | C1 | | |
+| ms.cpp:130:27:130:45 | C3 | | |
| ms.cpp:130:27:130:45 | __is_base_of | C1,C3 | 1 |
+| ms.cpp:131:27:131:45 | C1 | | |
+| ms.cpp:131:27:131:45 | C5 | | |
| ms.cpp:131:27:131:45 | __is_base_of | C1,C5 | 0 |
+| ms.cpp:132:27:132:45 | C2 | | |
+| ms.cpp:132:27:132:45 | C3 | | |
| ms.cpp:132:27:132:45 | __is_base_of | C3,C2 | 0 |
+| ms.cpp:133:27:133:45 | C1 | | |
+| ms.cpp:133:27:133:45 | C3 | | |
| ms.cpp:133:27:133:45 | __is_base_of | C3,C1 | 0 |
+| ms.cpp:134:27:134:45 | C2 | | |
+| ms.cpp:134:27:134:45 | C4 | | |
| ms.cpp:134:27:134:45 | __is_base_of | C2,C4 | 0 |
| ms.cpp:135:27:135:47 | __is_base_of | int,int | 0 |
+| ms.cpp:135:27:135:47 | int | | |
+| ms.cpp:135:27:135:47 | int | | |
| ms.cpp:136:27:136:48 | __is_base_of | int,long | 0 |
+| ms.cpp:136:27:136:48 | int | | |
+| ms.cpp:136:27:136:48 | long | | |
| ms.cpp:137:28:137:49 | __is_base_of | long,int | 0 |
+| ms.cpp:137:28:137:49 | int | | |
+| ms.cpp:137:28:137:49 | long | | |
| ms.cpp:138:28:138:51 | __is_base_of | int,double | 0 |
+| ms.cpp:138:28:138:51 | double | | |
+| ms.cpp:138:28:138:51 | int | | |
| ms.cpp:139:28:139:51 | __is_base_of | double,int | 0 |
+| ms.cpp:139:28:139:51 | double | | |
+| ms.cpp:139:28:139:51 | int | | |
| ms.cpp:141:25:141:41 | __is_class | empty | 1 |
+| ms.cpp:141:25:141:41 | empty | | |
| ms.cpp:142:25:142:43 | __is_class | an_enum | 0 |
+| ms.cpp:142:25:142:43 | an_enum | | |
| ms.cpp:143:25:143:43 | __is_class | a_union | 0 |
+| ms.cpp:143:25:143:43 | a_union | | |
| ms.cpp:144:25:144:44 | __is_class | a_struct | 1 |
+| ms.cpp:144:25:144:44 | a_struct | | |
| ms.cpp:145:25:145:39 | __is_class | int | 0 |
+| ms.cpp:145:25:145:39 | int | |