diff --git a/.gitignore b/.gitignore
index d020f307ebc..b14dab0a6b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,5 +19,5 @@
# It's useful (though not required) to be able to unpack codeql in the ql checkout itself
/codeql/
-.vscode/settings.json
+
csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json
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 af8575bc316..6b6cffedcaf 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,6 +1,6 @@
# Contributing to CodeQL
-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!
+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).
@@ -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.
@@ -36,7 +36,7 @@ If you have an idea for a query that you would like to share with other CodeQL u
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**
@@ -47,10 +47,6 @@ If you have an idea for a query that you would like to share with other CodeQL u
- The query must have at least one true positive result on some revision of a real project.
-6. **Contributor License Agreement**
-
- - The contributor can satisfy the [CLA](#contributor-license-agreement).
-
Experimental queries and libraries may not be actively maintained as the [supported](docs/supported-queries.md) libraries evolve. They may also be changed in backwards-incompatible ways or may be removed entirely in the future without deprecation warnings.
After the experimental query is merged, we welcome pull requests to improve it. Before a query can be moved out of the `experimental` subdirectory, it must satisfy [the requirements for being a supported query](docs/supported-queries.md).
@@ -65,33 +61,6 @@ 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@semmle.com) if you have any questions about
+Please do get in touch (privacy@github.com) if you have any questions about
this or our data protection policies.
-## Contributor License Agreement
-
-This Contributor License Agreement (“Agreement”) is entered into between Semmle Limited (“Semmle,” “we” or “us” etc.), and You (as defined and further identified below).
-
-Accordingly, You hereby agree to the following terms for Your present and future Contributions submitted to Semmle:
-
-1. **Definitions**.
-
- * "You" (or "Your") shall mean the Contribution copyright owner (whether an individual or organization) or legal entity authorized by the copyright owner that is making this Agreement with Semmle. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
-
- * "Contribution(s)" shall mean the code, documentation or other original works of authorship, including any modifications or additions to an existing work, submitted by You to Semmle for inclusion in, or documentation of, any of the products or projects owned or managed by Semmle (the "Work(s)"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to Semmle or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Semmle for the purpose of discussing and/or improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
-
-2. **Grant of Copyright License**. You hereby grant to Semmle and to recipients of software distributed by Semmle a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works.
-
-3. **Grant of Patent License**. You hereby grant to Semmle and to recipients of software distributed by Semmle a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that Your Contribution, or the Work to which You have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.
-
-4. **Ownership**. Except as set out above, You keep all right, title, and interest in Your Contribution. The rights that You grant to us under this Agreement are effective on the date You first submitted a Contribution to us, even if Your submission took place before the date You entered this Agreement.
-
-5. **Representations**. You represent and warrant that: (i) the Contributions are an original work and that You can legally grant the rights set out in this Agreement; (ii) the Contributions and Semmle’s exercise of any license rights granted hereunder, does not and will not, infringe the rights of any third party; (iii) You are not aware of any pending or threatened claims, suits, actions, or charges pertaining to the Contributions, including without limitation any claims or allegations that any or all of the Contributions infringes, violates, or misappropriate the intellectual property rights of any third party (You further agree that You will notify Semmle immediately if You become aware of any such actual or potential claims, suits, actions, allegations or charges).
-
-6. **Employer**. If Your employer(s) has rights to intellectual property that You create that includes Your Contributions, You represent and warrant that Your employer has waived such rights for Your Contributions to Semmle, or that You have received permission to make Contributions on behalf of that employer and that You are authorized to execute this Agreement on behalf of Your employer.
-
-7. **Inclusion of Code**. We determine the code that is in our Works. You understand that the decision to include the Contribution in any project or source repository is entirely that of Semmle, and this agreement does not guarantee that the Contributions will be included in any product.
-
-8. **Disclaimer**. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Except as set forth herein, and unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND.
-
-9. **General**. The failure of either party to enforce its rights under this Agreement for any period shall not be construed as a waiver of such rights. No changes or modifications or waivers to this Agreement will be effective unless in writing and signed by both parties. In the event that any provision of this Agreement shall be determined to be illegal or unenforceable, that provision will be limited or eliminated to the minimum extent necessary so that this Agreement shall otherwise remain in full force and effect and enforceable. This Agreement shall be governed by and construed in accordance with the laws of the State of California in the United States without regard to the conflicts of laws provisions thereof. In any action or proceeding to enforce rights under this Agreement, the prevailing party will be entitled to recover costs and attorneys’ fees.
diff --git a/COPYRIGHT b/COPYRIGHT
deleted file mode 100644
index 65d947ff274..00000000000
--- a/COPYRIGHT
+++ /dev/null
@@ -1,13 +0,0 @@
-Copyright (c) Semmle Inc and other contributors. All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License"); you may not use
-this file except in compliance with the License. You may obtain a copy of the
-License at http://www.apache.org/licenses/LICENSE-2.0
-
-THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
-WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
-MERCHANTABLITY OR NON-INFRINGEMENT.
-
-See the Apache Version 2.0 License for specific language governing permissions
-and limitations under the License.
diff --git a/LICENSE b/LICENSE
index d9a10c0d8e8..e29b05cd648 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,176 +1,21 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
+MIT License
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+Copyright (c) 2006-2020 GitHub, Inc.
- 1. Definitions.
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 1f77856cfc4..19d38c3b40a 100644
--- a/README.md
+++ b/README.md
@@ -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 2b53e6d56aa..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,44 +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. |
+| 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.
+ * 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-csharp.md b/change-notes/1.25/analysis-csharp.md
new file mode 100644
index 00000000000..fe19c1d8b20
--- /dev/null
+++ b/change-notes/1.25/analysis-csharp.md
@@ -0,0 +1,28 @@
+# 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
+
+## 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 construced 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`.)
+
+## Changes to autobuilder
diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md
new file mode 100644
index 00000000000..6e759b62868
--- /dev/null
+++ b/change-notes/1.25/analysis-javascript.md
@@ -0,0 +1,27 @@
+# Improvements to JavaScript analysis
+
+## General improvements
+
+* Support for the following frameworks and libraries has been improved:
+ - [jGrowl](https://github.com/stanlemon/jGrowl)
+ - [jQuery](https://jquery.com/)
+
+## 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. |
+
+## Changes to existing queries
+
+| **Query** | **Expected impact** | **Change** |
+|--------------------------------|------------------------------|---------------------------------------------------------------------------|
+| Misspelled variable name (`js/misspelled-variable-name`) | Message changed | The message for this query now correctly identifies the misspelled variable in additional cases. |
+| Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional file system calls. |
+| Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional command execution calls. |
+| Expression has no effect (`js/useless-expression`) | Less results | This query no longer flags an expression when that expression is the only content of the containing file. |
+
+## Changes to libraries
+
+* Added data flow for `Map` and `Set`, and added matching type-tracking steps that can accessed using the `CollectionsTypeTracking` module.
diff --git a/config/sync-files.py b/config/sync-files.py
index 30be173c18b..e667907b12c 100644
--- a/config/sync-files.py
+++ b/config/sync-files.py
@@ -107,7 +107,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 d548aca26e7..44557503e43 100644
--- a/cpp/ql/src/Critical/MemoryFreed.qll
+++ b/cpp/ql/src/Critical/MemoryFreed.qll
@@ -4,15 +4,24 @@ private predicate freed(Expr e) {
e = any(DeallocationExpr de).getFreedExpr()
or
exists(ExprCall c |
- // cautiously assume that any ExprCall could be a freeCall.
+ // cautiously assume that any `ExprCall` could be a deallocation expression.
c.getAnArgument() = 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/Critical/NewDelete.qll b/cpp/ql/src/Critical/NewDelete.qll
index 9dd55525b59..30b9f9ad94a 100644
--- a/cpp/ql/src/Critical/NewDelete.qll
+++ b/cpp/ql/src/Critical/NewDelete.qll
@@ -5,17 +5,34 @@
import cpp
import semmle.code.cpp.controlflow.SSA
import semmle.code.cpp.dataflow.DataFlow
+import semmle.code.cpp.models.implementations.Allocation
+import semmle.code.cpp.models.implementations.Deallocation
/**
* Holds if `alloc` is a use of `malloc` or `new`. `kind` is
* a string describing the type of the allocation.
*/
predicate allocExpr(Expr alloc, string kind) {
- isAllocationExpr(alloc) and
- not alloc.isFromUninstantiatedTemplate(_) and
(
- alloc instanceof FunctionCall and
- kind = "malloc"
+ exists(Function target |
+ alloc.(AllocationExpr).(FunctionCall).getTarget() = target and
+ (
+ target.getName() = "operator new" and
+ kind = "new" and
+ // exclude placement new and custom overloads as they
+ // may not conform to assumptions
+ not target.getNumberOfParameters() > 1
+ or
+ target.getName() = "operator new[]" and
+ kind = "new[]" and
+ // exclude placement new and custom overloads as they
+ // may not conform to assumptions
+ not target.getNumberOfParameters() > 1
+ or
+ not target instanceof OperatorNewAllocationFunction and
+ kind = "malloc"
+ )
+ )
or
alloc instanceof NewExpr and
kind = "new" and
@@ -28,7 +45,8 @@ predicate allocExpr(Expr alloc, string kind) {
// exclude placement new and custom overloads as they
// may not conform to assumptions
not alloc.(NewArrayExpr).getAllocatorCall().getTarget().getNumberOfParameters() > 1
- )
+ ) and
+ not alloc.isFromUninstantiatedTemplate(_)
}
/**
@@ -110,8 +128,20 @@ predicate allocReaches(Expr e, Expr alloc, string kind) {
* describing the type of that free or delete.
*/
predicate freeExpr(Expr free, Expr freed, string kind) {
- freeCall(free, freed) and
- kind = "free"
+ exists(Function target |
+ freed = free.(DeallocationExpr).getFreedExpr() and
+ free.(FunctionCall).getTarget() = target and
+ (
+ target.getName() = "operator delete" and
+ kind = "delete"
+ or
+ target.getName() = "operator delete[]" and
+ kind = "delete[]"
+ or
+ not target instanceof OperatorDeleteDeallocationFunction and
+ kind = "free"
+ )
+ )
or
free.(DeleteExpr).getExpr() = freed and
kind = "delete"
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/Protocols/TlsSettingsMisconfiguration.ql b/cpp/ql/src/Likely Bugs/Protocols/TlsSettingsMisconfiguration.ql
index fb7edce038c..04b3d13a3f7 100644
--- a/cpp/ql/src/Likely Bugs/Protocols/TlsSettingsMisconfiguration.ql
+++ b/cpp/ql/src/Likely Bugs/Protocols/TlsSettingsMisconfiguration.ql
@@ -3,7 +3,7 @@
* @description Using the TLS or SSLv23 protocol from the boost::asio library, but not disabling deprecated protocols, or disabling minimum-recommended protocols.
* @kind problem
* @problem.severity error
- * @id cpp/boost/tls_settings_misconfiguration
+ * @id cpp/boost/tls-settings-misconfiguration
* @tags security
*/
diff --git a/cpp/ql/src/Likely Bugs/ReturnConstTypeMember.cpp b/cpp/ql/src/Likely Bugs/ReturnConstTypeMember.cpp
index cc6b6da3abb..ad3d7d3c9e1 100644
--- a/cpp/ql/src/Likely Bugs/ReturnConstTypeMember.cpp
+++ b/cpp/ql/src/Likely Bugs/ReturnConstTypeMember.cpp
@@ -8,6 +8,6 @@ struct S {
// Whereas here it does make a semantic difference.
auto getValCorrect() const -> int {
- return val
+ return val;
}
};
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/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-022/TaintedPath.ql b/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql
index 42a29b96268..496b957cca3 100644
--- a/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql
+++ b/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql
@@ -2,7 +2,7 @@
* @name Uncontrolled data used in path expression
* @description Accessing paths influenced by users can allow an
* attacker to access unexpected resources.
- * @kind problem
+ * @kind path-problem
* @problem.severity warning
* @precision medium
* @id cpp/path-injection
@@ -17,6 +17,7 @@ import cpp
import semmle.code.cpp.security.FunctionWithWrappers
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking
+import TaintedWithPath
/**
* A function for opening a file.
@@ -51,12 +52,19 @@ class FileFunction extends FunctionWithWrappers {
override predicate interestingArg(int arg) { arg = 0 }
}
+class TaintedPathConfiguration extends TaintTrackingConfiguration {
+ override predicate isSink(Element tainted) {
+ exists(FileFunction fileFunction | fileFunction.outermostWrapperFunctionCall(tainted, _))
+ }
+}
+
from
- FileFunction fileFunction, Expr taintedArg, Expr taintSource, string taintCause, string callChain
+ FileFunction fileFunction, Expr taintedArg, Expr taintSource, PathNode sourceNode,
+ PathNode sinkNode, string taintCause, string callChain
where
fileFunction.outermostWrapperFunctionCall(taintedArg, callChain) and
- tainted(taintSource, taintedArg) and
+ taintedWithPath(taintSource, taintedArg, sourceNode, sinkNode) and
isUserInput(taintSource, taintCause)
-select taintedArg,
+select taintedArg, sourceNode, sinkNode,
"This argument to a file access function is derived from $@ and then passed to " + callChain,
taintSource, "user input (" + taintCause + ")"
diff --git a/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql b/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql
index 8b7fb83df81..0e0c4add7f6 100644
--- a/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql
+++ b/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql
@@ -2,7 +2,7 @@
* @name CGI script vulnerable to cross-site scripting
* @description Writing user input directly to a web page
* allows for a cross-site scripting vulnerability.
- * @kind problem
+ * @kind path-problem
* @problem.severity error
* @precision high
* @id cpp/cgi-xss
@@ -13,6 +13,7 @@
import cpp
import semmle.code.cpp.commons.Environment
import semmle.code.cpp.security.TaintTracking
+import TaintedWithPath
/** A call that prints its arguments to `stdout`. */
class PrintStdoutCall extends FunctionCall {
@@ -27,8 +28,13 @@ class QueryString extends EnvironmentRead {
QueryString() { getEnvironmentVariable() = "QUERY_STRING" }
}
-from QueryString query, PrintStdoutCall call, Element printedArg
-where
- call.getAnArgument() = printedArg and
- tainted(query, printedArg)
-select printedArg, "Cross-site scripting vulnerability due to $@.", query, "this query data"
+class Configuration extends TaintTrackingConfiguration {
+ override predicate isSink(Element tainted) {
+ exists(PrintStdoutCall call | call.getAnArgument() = tainted)
+ }
+}
+
+from QueryString query, Element printedArg, PathNode sourceNode, PathNode sinkNode
+where taintedWithPath(query, printedArg, sourceNode, sinkNode)
+select printedArg, sourceNode, sinkNode, "Cross-site scripting vulnerability due to $@.", query,
+ "this query data"
diff --git a/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql b/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql
index 3f234dd0678..de786d22f30 100644
--- a/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql
+++ b/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql
@@ -3,7 +3,7 @@
* @description Including user-supplied data in a SQL query without
* neutralizing special elements can make code vulnerable
* to SQL Injection.
- * @kind problem
+ * @kind path-problem
* @problem.severity error
* @precision high
* @id cpp/sql-injection
@@ -15,6 +15,7 @@ import cpp
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.FunctionWithWrappers
import semmle.code.cpp.security.TaintTracking
+import TaintedWithPath
class SQLLikeFunction extends FunctionWithWrappers {
SQLLikeFunction() { sqlArgument(this.getName(), _) }
@@ -22,11 +23,19 @@ class SQLLikeFunction extends FunctionWithWrappers {
override predicate interestingArg(int arg) { sqlArgument(this.getName(), arg) }
}
-from SQLLikeFunction runSql, Expr taintedArg, Expr taintSource, string taintCause, string callChain
+class Configuration extends TaintTrackingConfiguration {
+ override predicate isSink(Element tainted) {
+ exists(SQLLikeFunction runSql | runSql.outermostWrapperFunctionCall(tainted, _))
+ }
+}
+
+from
+ SQLLikeFunction runSql, Expr taintedArg, Expr taintSource, PathNode sourceNode, PathNode sinkNode,
+ string taintCause, string callChain
where
runSql.outermostWrapperFunctionCall(taintedArg, callChain) and
- tainted(taintSource, taintedArg) and
+ taintedWithPath(taintSource, taintedArg, sourceNode, sinkNode) and
isUserInput(taintSource, taintCause)
-select taintedArg,
+select taintedArg, sourceNode, sinkNode,
"This argument to a SQL query function is derived from $@ and then passed to " + callChain,
taintSource, "user input (" + taintCause + ")"
diff --git a/cpp/ql/src/Security/CWE/CWE-114/UncontrolledProcessOperation.ql b/cpp/ql/src/Security/CWE/CWE-114/UncontrolledProcessOperation.ql
index 233fe34e6bf..943c13f9c5d 100644
--- a/cpp/ql/src/Security/CWE/CWE-114/UncontrolledProcessOperation.ql
+++ b/cpp/ql/src/Security/CWE/CWE-114/UncontrolledProcessOperation.ql
@@ -3,7 +3,7 @@
* @description Using externally controlled strings in a process
* operation can allow an attacker to execute malicious
* commands.
- * @kind problem
+ * @kind path-problem
* @problem.severity warning
* @precision medium
* @id cpp/uncontrolled-process-operation
@@ -14,13 +14,24 @@
import cpp
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking
+import TaintedWithPath
-from string processOperation, int processOperationArg, FunctionCall call, Expr arg, Element source
+predicate isProcessOperationExplanation(Expr arg, string processOperation) {
+ exists(int processOperationArg, FunctionCall call |
+ isProcessOperationArgument(processOperation, processOperationArg) and
+ call.getTarget().getName() = processOperation and
+ call.getArgument(processOperationArg) = arg
+ )
+}
+
+class Configuration extends TaintTrackingConfiguration {
+ override predicate isSink(Element arg) { isProcessOperationExplanation(arg, _) }
+}
+
+from string processOperation, Expr arg, Expr source, PathNode sourceNode, PathNode sinkNode
where
- isProcessOperationArgument(processOperation, processOperationArg) and
- call.getTarget().getName() = processOperation and
- call.getArgument(processOperationArg) = arg and
- tainted(source, arg)
-select arg,
+ isProcessOperationExplanation(arg, processOperation) and
+ taintedWithPath(source, arg, sourceNode, sinkNode)
+select arg, sourceNode, sinkNode,
"The value of this argument may come from $@ and is being passed to " + processOperation, source,
source.toString()
diff --git a/cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.ql b/cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.ql
index ff136d2c06b..f1a8b4e8544 100644
--- a/cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.ql
+++ b/cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.ql
@@ -2,7 +2,7 @@
* @name Unbounded write
* @description Buffer write operations that do not control the length
* of data written may overflow.
- * @kind problem
+ * @kind path-problem
* @problem.severity error
* @precision medium
* @id cpp/unbounded-write
@@ -16,6 +16,7 @@
import semmle.code.cpp.security.BufferWrite
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking
+import TaintedWithPath
/*
* --- Summary of CWE-120 alerts ---
@@ -54,32 +55,48 @@ predicate isUnboundedWrite(BufferWrite bw) {
* }
*/
+/**
+ * Holds if `e` is a source buffer going into an unbounded write `bw` or a
+ * qualifier of (a qualifier of ...) such a source.
+ */
+predicate unboundedWriteSource(Expr e, BufferWrite bw) {
+ isUnboundedWrite(bw) and e = bw.getASource()
+ or
+ exists(FieldAccess fa | unboundedWriteSource(fa, bw) and e = fa.getQualifier())
+}
+
/*
* --- user input reach ---
*/
-/**
- * Identifies expressions that are potentially tainted with user
- * input. Most of the work for this is actually done by the
- * TaintTracking library.
- */
-predicate tainted2(Expr expr, Expr inputSource, string inputCause) {
- taintedIncludingGlobalVars(inputSource, expr, _) and
- inputCause = inputSource.toString()
- or
- exists(Expr e | tainted2(e, inputSource, inputCause) |
- // field accesses of a tainted struct are tainted
- e = expr.(FieldAccess).getQualifier()
- )
+class Configuration extends TaintTrackingConfiguration {
+ override predicate isSink(Element tainted) { unboundedWriteSource(tainted, _) }
+
+ override predicate taintThroughGlobals() { any() }
}
/*
* --- put it together ---
*/
-from BufferWrite bw, Expr inputSource, string inputCause
+/*
+ * An unbounded write is, for example `strcpy(..., tainted)`. We're looking
+ * for a tainted source buffer of an unbounded write, where this source buffer
+ * is a sink in the taint-tracking analysis.
+ *
+ * In the case of `gets` and `scanf`, where the source buffer is implicit, the
+ * `BufferWrite` library reports the source buffer to be the same as the
+ * destination buffer. Since those destination-buffer arguments are also
+ * modeled in the taint-tracking library as being _sources_ of taint, they are
+ * in practice reported as being tainted because the `security.TaintTracking`
+ * library does not distinguish between taint going into an argument and out of
+ * an argument. Thus, we get the desired alerts.
+ */
+
+from BufferWrite bw, Expr inputSource, Expr tainted, PathNode sourceNode, PathNode sinkNode
where
- isUnboundedWrite(bw) and
- tainted2(bw.getASource(), inputSource, inputCause)
-select bw, "This '" + bw.getBWDesc() + "' with input from $@ may overflow the destination.",
- inputSource, inputCause
+ taintedWithPath(inputSource, tainted, sourceNode, sinkNode) and
+ unboundedWriteSource(tainted, bw)
+select bw, sourceNode, sinkNode,
+ "This '" + bw.getBWDesc() + "' with input from $@ may overflow the destination.", inputSource,
+ inputSource.toString()
diff --git a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql
index 02a0f42fa6b..91ccc5c4d40 100644
--- a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql
+++ b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql
@@ -3,7 +3,7 @@
* @description Using externally-controlled format strings in
* printf-style functions can lead to buffer overflows
* or data representation problems.
- * @kind problem
+ * @kind path-problem
* @problem.severity warning
* @precision medium
* @id cpp/tainted-format-string
@@ -16,12 +16,21 @@ import cpp
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.FunctionWithWrappers
import semmle.code.cpp.security.TaintTracking
+import TaintedWithPath
-from PrintfLikeFunction printf, Expr arg, string printfFunction, Expr userValue, string cause
+class Configuration extends TaintTrackingConfiguration {
+ override predicate isSink(Element tainted) {
+ exists(PrintfLikeFunction printf | printf.outermostWrapperFunctionCall(tainted, _))
+ }
+}
+
+from
+ PrintfLikeFunction printf, Expr arg, PathNode sourceNode, PathNode sinkNode,
+ string printfFunction, Expr userValue, string cause
where
printf.outermostWrapperFunctionCall(arg, printfFunction) and
- tainted(userValue, arg) and
+ taintedWithPath(userValue, arg, sourceNode, sinkNode) and
isUserInput(userValue, cause)
-select arg,
+select arg, sourceNode, sinkNode,
"The value of this argument may come from $@ and is being used as a formatting argument to " +
printfFunction, userValue, cause
diff --git a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql
index f3cb4fcf1bb..96cffdb024b 100644
--- a/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql
+++ b/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatStringThroughGlobalVar.ql
@@ -3,7 +3,7 @@
* @description Using externally-controlled format strings in
* printf-style functions can lead to buffer overflows
* or data representation problems.
- * @kind problem
+ * @kind path-problem
* @problem.severity warning
* @precision medium
* @id cpp/tainted-format-string-through-global
@@ -16,15 +16,24 @@ import cpp
import semmle.code.cpp.security.FunctionWithWrappers
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking
+import TaintedWithPath
+
+class Configuration extends TaintTrackingConfiguration {
+ override predicate isSink(Element tainted) {
+ exists(PrintfLikeFunction printf | printf.outermostWrapperFunctionCall(tainted, _))
+ }
+
+ override predicate taintThroughGlobals() { any() }
+}
from
- PrintfLikeFunction printf, Expr arg, string printfFunction, Expr userValue, string cause,
- string globalVar
+ PrintfLikeFunction printf, Expr arg, PathNode sourceNode, PathNode sinkNode,
+ string printfFunction, Expr userValue, string cause
where
printf.outermostWrapperFunctionCall(arg, printfFunction) and
- not tainted(_, arg) and
- taintedIncludingGlobalVars(userValue, arg, globalVar) and
+ not taintedWithoutGlobals(arg) and
+ taintedWithPath(userValue, arg, sourceNode, sinkNode) and
isUserInput(userValue, cause)
-select arg,
- "This value may flow through $@, originating from $@, and is a formatting argument to " +
- printfFunction + ".", globalVarFromId(globalVar), globalVar, userValue, cause
+select arg, sourceNode, sinkNode,
+ "The value of this argument may come from $@ and is being used as a formatting argument to " +
+ printfFunction, userValue, cause
diff --git a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql
index 8013fbcf614..a4b0f131d14 100644
--- a/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql
+++ b/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql
@@ -2,7 +2,7 @@
* @name Uncontrolled data in arithmetic expression
* @description Arithmetic operations on uncontrolled data that is not
* validated can cause overflows.
- * @kind problem
+ * @kind path-problem
* @problem.severity warning
* @precision medium
* @id cpp/uncontrolled-arithmetic
@@ -15,6 +15,7 @@ import cpp
import semmle.code.cpp.security.Overflow
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking
+import TaintedWithPath
predicate isRandCall(FunctionCall fc) { fc.getTarget().getName() = "rand" }
@@ -40,9 +41,22 @@ class SecurityOptionsArith extends SecurityOptions {
}
}
-predicate taintedVarAccess(Expr origin, VariableAccess va) {
- isUserInput(origin, _) and
- tainted(origin, va)
+predicate isDiv(VariableAccess va) { exists(AssignDivExpr div | div.getLValue() = va) }
+
+predicate missingGuard(VariableAccess va, string effect) {
+ exists(Operation op | op.getAnOperand() = va |
+ missingGuardAgainstUnderflow(op, va) and effect = "underflow"
+ or
+ missingGuardAgainstOverflow(op, va) and effect = "overflow"
+ )
+}
+
+class Configuration extends TaintTrackingConfiguration {
+ override predicate isSink(Element e) {
+ isDiv(e)
+ or
+ missingGuard(e, _)
+ }
}
/**
@@ -50,19 +64,17 @@ predicate taintedVarAccess(Expr origin, VariableAccess va) {
* range.
*/
predicate guardedByAssignDiv(Expr origin) {
- isUserInput(origin, _) and
- exists(AssignDivExpr div, VariableAccess va | tainted(origin, va) and div.getLValue() = va)
+ exists(VariableAccess va |
+ taintedWithPath(origin, va, _, _) and
+ isDiv(va)
+ )
}
-from Expr origin, Operation op, VariableAccess va, string effect
+from Expr origin, VariableAccess va, string effect, PathNode sourceNode, PathNode sinkNode
where
- taintedVarAccess(origin, va) and
- op.getAnOperand() = va and
- (
- missingGuardAgainstUnderflow(op, va) and effect = "underflow"
- or
- missingGuardAgainstOverflow(op, va) and effect = "overflow"
- ) and
+ taintedWithPath(origin, va, sourceNode, sinkNode) and
+ missingGuard(va, effect) and
not guardedByAssignDiv(origin)
-select va, "$@ flows to here and is used in arithmetic, potentially causing an " + effect + ".",
- origin, "Uncontrolled value"
+select va, sourceNode, sinkNode,
+ "$@ flows to here and is used in arithmetic, potentially causing an " + effect + ".", origin,
+ "Uncontrolled value"
diff --git a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql
index 85248a26d19..54c6d1e7a96 100644
--- a/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql
+++ b/cpp/ql/src/Security/CWE/CWE-190/TaintedAllocationSize.ql
@@ -2,7 +2,7 @@
* @name Overflow in uncontrolled allocation size
* @description Allocating memory with a size controlled by an external
* user can result in integer overflow.
- * @kind problem
+ * @kind path-problem
* @problem.severity error
* @precision high
* @id cpp/uncontrolled-allocation-size
@@ -13,21 +13,33 @@
import cpp
import semmle.code.cpp.security.TaintTracking
+import TaintedWithPath
-predicate taintedAllocSize(Expr e, Expr source, string taintCause) {
- (
- isAllocationExpr(e) or
- any(MulExpr me | me.getAChild() instanceof SizeofOperator) = e
- ) 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) { allocSink(_, tainted) }
+}
+
+predicate taintedAllocSize(
+ Expr source, Expr alloc, PathNode sourceNode, PathNode sinkNode, string taintCause
+) {
+ isUserInput(source, taintCause) and
exists(Expr tainted |
- tainted = e.getAChild() and
- tainted.getUnspecifiedType() instanceof IntegralType and
- isUserInput(source, taintCause) and
- tainted(source, tainted)
+ allocSink(alloc, tainted) and
+ taintedWithPath(source, tainted, sourceNode, sinkNode)
)
}
-from Expr e, Expr source, string taintCause
-where taintedAllocSize(e, source, taintCause)
-select e, "This allocation size is derived from $@ and might overflow", source,
- "user input (" + taintCause + ")"
+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-290/AuthenticationBypass.ql b/cpp/ql/src/Security/CWE/CWE-290/AuthenticationBypass.ql
index 19bee24a16d..80b5ee49e97 100644
--- a/cpp/ql/src/Security/CWE/CWE-290/AuthenticationBypass.ql
+++ b/cpp/ql/src/Security/CWE/CWE-290/AuthenticationBypass.ql
@@ -3,7 +3,7 @@
* @description Authentication by checking that the peer's address
* matches a known IP or web address is unsafe as it is
* vulnerable to spoofing attacks.
- * @kind problem
+ * @kind path-problem
* @problem.severity warning
* @precision medium
* @id cpp/user-controlled-bypass
@@ -12,6 +12,7 @@
*/
import semmle.code.cpp.security.TaintTracking
+import TaintedWithPath
predicate hardCodedAddressOrIP(StringLiteral txt) {
exists(string s | s = txt.getValueText() |
@@ -102,16 +103,21 @@ predicate useOfHardCodedAddressOrIP(Expr use) {
* untrusted input then it might be vulnerable to a spoofing
* attack.
*/
-predicate hardCodedAddressInCondition(Expr source, Expr condition) {
- // One of the sub-expressions of the condition is tainted.
- exists(Expr taintedExpr | taintedExpr.getParent+() = condition | tainted(source, taintedExpr)) and
+predicate hardCodedAddressInCondition(Expr subexpression, Expr condition) {
+ subexpression = condition.getAChild+() and
// One of the sub-expressions of the condition is a hard-coded
// IP or web-address.
- exists(Expr use | use.getParent+() = condition | useOfHardCodedAddressOrIP(use)) and
+ exists(Expr use | use = condition.getAChild+() | useOfHardCodedAddressOrIP(use)) and
condition = any(IfStmt ifStmt).getCondition()
}
-from Expr source, Expr condition
-where hardCodedAddressInCondition(source, condition)
-select condition, "Untrusted input $@ might be vulnerable to a spoofing attack.", source,
- source.toString()
+class Configuration extends TaintTrackingConfiguration {
+ override predicate isSink(Element sink) { hardCodedAddressInCondition(sink, _) }
+}
+
+from Expr subexpression, Expr source, Expr condition, PathNode sourceNode, PathNode sinkNode
+where
+ hardCodedAddressInCondition(subexpression, condition) and
+ taintedWithPath(source, subexpression, sourceNode, sinkNode)
+select condition, sourceNode, sinkNode,
+ "Untrusted input $@ might be vulnerable to a spoofing attack.", source, source.toString()
diff --git a/cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.ql b/cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.ql
index aa153779df1..3e84c0a87d9 100644
--- a/cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.ql
+++ b/cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.ql
@@ -2,7 +2,7 @@
* @name Cleartext storage of sensitive information in buffer
* @description Storing sensitive information in cleartext can expose it
* to an attacker.
- * @kind problem
+ * @kind path-problem
* @problem.severity warning
* @precision medium
* @id cpp/cleartext-storage-buffer
@@ -14,12 +14,20 @@ import cpp
import semmle.code.cpp.security.BufferWrite
import semmle.code.cpp.security.TaintTracking
import semmle.code.cpp.security.SensitiveExprs
+import TaintedWithPath
-from BufferWrite w, Expr taintedArg, Expr taintSource, string taintCause, SensitiveExpr dest
+class Configuration extends TaintTrackingConfiguration {
+ override predicate isSink(Element tainted) { exists(BufferWrite w | w.getASource() = tainted) }
+}
+
+from
+ BufferWrite w, Expr taintedArg, Expr taintSource, PathNode sourceNode, PathNode sinkNode,
+ string taintCause, SensitiveExpr dest
where
- tainted(taintSource, taintedArg) and
+ taintedWithPath(taintSource, taintedArg, sourceNode, sinkNode) and
isUserInput(taintSource, taintCause) and
w.getASource() = taintedArg and
dest = w.getDest()
-select w, "This write into buffer '" + dest.toString() + "' may contain unencrypted data from $@",
+select w, sourceNode, sinkNode,
+ "This write into buffer '" + dest.toString() + "' may contain unencrypted data from $@",
taintSource, "user input (" + taintCause + ")"
diff --git a/cpp/ql/src/Security/CWE/CWE-313/CleartextSqliteDatabase.ql b/cpp/ql/src/Security/CWE/CWE-313/CleartextSqliteDatabase.ql
index e4f1e9c834a..000833cbb58 100644
--- a/cpp/ql/src/Security/CWE/CWE-313/CleartextSqliteDatabase.ql
+++ b/cpp/ql/src/Security/CWE/CWE-313/CleartextSqliteDatabase.ql
@@ -2,7 +2,7 @@
* @name Cleartext storage of sensitive information in an SQLite database
* @description Storing sensitive information in a non-encrypted
* database can expose it to an attacker.
- * @kind problem
+ * @kind path-problem
* @problem.severity warning
* @precision medium
* @id cpp/cleartext-storage-database
@@ -13,6 +13,7 @@
import cpp
import semmle.code.cpp.security.SensitiveExprs
import semmle.code.cpp.security.TaintTracking
+import TaintedWithPath
class UserInputIsSensitiveExpr extends SecurityOptions {
override predicate isUserInput(Expr expr, string cause) {
@@ -32,10 +33,21 @@ predicate sqlite_encryption_used() {
any(FunctionCall fc).getTarget().getName().matches("sqlite%\\_key\\_%")
}
-from SensitiveExpr taintSource, Expr taintedArg, SqliteFunctionCall sqliteCall
+class Configuration extends TaintTrackingConfiguration {
+ override predicate isSink(Element taintedArg) {
+ exists(SqliteFunctionCall sqliteCall |
+ taintedArg = sqliteCall.getASource() and
+ not sqlite_encryption_used()
+ )
+ }
+}
+
+from
+ SensitiveExpr taintSource, Expr taintedArg, SqliteFunctionCall sqliteCall, PathNode sourceNode,
+ PathNode sinkNode
where
- tainted(taintSource, taintedArg) and
- taintedArg = sqliteCall.getASource() and
- not sqlite_encryption_used()
-select sqliteCall, "This SQLite call may store $@ in a non-encrypted SQLite database", taintSource,
+ taintedWithPath(taintSource, taintedArg, sourceNode, sinkNode) and
+ taintedArg = sqliteCall.getASource()
+select sqliteCall, sourceNode, sinkNode,
+ "This SQLite call may store $@ in a non-encrypted SQLite database", taintSource,
"sensitive information"
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/Security/CWE/CWE-807/TaintedCondition.ql b/cpp/ql/src/Security/CWE/CWE-807/TaintedCondition.ql
index b940da029ec..e60f592b2af 100644
--- a/cpp/ql/src/Security/CWE/CWE-807/TaintedCondition.ql
+++ b/cpp/ql/src/Security/CWE/CWE-807/TaintedCondition.ql
@@ -3,7 +3,7 @@
* @description Using untrusted inputs in a statement that makes a
* security decision makes code vulnerable to
* attack.
- * @kind problem
+ * @kind path-problem
* @problem.severity warning
* @precision medium
* @id cpp/tainted-permissions-check
@@ -12,14 +12,9 @@
*/
import semmle.code.cpp.security.TaintTracking
+import TaintedWithPath
-/**
- * Holds if there is an 'if' statement whose condition `condition`
- * is influenced by tainted data `source`, and the body contains
- * `raise` which escalates privilege.
- */
-predicate cwe807violation(Expr source, Expr condition, Expr raise) {
- tainted(source, condition) and
+predicate sensitiveCondition(Expr condition, Expr raise) {
raisesPrivilege(raise) and
exists(IfStmt ifstmt |
ifstmt.getCondition() = condition and
@@ -27,7 +22,19 @@ predicate cwe807violation(Expr source, Expr condition, Expr raise) {
)
}
-from Expr source, Expr condition, Expr raise
-where cwe807violation(source, condition, raise)
-select condition, "Reliance on untrusted input $@ to raise privilege at $@", source,
- source.toString(), raise, raise.toString()
+class Configuration extends TaintTrackingConfiguration {
+ override predicate isSink(Element tainted) { sensitiveCondition(tainted, _) }
+}
+
+/*
+ * Produce an alert if there is an 'if' statement whose condition `condition`
+ * is influenced by tainted data `source`, and the body contains
+ * `raise` which escalates privilege.
+ */
+
+from Expr source, Expr condition, Expr raise, PathNode sourceNode, PathNode sinkNode
+where
+ taintedWithPath(source, condition, sourceNode, sinkNode) and
+ sensitiveCondition(condition, raise)
+select condition, sourceNode, sinkNode, "Reliance on untrusted input $@ to raise privilege at $@",
+ source, source.toString(), raise, raise.toString()
diff --git a/cpp/ql/src/codeql-suites/cpp-lgtm-full.qls b/cpp/ql/src/codeql-suites/cpp-lgtm-full.qls
index 2036584e44c..e9fda1cdb9e 100644
--- a/cpp/ql/src/codeql-suites/cpp-lgtm-full.qls
+++ b/cpp/ql/src/codeql-suites/cpp-lgtm-full.qls
@@ -12,3 +12,8 @@
- Critical/FileNeverClosed.ql
- Critical/MemoryMayNotBeFreed.ql
- Critical/MemoryNeverFreed.ql
+# These are only for IDE use.
+- exclude:
+ tags contain:
+ - ide-contextual-queries/local-definitions
+ - ide-contextual-queries/local-references
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/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/qlpack.yml b/cpp/ql/src/qlpack.yml
index a1c7df902de..45ae614f88e 100644
--- a/cpp/ql/src/qlpack.yml
+++ b/cpp/ql/src/qlpack.yml
@@ -2,3 +2,4 @@ name: codeql-cpp
version: 0.0.0
dbscheme: semmlecode.cpp.dbscheme
suites: codeql-suites
+extractor: cpp
diff --git a/cpp/ql/src/semmle/code/cpp/Class.qll b/cpp/ql/src/semmle/code/cpp/Class.qll
index 67a2d11ff24..5aa9f43f48b 100644
--- a/cpp/ql/src/semmle/code/cpp/Class.qll
+++ b/cpp/ql/src/semmle/code/cpp/Class.qll
@@ -458,6 +458,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 +909,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..7f961bfd6f6 100644
--- a/cpp/ql/src/semmle/code/cpp/Comments.qll
+++ b/cpp/ql/src/semmle/code/cpp/Comments.qll
@@ -13,8 +13,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..691c0e08de3 100644
--- a/cpp/ql/src/semmle/code/cpp/Compilation.qll
+++ b/cpp/ql/src/semmle/code/cpp/Compilation.qll
@@ -21,9 +21,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 +40,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..840705b01b5 100644
--- a/cpp/ql/src/semmle/code/cpp/Declaration.qll
+++ b/cpp/ql/src/semmle/code/cpp/Declaration.qll
@@ -25,7 +25,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.
*
@@ -161,6 +161,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 +304,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..37459602c03 100644
--- a/cpp/ql/src/semmle/code/cpp/Diagnostics.qll
+++ b/cpp/ql/src/semmle/code/cpp/Diagnostics.qll
@@ -11,6 +11,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/File.qll b/cpp/ql/src/semmle/code/cpp/File.qll
index 60ef2d587ef..2c81e768db7 100644
--- a/cpp/ql/src/semmle/code/cpp/File.qll
+++ b/cpp/ql/src/semmle/code/cpp/File.qll
@@ -3,7 +3,7 @@ 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 +28,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 +36,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
diff --git a/cpp/ql/src/semmle/code/cpp/Function.qll b/cpp/ql/src/semmle/code/cpp/Function.qll
index 9ed4b27f562..90998ed4ea3 100644
--- a/cpp/ql/src/semmle/code/cpp/Function.qll
+++ b/cpp/ql/src/semmle/code/cpp/Function.qll
@@ -103,6 +103,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 +118,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)`.
diff --git a/cpp/ql/src/semmle/code/cpp/Namespace.qll b/cpp/ql/src/semmle/code/cpp/Namespace.qll
index d53fda87c2f..839a85d6b8f 100644
--- a/cpp/ql/src/semmle/code/cpp/Namespace.qll
+++ b/cpp/ql/src/semmle/code/cpp/Namespace.qll
@@ -130,7 +130,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) }
}
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..4d546a736dc 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))
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/commons/Alloc.qll b/cpp/ql/src/semmle/code/cpp/commons/Alloc.qll
index a508c929d9a..a9597fc72b5 100644
--- a/cpp/ql/src/semmle/code/cpp/commons/Alloc.qll
+++ b/cpp/ql/src/semmle/code/cpp/commons/Alloc.qll
@@ -23,6 +23,8 @@ predicate freeFunction(Function f, int argNum) { argNum = f.(DeallocationFunctio
/**
* A call to a library routine that frees memory.
+ *
+ * DEPRECATED: Use `DeallocationExpr` instead (this also includes `delete` expressions).
*/
predicate freeCall(FunctionCall fc, Expr arg) { arg = fc.(DeallocationExpr).getFreedExpr() }
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/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..22e39f0b907 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/EscapesTree.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/EscapesTree.qll
@@ -166,10 +166,13 @@ 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())
+ or
+ e = call.getQualifier().getFullyConverted() and
+ e.getUnderlyingType() instanceof PointerType
)
or
exists(AssignExpr assign | e = assign.getRValue().getFullyConverted())
@@ -187,8 +190,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 +210,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 +272,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 +305,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/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
index a1daeb66411..9587ea5f274 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
@@ -251,15 +251,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 +289,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
+ nodeCandFwd1IsStored(f, config) and
not inBarrier(node, config)
)
or
@@ -317,13 +313,34 @@ 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 f, Node node, boolean fromArg, Configuration config) {
+ exists(Node mid |
+ nodeCandFwd1(mid, fromArg, config) and
+ read(mid, f, node)
+ )
+}
+
+/**
+ * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
+ exists(Node mid, Node node |
+ not fullBarrier(node, config) and
+ useFieldFlow(config) and
+ nodeCandFwd1(mid, config) and
+ store(mid, f, node)
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1ReturnPosition(
ReturnPosition pos, boolean fromArg, Configuration config
@@ -335,43 +352,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 +368,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 +379,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) {
@@ -435,55 +422,43 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
// store
exists(Content f |
nodeCand1Store(f, node, toReturn, config) and
- readCand1(f, config)
+ nodeCand1IsRead(f, config)
)
or
// read
exists(Node mid, Content f |
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ read(node, f, mid) and
+ nodeCandFwd1IsStored(f, 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`.
*/
pragma[nomagic]
-private predicate readCand1(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content f, 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, f, mid) and
+ nodeCandFwd1IsStored(f, unbind(config)) and
nodeCand1(mid, _, config)
)
}
@@ -492,8 +467,8 @@ pragma[nomagic]
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
exists(Node mid |
nodeCand1(mid, toReturn, config) and
- storeCandFwd1(f, unbind(config)) and
- storeDirect(node, f, mid)
+ nodeCandFwd1IsStored(f, unbind(config)) and
+ store(node, f, mid)
)
}
@@ -501,13 +476,29 @@ private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configu
* Holds if `f` 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
+private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
+ nodeCand1IsRead(f, conf) and
nodeCand1Store(f, _, _, 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 +506,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 +543,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 +557,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)
@@ -576,419 +570,73 @@ private predicate parameterThroughFlowCand(ParameterNode p, Configuration config
pragma[nomagic]
private predicate store(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
+ nodeCand1IsReadAndStored(f, config) and
nodeCand1(n2, unbind(config)) and
- (
- storeDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentSome(f), n2)
- )
+ store(n1, f, n2)
}
pragma[nomagic]
private predicate read(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
+ nodeCand1IsReadAndStored(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)
- )
+ read(n1, f, n2)
}
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 predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ localFlowStep(node1, node2, 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[noinline]
+private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ additionalLocalFlowStep(node1, node2, 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
- 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)
- )
-}
-
-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
+private predicate viableReturnPosOutNodeCand1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
) {
- argumentFlowsThrough(arg.getNode(), out.getNode(), _, t, TSummaryTaint(), config)
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, 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[noinline]
-private ReturnPosition getReturnPosition1(ReturnNodeExt node, Configuration config) {
- result = getReturnPosition(node) and
- nodeCand1(node, 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 +647,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,117 +659,126 @@ 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, Content f |
+ nodeCandFwd2(mid, fromArg, argStored, _, config) and
+ store(mid, f, node, config) and
stored = true
)
or
// read
exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, config) and
- storeCandFwd2(f, stored, config)
+ nodeCandFwd2Read(f, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(f, 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)
+ )
)
)
}
@@ -1130,86 +787,148 @@ private predicate nodeCandFwd2(NodeExt node, boolean fromArg, boolean stored, Co
* Holds if `f` 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 f, 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
+ store(mid, f, 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 f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+) {
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, true, config) and
+ read(mid, f, 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)
+ nodeCand2Store(f, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(f, 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 f, boolean read0 |
+ read(node, f, mid, config) and
+ nodeCandFwd2IsStored(f, 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()
)
}
@@ -1217,32 +936,36 @@ private predicate nodeCand2(NodeExt node, boolean toReturn, boolean read, Config
* Holds if `f` 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 f, 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, f, mid, config) and
+ nodeCandFwd2IsStored(f, 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 f, 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))
+ exists(Node mid |
+ store(node, f, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, true, config) and
+ nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
+/**
+ * Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
+ */
pragma[nomagic]
-private predicate storeCand2(Content f, boolean stored, Configuration conf) {
- exists(NodeExt node |
- nodeCand2Store(f, node, _, stored, conf) and
- nodeCand2(node, _, stored, conf)
+private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
+ exists(Node node |
+ nodeCand2Store(f, node, _, _, stored, conf) and
+ nodeCand2(node, _, _, stored, conf)
)
}
@@ -1251,29 +974,76 @@ private predicate storeCand2(Content f, boolean stored, Configuration conf) {
* covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate readStoreCand(Content f, Configuration conf) {
+private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
exists(boolean apNonEmpty |
- storeCand2(f, apNonEmpty, conf) and
- readCand2(f, apNonEmpty, conf)
+ nodeCand2IsStored(f, apNonEmpty, conf) and
+ nodeCand2IsRead(f, apNonEmpty, conf)
)
}
-private predicate nodeCand2(NodeExt node, Configuration config) { nodeCand2(node, _, _, config) }
+pragma[nomagic]
+private predicate nodeCand2Out(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
+) {
+ 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 flowOutOfCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate nodeCand2In(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
) {
- flowOutOfCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ 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 +1054,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 +1072,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 +1086,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 +1109,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,307 +1146,365 @@ 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 f, Node node2, Configuration config) {
+ read(node1, f, node2, config) and
+ nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- readStoreCand(f, unbind(config))
+ nodeCand2IsReadAndStored(f, 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, Content f, Node node2, Configuration config) {
+ store(node1, f, node2, config) 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(f, 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
+ 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
+ // store
+ exists(Node mid, Content f |
+ flowCandFwd(mid, fromArg, argApf, _, config) and
+ storeCand2(mid, f, node, config) and
+ nodeCand2(node, _, _, true, unbind(config)) and
apf.headUsesContent(f)
)
or
+ // read
exists(Content f |
- flowCandFwdRead(f, node, fromArg, config) and
- consCandFwd(f, apf, config) and
- nodeCand2(node, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ flowCandFwdRead(f, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(f, 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
+private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n |
+ flowCandFwd(mid, _, _, apf, config) and
+ storeCand2(mid, f, n, config) and
+ nodeCand2(n, _, _, true, unbind(config)) and
compatibleTypes(apf.getType(), f.getType())
)
}
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
+private predicate flowCandFwdRead(
+ Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+) {
+ exists(Node mid, AccessPathFrontHead apf0 |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ readCand2(mid, f, node, config) and
apf0.headUsesContent(f)
)
}
+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 data can flow from a source to `node` with the given `apf` and
- * from there flow to a sink.
+ * Holds if an argument to `call` is reached in the flow covered by `flowCandFwd`.
*/
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 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 `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(
+ 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
- toReturn = false
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flowCand(mid, _, _, apf, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone()
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- additionalJumpStepExt(node, mid, config) and
- flowCand(mid, _, nil, config) and
+ 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
- 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
+ // store
exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, apf0, config) and
+ flowCandStore(node, f, toReturn, returnApf, apf0, config) and
apf0.headUsesContent(f) and
- consCand(f, apf, config)
+ flowCandConsCand(f, apf, config)
)
or
+ // read
exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, config) and
+ flowCandRead(node, f, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(f, apf0, config) and
apf.headUsesContent(f)
)
+ 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
+ // 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 flowCandRead(
- NodeExt node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
+ Node node, Content f, 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 |
+ readCand2(node, f, 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, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
+ Configuration config
) {
- exists(NodeExt mid |
- storeExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ storeCand2(node, f, mid, config) and
+ flowCand(mid, toReturn, returnApf, apf0, 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
+private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(f, apf, config) and
+ exists(Node n, AccessPathFrontHead apf0 |
+ flowCandFwd(n, _, _, apf0, config) and
apf0.headUsesContent(f) and
- flowCandRead(n, f, _, apf, config)
+ flowCandRead(n, f, _, _, 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
+ TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
TConsCons(Content f1, Content f2, int len) {
- consCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
}
/**
@@ -1778,292 +1611,396 @@ private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
/** Gets the access path obtained by pushing `f` onto `ap`. */
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, 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
+ // store
exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, config) and
+ flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
ap = push(f, ap0)
)
or
+ // read
exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, config) and
- flowConsCandFwd(f, apf, ap, config)
+ flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
+ flowFwdConsCand(f, apf, ap, config)
+ )
+ or
+ // 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, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFront apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
+ exists(Node mid, AccessPathFront apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
flowFwdStore1(mid, f, node, apf0, apf, config)
)
}
pragma[nomagic]
private predicate flowFwdStore0(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, Configuration config
+ Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
) {
- storeExtCand2(mid, f, node, config) and
- flowCand(mid, _, apf0, config)
+ storeCand2(mid, f, node, config) and
+ flowCand(mid, _, _, apf0, config)
}
pragma[noinline]
private predicate flowFwdStore1(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, AccessPathFrontHead apf,
+ Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
flowFwdStore0(mid, f, node, apf0, config) and
- consCand(f, apf0, config) and
+ flowCandConsCand(f, apf0, config) and
apf.headUsesContent(f) and
- flowCand(node, _, apf, unbind(config))
+ flowCand(node, _, _, apf, unbind(config))
}
pragma[nomagic]
private predicate flowFwdRead(
- NodeExt node, Content f, AccessPath ap0, boolean fromArg, Configuration config
+ Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
+ Configuration config
) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- readExtCand2(mid, f, node, config) and
+ exists(Node mid, AccessPathFrontHead apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
+ readCand2(mid, f, node, config) and
apf0.headUsesContent(f) and
- flowCand(node, _, _, unbind(config))
+ flowCand(node, _, _, _, unbind(config))
)
}
pragma[nomagic]
-private predicate flowConsCandFwd(
+private predicate flowFwdConsCand(
Content f, AccessPathFront apf, AccessPath ap, Configuration config
) {
- exists(NodeExt n |
- flowFwd(n, _, apf, ap, config) and
+ exists(Node n |
+ flowFwd(n, _, _, apf, ap, config) and
flowFwdStore1(n, f, _, apf, _, config)
)
}
-/**
- * Holds if data can flow from a source to `node` with the given `ap` and
- * from there flow to a sink.
- */
-private predicate flow(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flow0(node, toReturn, 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
- toReturn = false and
- ap instanceof AccessPathNil
- or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flow(mid, toReturn, 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
- ap instanceof AccessPathNil
- )
- or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flow(mid, _, ap, config) and
- toReturn = false
- )
- 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)
+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 storeFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate flowFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
) {
- storeExtCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, config) and
+ 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 `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(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flow0(node, toReturn, returnAp, ap, config) and
+ flowFwd(node, _, _, _, ap, config)
+}
+
+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(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flow(mid, toReturn, returnAp, ap, config)
+ )
+ or
+ 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(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(Content f |
+ flowStore(f, node, toReturn, returnAp, ap, config) and
+ flowConsCand(f, 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
+ // 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 storeFlowFwd(
+ Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+) {
+ storeCand2(node1, f, node2, config) and
+ flowFwdStore(node2, f, ap, _, _, _, config) and
ap0 = push(f, ap)
}
pragma[nomagic]
private predicate flowStore(
- Content f, NodeExt node, boolean toReturn, AccessPath ap, Configuration config
+ Content f, 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, f, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- readExtCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, config) and
+ readCand2(node1, f, node2, config) and
+ flowFwdRead(node2, f, ap, _, _, config) and
ap0 = pop(f, ap) and
- flowConsCandFwd(f, _, ap0, unbind(config))
+ flowFwdConsCand(f, _, 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)
+ exists(Node n, Node mid |
+ flow(mid, _, _, ap, config) and
+ readFlowFwd(n, f, 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 +2050,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) {
@@ -2293,17 +2230,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 +2256,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(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2331,23 +2269,9 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
}
-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
+ read(node1, f, node2) and
flow(node2, config)
}
@@ -2360,7 +2284,7 @@ private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Conte
pragma[nomagic]
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- storeDirect(node1, f, node2) and
+ store(node1, f, node2) and
flow(node2, config)
}
@@ -2399,11 +2323,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 +2339,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 +2363,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 +2438,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 +2479,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
@@ -2854,7 +2775,7 @@ private module FlowExploration {
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
) {
ap1 = mid.getAp() and
- storeDirect(mid.getNode(), f, node) and
+ store(mid.getNode(), f, node) and
ap2.getHead() = f and
ap2.len() = unbindInt(ap1.len() + 1) and
compatibleTypes(ap1.getType(), f.getType())
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..9587ea5f274 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
@@ -251,15 +251,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 +289,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
+ nodeCandFwd1IsStored(f, config) and
not inBarrier(node, config)
)
or
@@ -317,13 +313,34 @@ 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 f, Node node, boolean fromArg, Configuration config) {
+ exists(Node mid |
+ nodeCandFwd1(mid, fromArg, config) and
+ read(mid, f, node)
+ )
+}
+
+/**
+ * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
+ exists(Node mid, Node node |
+ not fullBarrier(node, config) and
+ useFieldFlow(config) and
+ nodeCandFwd1(mid, config) and
+ store(mid, f, node)
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1ReturnPosition(
ReturnPosition pos, boolean fromArg, Configuration config
@@ -335,43 +352,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 +368,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 +379,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) {
@@ -435,55 +422,43 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
// store
exists(Content f |
nodeCand1Store(f, node, toReturn, config) and
- readCand1(f, config)
+ nodeCand1IsRead(f, config)
)
or
// read
exists(Node mid, Content f |
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ read(node, f, mid) and
+ nodeCandFwd1IsStored(f, 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`.
*/
pragma[nomagic]
-private predicate readCand1(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content f, 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, f, mid) and
+ nodeCandFwd1IsStored(f, unbind(config)) and
nodeCand1(mid, _, config)
)
}
@@ -492,8 +467,8 @@ pragma[nomagic]
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
exists(Node mid |
nodeCand1(mid, toReturn, config) and
- storeCandFwd1(f, unbind(config)) and
- storeDirect(node, f, mid)
+ nodeCandFwd1IsStored(f, unbind(config)) and
+ store(node, f, mid)
)
}
@@ -501,13 +476,29 @@ private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configu
* Holds if `f` 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
+private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
+ nodeCand1IsRead(f, conf) and
nodeCand1Store(f, _, _, 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 +506,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 +543,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 +557,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)
@@ -576,419 +570,73 @@ private predicate parameterThroughFlowCand(ParameterNode p, Configuration config
pragma[nomagic]
private predicate store(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
+ nodeCand1IsReadAndStored(f, config) and
nodeCand1(n2, unbind(config)) and
- (
- storeDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentSome(f), n2)
- )
+ store(n1, f, n2)
}
pragma[nomagic]
private predicate read(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
+ nodeCand1IsReadAndStored(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)
- )
+ read(n1, f, n2)
}
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 predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ localFlowStep(node1, node2, 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[noinline]
+private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ additionalLocalFlowStep(node1, node2, 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
- 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)
- )
-}
-
-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
+private predicate viableReturnPosOutNodeCand1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
) {
- argumentFlowsThrough(arg.getNode(), out.getNode(), _, t, TSummaryTaint(), config)
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, 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[noinline]
-private ReturnPosition getReturnPosition1(ReturnNodeExt node, Configuration config) {
- result = getReturnPosition(node) and
- nodeCand1(node, 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 +647,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,117 +659,126 @@ 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, Content f |
+ nodeCandFwd2(mid, fromArg, argStored, _, config) and
+ store(mid, f, node, config) and
stored = true
)
or
// read
exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, config) and
- storeCandFwd2(f, stored, config)
+ nodeCandFwd2Read(f, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(f, 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)
+ )
)
)
}
@@ -1130,86 +787,148 @@ private predicate nodeCandFwd2(NodeExt node, boolean fromArg, boolean stored, Co
* Holds if `f` 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 f, 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
+ store(mid, f, 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 f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+) {
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, true, config) and
+ read(mid, f, 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)
+ nodeCand2Store(f, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(f, 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 f, boolean read0 |
+ read(node, f, mid, config) and
+ nodeCandFwd2IsStored(f, 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()
)
}
@@ -1217,32 +936,36 @@ private predicate nodeCand2(NodeExt node, boolean toReturn, boolean read, Config
* Holds if `f` 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 f, 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, f, mid, config) and
+ nodeCandFwd2IsStored(f, 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 f, 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))
+ exists(Node mid |
+ store(node, f, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, true, config) and
+ nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
+/**
+ * Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
+ */
pragma[nomagic]
-private predicate storeCand2(Content f, boolean stored, Configuration conf) {
- exists(NodeExt node |
- nodeCand2Store(f, node, _, stored, conf) and
- nodeCand2(node, _, stored, conf)
+private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
+ exists(Node node |
+ nodeCand2Store(f, node, _, _, stored, conf) and
+ nodeCand2(node, _, _, stored, conf)
)
}
@@ -1251,29 +974,76 @@ private predicate storeCand2(Content f, boolean stored, Configuration conf) {
* covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate readStoreCand(Content f, Configuration conf) {
+private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
exists(boolean apNonEmpty |
- storeCand2(f, apNonEmpty, conf) and
- readCand2(f, apNonEmpty, conf)
+ nodeCand2IsStored(f, apNonEmpty, conf) and
+ nodeCand2IsRead(f, apNonEmpty, conf)
)
}
-private predicate nodeCand2(NodeExt node, Configuration config) { nodeCand2(node, _, _, config) }
+pragma[nomagic]
+private predicate nodeCand2Out(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
+) {
+ 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 flowOutOfCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate nodeCand2In(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
) {
- flowOutOfCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ 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 +1054,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 +1072,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 +1086,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 +1109,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,307 +1146,365 @@ 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 f, Node node2, Configuration config) {
+ read(node1, f, node2, config) and
+ nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- readStoreCand(f, unbind(config))
+ nodeCand2IsReadAndStored(f, 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, Content f, Node node2, Configuration config) {
+ store(node1, f, node2, config) 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(f, 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
+ 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
+ // store
+ exists(Node mid, Content f |
+ flowCandFwd(mid, fromArg, argApf, _, config) and
+ storeCand2(mid, f, node, config) and
+ nodeCand2(node, _, _, true, unbind(config)) and
apf.headUsesContent(f)
)
or
+ // read
exists(Content f |
- flowCandFwdRead(f, node, fromArg, config) and
- consCandFwd(f, apf, config) and
- nodeCand2(node, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ flowCandFwdRead(f, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(f, 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
+private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n |
+ flowCandFwd(mid, _, _, apf, config) and
+ storeCand2(mid, f, n, config) and
+ nodeCand2(n, _, _, true, unbind(config)) and
compatibleTypes(apf.getType(), f.getType())
)
}
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
+private predicate flowCandFwdRead(
+ Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+) {
+ exists(Node mid, AccessPathFrontHead apf0 |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ readCand2(mid, f, node, config) and
apf0.headUsesContent(f)
)
}
+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 data can flow from a source to `node` with the given `apf` and
- * from there flow to a sink.
+ * Holds if an argument to `call` is reached in the flow covered by `flowCandFwd`.
*/
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 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 `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(
+ 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
- toReturn = false
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flowCand(mid, _, _, apf, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone()
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- additionalJumpStepExt(node, mid, config) and
- flowCand(mid, _, nil, config) and
+ 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
- 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
+ // store
exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, apf0, config) and
+ flowCandStore(node, f, toReturn, returnApf, apf0, config) and
apf0.headUsesContent(f) and
- consCand(f, apf, config)
+ flowCandConsCand(f, apf, config)
)
or
+ // read
exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, config) and
+ flowCandRead(node, f, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(f, apf0, config) and
apf.headUsesContent(f)
)
+ 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
+ // 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 flowCandRead(
- NodeExt node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
+ Node node, Content f, 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 |
+ readCand2(node, f, 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, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
+ Configuration config
) {
- exists(NodeExt mid |
- storeExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ storeCand2(node, f, mid, config) and
+ flowCand(mid, toReturn, returnApf, apf0, 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
+private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(f, apf, config) and
+ exists(Node n, AccessPathFrontHead apf0 |
+ flowCandFwd(n, _, _, apf0, config) and
apf0.headUsesContent(f) and
- flowCandRead(n, f, _, apf, config)
+ flowCandRead(n, f, _, _, 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
+ TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
TConsCons(Content f1, Content f2, int len) {
- consCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
}
/**
@@ -1778,292 +1611,396 @@ private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
/** Gets the access path obtained by pushing `f` onto `ap`. */
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, 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
+ // store
exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, config) and
+ flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
ap = push(f, ap0)
)
or
+ // read
exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, config) and
- flowConsCandFwd(f, apf, ap, config)
+ flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
+ flowFwdConsCand(f, apf, ap, config)
+ )
+ or
+ // 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, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFront apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
+ exists(Node mid, AccessPathFront apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
flowFwdStore1(mid, f, node, apf0, apf, config)
)
}
pragma[nomagic]
private predicate flowFwdStore0(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, Configuration config
+ Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
) {
- storeExtCand2(mid, f, node, config) and
- flowCand(mid, _, apf0, config)
+ storeCand2(mid, f, node, config) and
+ flowCand(mid, _, _, apf0, config)
}
pragma[noinline]
private predicate flowFwdStore1(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, AccessPathFrontHead apf,
+ Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
flowFwdStore0(mid, f, node, apf0, config) and
- consCand(f, apf0, config) and
+ flowCandConsCand(f, apf0, config) and
apf.headUsesContent(f) and
- flowCand(node, _, apf, unbind(config))
+ flowCand(node, _, _, apf, unbind(config))
}
pragma[nomagic]
private predicate flowFwdRead(
- NodeExt node, Content f, AccessPath ap0, boolean fromArg, Configuration config
+ Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
+ Configuration config
) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- readExtCand2(mid, f, node, config) and
+ exists(Node mid, AccessPathFrontHead apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
+ readCand2(mid, f, node, config) and
apf0.headUsesContent(f) and
- flowCand(node, _, _, unbind(config))
+ flowCand(node, _, _, _, unbind(config))
)
}
pragma[nomagic]
-private predicate flowConsCandFwd(
+private predicate flowFwdConsCand(
Content f, AccessPathFront apf, AccessPath ap, Configuration config
) {
- exists(NodeExt n |
- flowFwd(n, _, apf, ap, config) and
+ exists(Node n |
+ flowFwd(n, _, _, apf, ap, config) and
flowFwdStore1(n, f, _, apf, _, config)
)
}
-/**
- * Holds if data can flow from a source to `node` with the given `ap` and
- * from there flow to a sink.
- */
-private predicate flow(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flow0(node, toReturn, 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
- toReturn = false and
- ap instanceof AccessPathNil
- or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flow(mid, toReturn, 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
- ap instanceof AccessPathNil
- )
- or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flow(mid, _, ap, config) and
- toReturn = false
- )
- 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)
+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 storeFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate flowFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
) {
- storeExtCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, config) and
+ 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 `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(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flow0(node, toReturn, returnAp, ap, config) and
+ flowFwd(node, _, _, _, ap, config)
+}
+
+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(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flow(mid, toReturn, returnAp, ap, config)
+ )
+ or
+ 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(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(Content f |
+ flowStore(f, node, toReturn, returnAp, ap, config) and
+ flowConsCand(f, 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
+ // 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 storeFlowFwd(
+ Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+) {
+ storeCand2(node1, f, node2, config) and
+ flowFwdStore(node2, f, ap, _, _, _, config) and
ap0 = push(f, ap)
}
pragma[nomagic]
private predicate flowStore(
- Content f, NodeExt node, boolean toReturn, AccessPath ap, Configuration config
+ Content f, 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, f, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- readExtCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, config) and
+ readCand2(node1, f, node2, config) and
+ flowFwdRead(node2, f, ap, _, _, config) and
ap0 = pop(f, ap) and
- flowConsCandFwd(f, _, ap0, unbind(config))
+ flowFwdConsCand(f, _, 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)
+ exists(Node n, Node mid |
+ flow(mid, _, _, ap, config) and
+ readFlowFwd(n, f, 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 +2050,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) {
@@ -2293,17 +2230,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 +2256,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(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2331,23 +2269,9 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
}
-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
+ read(node1, f, node2) and
flow(node2, config)
}
@@ -2360,7 +2284,7 @@ private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Conte
pragma[nomagic]
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- storeDirect(node1, f, node2) and
+ store(node1, f, node2) and
flow(node2, config)
}
@@ -2399,11 +2323,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 +2339,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 +2363,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 +2438,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 +2479,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
@@ -2854,7 +2775,7 @@ private module FlowExploration {
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
) {
ap1 = mid.getAp() and
- storeDirect(mid.getNode(), f, node) and
+ store(mid.getNode(), f, node) and
ap2.getHead() = f and
ap2.len() = unbindInt(ap1.len() + 1) and
compatibleTypes(ap1.getType(), f.getType())
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..9587ea5f274 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
@@ -251,15 +251,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 +289,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
+ nodeCandFwd1IsStored(f, config) and
not inBarrier(node, config)
)
or
@@ -317,13 +313,34 @@ 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 f, Node node, boolean fromArg, Configuration config) {
+ exists(Node mid |
+ nodeCandFwd1(mid, fromArg, config) and
+ read(mid, f, node)
+ )
+}
+
+/**
+ * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
+ exists(Node mid, Node node |
+ not fullBarrier(node, config) and
+ useFieldFlow(config) and
+ nodeCandFwd1(mid, config) and
+ store(mid, f, node)
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1ReturnPosition(
ReturnPosition pos, boolean fromArg, Configuration config
@@ -335,43 +352,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 +368,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 +379,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) {
@@ -435,55 +422,43 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
// store
exists(Content f |
nodeCand1Store(f, node, toReturn, config) and
- readCand1(f, config)
+ nodeCand1IsRead(f, config)
)
or
// read
exists(Node mid, Content f |
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ read(node, f, mid) and
+ nodeCandFwd1IsStored(f, 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`.
*/
pragma[nomagic]
-private predicate readCand1(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content f, 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, f, mid) and
+ nodeCandFwd1IsStored(f, unbind(config)) and
nodeCand1(mid, _, config)
)
}
@@ -492,8 +467,8 @@ pragma[nomagic]
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
exists(Node mid |
nodeCand1(mid, toReturn, config) and
- storeCandFwd1(f, unbind(config)) and
- storeDirect(node, f, mid)
+ nodeCandFwd1IsStored(f, unbind(config)) and
+ store(node, f, mid)
)
}
@@ -501,13 +476,29 @@ private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configu
* Holds if `f` 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
+private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
+ nodeCand1IsRead(f, conf) and
nodeCand1Store(f, _, _, 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 +506,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 +543,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 +557,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)
@@ -576,419 +570,73 @@ private predicate parameterThroughFlowCand(ParameterNode p, Configuration config
pragma[nomagic]
private predicate store(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
+ nodeCand1IsReadAndStored(f, config) and
nodeCand1(n2, unbind(config)) and
- (
- storeDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentSome(f), n2)
- )
+ store(n1, f, n2)
}
pragma[nomagic]
private predicate read(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
+ nodeCand1IsReadAndStored(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)
- )
+ read(n1, f, n2)
}
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 predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ localFlowStep(node1, node2, 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[noinline]
+private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ additionalLocalFlowStep(node1, node2, 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
- 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)
- )
-}
-
-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
+private predicate viableReturnPosOutNodeCand1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
) {
- argumentFlowsThrough(arg.getNode(), out.getNode(), _, t, TSummaryTaint(), config)
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, 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[noinline]
-private ReturnPosition getReturnPosition1(ReturnNodeExt node, Configuration config) {
- result = getReturnPosition(node) and
- nodeCand1(node, 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 +647,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,117 +659,126 @@ 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, Content f |
+ nodeCandFwd2(mid, fromArg, argStored, _, config) and
+ store(mid, f, node, config) and
stored = true
)
or
// read
exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, config) and
- storeCandFwd2(f, stored, config)
+ nodeCandFwd2Read(f, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(f, 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)
+ )
)
)
}
@@ -1130,86 +787,148 @@ private predicate nodeCandFwd2(NodeExt node, boolean fromArg, boolean stored, Co
* Holds if `f` 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 f, 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
+ store(mid, f, 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 f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+) {
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, true, config) and
+ read(mid, f, 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)
+ nodeCand2Store(f, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(f, 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 f, boolean read0 |
+ read(node, f, mid, config) and
+ nodeCandFwd2IsStored(f, 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()
)
}
@@ -1217,32 +936,36 @@ private predicate nodeCand2(NodeExt node, boolean toReturn, boolean read, Config
* Holds if `f` 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 f, 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, f, mid, config) and
+ nodeCandFwd2IsStored(f, 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 f, 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))
+ exists(Node mid |
+ store(node, f, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, true, config) and
+ nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
+/**
+ * Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
+ */
pragma[nomagic]
-private predicate storeCand2(Content f, boolean stored, Configuration conf) {
- exists(NodeExt node |
- nodeCand2Store(f, node, _, stored, conf) and
- nodeCand2(node, _, stored, conf)
+private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
+ exists(Node node |
+ nodeCand2Store(f, node, _, _, stored, conf) and
+ nodeCand2(node, _, _, stored, conf)
)
}
@@ -1251,29 +974,76 @@ private predicate storeCand2(Content f, boolean stored, Configuration conf) {
* covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate readStoreCand(Content f, Configuration conf) {
+private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
exists(boolean apNonEmpty |
- storeCand2(f, apNonEmpty, conf) and
- readCand2(f, apNonEmpty, conf)
+ nodeCand2IsStored(f, apNonEmpty, conf) and
+ nodeCand2IsRead(f, apNonEmpty, conf)
)
}
-private predicate nodeCand2(NodeExt node, Configuration config) { nodeCand2(node, _, _, config) }
+pragma[nomagic]
+private predicate nodeCand2Out(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
+) {
+ 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 flowOutOfCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate nodeCand2In(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
) {
- flowOutOfCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ 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 +1054,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 +1072,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 +1086,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 +1109,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,307 +1146,365 @@ 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 f, Node node2, Configuration config) {
+ read(node1, f, node2, config) and
+ nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- readStoreCand(f, unbind(config))
+ nodeCand2IsReadAndStored(f, 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, Content f, Node node2, Configuration config) {
+ store(node1, f, node2, config) 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(f, 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
+ 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
+ // store
+ exists(Node mid, Content f |
+ flowCandFwd(mid, fromArg, argApf, _, config) and
+ storeCand2(mid, f, node, config) and
+ nodeCand2(node, _, _, true, unbind(config)) and
apf.headUsesContent(f)
)
or
+ // read
exists(Content f |
- flowCandFwdRead(f, node, fromArg, config) and
- consCandFwd(f, apf, config) and
- nodeCand2(node, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ flowCandFwdRead(f, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(f, 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
+private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n |
+ flowCandFwd(mid, _, _, apf, config) and
+ storeCand2(mid, f, n, config) and
+ nodeCand2(n, _, _, true, unbind(config)) and
compatibleTypes(apf.getType(), f.getType())
)
}
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
+private predicate flowCandFwdRead(
+ Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+) {
+ exists(Node mid, AccessPathFrontHead apf0 |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ readCand2(mid, f, node, config) and
apf0.headUsesContent(f)
)
}
+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 data can flow from a source to `node` with the given `apf` and
- * from there flow to a sink.
+ * Holds if an argument to `call` is reached in the flow covered by `flowCandFwd`.
*/
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 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 `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(
+ 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
- toReturn = false
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flowCand(mid, _, _, apf, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone()
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- additionalJumpStepExt(node, mid, config) and
- flowCand(mid, _, nil, config) and
+ 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
- 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
+ // store
exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, apf0, config) and
+ flowCandStore(node, f, toReturn, returnApf, apf0, config) and
apf0.headUsesContent(f) and
- consCand(f, apf, config)
+ flowCandConsCand(f, apf, config)
)
or
+ // read
exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, config) and
+ flowCandRead(node, f, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(f, apf0, config) and
apf.headUsesContent(f)
)
+ 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
+ // 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 flowCandRead(
- NodeExt node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
+ Node node, Content f, 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 |
+ readCand2(node, f, 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, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
+ Configuration config
) {
- exists(NodeExt mid |
- storeExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ storeCand2(node, f, mid, config) and
+ flowCand(mid, toReturn, returnApf, apf0, 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
+private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(f, apf, config) and
+ exists(Node n, AccessPathFrontHead apf0 |
+ flowCandFwd(n, _, _, apf0, config) and
apf0.headUsesContent(f) and
- flowCandRead(n, f, _, apf, config)
+ flowCandRead(n, f, _, _, 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
+ TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
TConsCons(Content f1, Content f2, int len) {
- consCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
}
/**
@@ -1778,292 +1611,396 @@ private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
/** Gets the access path obtained by pushing `f` onto `ap`. */
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, 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
+ // store
exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, config) and
+ flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
ap = push(f, ap0)
)
or
+ // read
exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, config) and
- flowConsCandFwd(f, apf, ap, config)
+ flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
+ flowFwdConsCand(f, apf, ap, config)
+ )
+ or
+ // 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, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFront apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
+ exists(Node mid, AccessPathFront apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
flowFwdStore1(mid, f, node, apf0, apf, config)
)
}
pragma[nomagic]
private predicate flowFwdStore0(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, Configuration config
+ Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
) {
- storeExtCand2(mid, f, node, config) and
- flowCand(mid, _, apf0, config)
+ storeCand2(mid, f, node, config) and
+ flowCand(mid, _, _, apf0, config)
}
pragma[noinline]
private predicate flowFwdStore1(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, AccessPathFrontHead apf,
+ Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
flowFwdStore0(mid, f, node, apf0, config) and
- consCand(f, apf0, config) and
+ flowCandConsCand(f, apf0, config) and
apf.headUsesContent(f) and
- flowCand(node, _, apf, unbind(config))
+ flowCand(node, _, _, apf, unbind(config))
}
pragma[nomagic]
private predicate flowFwdRead(
- NodeExt node, Content f, AccessPath ap0, boolean fromArg, Configuration config
+ Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
+ Configuration config
) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- readExtCand2(mid, f, node, config) and
+ exists(Node mid, AccessPathFrontHead apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
+ readCand2(mid, f, node, config) and
apf0.headUsesContent(f) and
- flowCand(node, _, _, unbind(config))
+ flowCand(node, _, _, _, unbind(config))
)
}
pragma[nomagic]
-private predicate flowConsCandFwd(
+private predicate flowFwdConsCand(
Content f, AccessPathFront apf, AccessPath ap, Configuration config
) {
- exists(NodeExt n |
- flowFwd(n, _, apf, ap, config) and
+ exists(Node n |
+ flowFwd(n, _, _, apf, ap, config) and
flowFwdStore1(n, f, _, apf, _, config)
)
}
-/**
- * Holds if data can flow from a source to `node` with the given `ap` and
- * from there flow to a sink.
- */
-private predicate flow(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flow0(node, toReturn, 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
- toReturn = false and
- ap instanceof AccessPathNil
- or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flow(mid, toReturn, 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
- ap instanceof AccessPathNil
- )
- or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flow(mid, _, ap, config) and
- toReturn = false
- )
- 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)
+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 storeFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate flowFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
) {
- storeExtCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, config) and
+ 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 `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(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flow0(node, toReturn, returnAp, ap, config) and
+ flowFwd(node, _, _, _, ap, config)
+}
+
+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(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flow(mid, toReturn, returnAp, ap, config)
+ )
+ or
+ 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(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(Content f |
+ flowStore(f, node, toReturn, returnAp, ap, config) and
+ flowConsCand(f, 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
+ // 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 storeFlowFwd(
+ Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+) {
+ storeCand2(node1, f, node2, config) and
+ flowFwdStore(node2, f, ap, _, _, _, config) and
ap0 = push(f, ap)
}
pragma[nomagic]
private predicate flowStore(
- Content f, NodeExt node, boolean toReturn, AccessPath ap, Configuration config
+ Content f, 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, f, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- readExtCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, config) and
+ readCand2(node1, f, node2, config) and
+ flowFwdRead(node2, f, ap, _, _, config) and
ap0 = pop(f, ap) and
- flowConsCandFwd(f, _, ap0, unbind(config))
+ flowFwdConsCand(f, _, 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)
+ exists(Node n, Node mid |
+ flow(mid, _, _, ap, config) and
+ readFlowFwd(n, f, 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 +2050,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) {
@@ -2293,17 +2230,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 +2256,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(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2331,23 +2269,9 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
}
-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
+ read(node1, f, node2) and
flow(node2, config)
}
@@ -2360,7 +2284,7 @@ private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Conte
pragma[nomagic]
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- storeDirect(node1, f, node2) and
+ store(node1, f, node2) and
flow(node2, config)
}
@@ -2399,11 +2323,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 +2339,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 +2363,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 +2438,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 +2479,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
@@ -2854,7 +2775,7 @@ private module FlowExploration {
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
) {
ap1 = mid.getAp() and
- storeDirect(mid.getNode(), f, node) and
+ store(mid.getNode(), f, node) and
ap2.getHead() = f and
ap2.len() = unbindInt(ap1.len() + 1) and
compatibleTypes(ap1.getType(), f.getType())
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..9587ea5f274 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
@@ -251,15 +251,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 +289,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
+ nodeCandFwd1IsStored(f, config) and
not inBarrier(node, config)
)
or
@@ -317,13 +313,34 @@ 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 f, Node node, boolean fromArg, Configuration config) {
+ exists(Node mid |
+ nodeCandFwd1(mid, fromArg, config) and
+ read(mid, f, node)
+ )
+}
+
+/**
+ * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
+ exists(Node mid, Node node |
+ not fullBarrier(node, config) and
+ useFieldFlow(config) and
+ nodeCandFwd1(mid, config) and
+ store(mid, f, node)
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1ReturnPosition(
ReturnPosition pos, boolean fromArg, Configuration config
@@ -335,43 +352,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 +368,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 +379,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) {
@@ -435,55 +422,43 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
// store
exists(Content f |
nodeCand1Store(f, node, toReturn, config) and
- readCand1(f, config)
+ nodeCand1IsRead(f, config)
)
or
// read
exists(Node mid, Content f |
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ read(node, f, mid) and
+ nodeCandFwd1IsStored(f, 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`.
*/
pragma[nomagic]
-private predicate readCand1(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content f, 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, f, mid) and
+ nodeCandFwd1IsStored(f, unbind(config)) and
nodeCand1(mid, _, config)
)
}
@@ -492,8 +467,8 @@ pragma[nomagic]
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
exists(Node mid |
nodeCand1(mid, toReturn, config) and
- storeCandFwd1(f, unbind(config)) and
- storeDirect(node, f, mid)
+ nodeCandFwd1IsStored(f, unbind(config)) and
+ store(node, f, mid)
)
}
@@ -501,13 +476,29 @@ private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configu
* Holds if `f` 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
+private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
+ nodeCand1IsRead(f, conf) and
nodeCand1Store(f, _, _, 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 +506,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 +543,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 +557,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)
@@ -576,419 +570,73 @@ private predicate parameterThroughFlowCand(ParameterNode p, Configuration config
pragma[nomagic]
private predicate store(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
+ nodeCand1IsReadAndStored(f, config) and
nodeCand1(n2, unbind(config)) and
- (
- storeDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentSome(f), n2)
- )
+ store(n1, f, n2)
}
pragma[nomagic]
private predicate read(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
+ nodeCand1IsReadAndStored(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)
- )
+ read(n1, f, n2)
}
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 predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ localFlowStep(node1, node2, 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[noinline]
+private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ additionalLocalFlowStep(node1, node2, 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
- 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)
- )
-}
-
-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
+private predicate viableReturnPosOutNodeCand1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
) {
- argumentFlowsThrough(arg.getNode(), out.getNode(), _, t, TSummaryTaint(), config)
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, 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[noinline]
-private ReturnPosition getReturnPosition1(ReturnNodeExt node, Configuration config) {
- result = getReturnPosition(node) and
- nodeCand1(node, 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 +647,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,117 +659,126 @@ 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, Content f |
+ nodeCandFwd2(mid, fromArg, argStored, _, config) and
+ store(mid, f, node, config) and
stored = true
)
or
// read
exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, config) and
- storeCandFwd2(f, stored, config)
+ nodeCandFwd2Read(f, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(f, 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)
+ )
)
)
}
@@ -1130,86 +787,148 @@ private predicate nodeCandFwd2(NodeExt node, boolean fromArg, boolean stored, Co
* Holds if `f` 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 f, 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
+ store(mid, f, 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 f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+) {
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, true, config) and
+ read(mid, f, 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)
+ nodeCand2Store(f, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(f, 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 f, boolean read0 |
+ read(node, f, mid, config) and
+ nodeCandFwd2IsStored(f, 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()
)
}
@@ -1217,32 +936,36 @@ private predicate nodeCand2(NodeExt node, boolean toReturn, boolean read, Config
* Holds if `f` 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 f, 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, f, mid, config) and
+ nodeCandFwd2IsStored(f, 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 f, 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))
+ exists(Node mid |
+ store(node, f, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, true, config) and
+ nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
+/**
+ * Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
+ */
pragma[nomagic]
-private predicate storeCand2(Content f, boolean stored, Configuration conf) {
- exists(NodeExt node |
- nodeCand2Store(f, node, _, stored, conf) and
- nodeCand2(node, _, stored, conf)
+private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
+ exists(Node node |
+ nodeCand2Store(f, node, _, _, stored, conf) and
+ nodeCand2(node, _, _, stored, conf)
)
}
@@ -1251,29 +974,76 @@ private predicate storeCand2(Content f, boolean stored, Configuration conf) {
* covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate readStoreCand(Content f, Configuration conf) {
+private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
exists(boolean apNonEmpty |
- storeCand2(f, apNonEmpty, conf) and
- readCand2(f, apNonEmpty, conf)
+ nodeCand2IsStored(f, apNonEmpty, conf) and
+ nodeCand2IsRead(f, apNonEmpty, conf)
)
}
-private predicate nodeCand2(NodeExt node, Configuration config) { nodeCand2(node, _, _, config) }
+pragma[nomagic]
+private predicate nodeCand2Out(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
+) {
+ 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 flowOutOfCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate nodeCand2In(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
) {
- flowOutOfCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ 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 +1054,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 +1072,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 +1086,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 +1109,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,307 +1146,365 @@ 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 f, Node node2, Configuration config) {
+ read(node1, f, node2, config) and
+ nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- readStoreCand(f, unbind(config))
+ nodeCand2IsReadAndStored(f, 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, Content f, Node node2, Configuration config) {
+ store(node1, f, node2, config) 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(f, 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
+ 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
+ // store
+ exists(Node mid, Content f |
+ flowCandFwd(mid, fromArg, argApf, _, config) and
+ storeCand2(mid, f, node, config) and
+ nodeCand2(node, _, _, true, unbind(config)) and
apf.headUsesContent(f)
)
or
+ // read
exists(Content f |
- flowCandFwdRead(f, node, fromArg, config) and
- consCandFwd(f, apf, config) and
- nodeCand2(node, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ flowCandFwdRead(f, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(f, 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
+private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n |
+ flowCandFwd(mid, _, _, apf, config) and
+ storeCand2(mid, f, n, config) and
+ nodeCand2(n, _, _, true, unbind(config)) and
compatibleTypes(apf.getType(), f.getType())
)
}
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
+private predicate flowCandFwdRead(
+ Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+) {
+ exists(Node mid, AccessPathFrontHead apf0 |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ readCand2(mid, f, node, config) and
apf0.headUsesContent(f)
)
}
+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 data can flow from a source to `node` with the given `apf` and
- * from there flow to a sink.
+ * Holds if an argument to `call` is reached in the flow covered by `flowCandFwd`.
*/
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 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 `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(
+ 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
- toReturn = false
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flowCand(mid, _, _, apf, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone()
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- additionalJumpStepExt(node, mid, config) and
- flowCand(mid, _, nil, config) and
+ 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
- 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
+ // store
exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, apf0, config) and
+ flowCandStore(node, f, toReturn, returnApf, apf0, config) and
apf0.headUsesContent(f) and
- consCand(f, apf, config)
+ flowCandConsCand(f, apf, config)
)
or
+ // read
exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, config) and
+ flowCandRead(node, f, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(f, apf0, config) and
apf.headUsesContent(f)
)
+ 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
+ // 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 flowCandRead(
- NodeExt node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
+ Node node, Content f, 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 |
+ readCand2(node, f, 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, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
+ Configuration config
) {
- exists(NodeExt mid |
- storeExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ storeCand2(node, f, mid, config) and
+ flowCand(mid, toReturn, returnApf, apf0, 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
+private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(f, apf, config) and
+ exists(Node n, AccessPathFrontHead apf0 |
+ flowCandFwd(n, _, _, apf0, config) and
apf0.headUsesContent(f) and
- flowCandRead(n, f, _, apf, config)
+ flowCandRead(n, f, _, _, 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
+ TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
TConsCons(Content f1, Content f2, int len) {
- consCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
}
/**
@@ -1778,292 +1611,396 @@ private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
/** Gets the access path obtained by pushing `f` onto `ap`. */
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, 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
+ // store
exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, config) and
+ flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
ap = push(f, ap0)
)
or
+ // read
exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, config) and
- flowConsCandFwd(f, apf, ap, config)
+ flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
+ flowFwdConsCand(f, apf, ap, config)
+ )
+ or
+ // 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, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFront apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
+ exists(Node mid, AccessPathFront apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
flowFwdStore1(mid, f, node, apf0, apf, config)
)
}
pragma[nomagic]
private predicate flowFwdStore0(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, Configuration config
+ Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
) {
- storeExtCand2(mid, f, node, config) and
- flowCand(mid, _, apf0, config)
+ storeCand2(mid, f, node, config) and
+ flowCand(mid, _, _, apf0, config)
}
pragma[noinline]
private predicate flowFwdStore1(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, AccessPathFrontHead apf,
+ Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
flowFwdStore0(mid, f, node, apf0, config) and
- consCand(f, apf0, config) and
+ flowCandConsCand(f, apf0, config) and
apf.headUsesContent(f) and
- flowCand(node, _, apf, unbind(config))
+ flowCand(node, _, _, apf, unbind(config))
}
pragma[nomagic]
private predicate flowFwdRead(
- NodeExt node, Content f, AccessPath ap0, boolean fromArg, Configuration config
+ Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
+ Configuration config
) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- readExtCand2(mid, f, node, config) and
+ exists(Node mid, AccessPathFrontHead apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
+ readCand2(mid, f, node, config) and
apf0.headUsesContent(f) and
- flowCand(node, _, _, unbind(config))
+ flowCand(node, _, _, _, unbind(config))
)
}
pragma[nomagic]
-private predicate flowConsCandFwd(
+private predicate flowFwdConsCand(
Content f, AccessPathFront apf, AccessPath ap, Configuration config
) {
- exists(NodeExt n |
- flowFwd(n, _, apf, ap, config) and
+ exists(Node n |
+ flowFwd(n, _, _, apf, ap, config) and
flowFwdStore1(n, f, _, apf, _, config)
)
}
-/**
- * Holds if data can flow from a source to `node` with the given `ap` and
- * from there flow to a sink.
- */
-private predicate flow(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flow0(node, toReturn, 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
- toReturn = false and
- ap instanceof AccessPathNil
- or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flow(mid, toReturn, 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
- ap instanceof AccessPathNil
- )
- or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flow(mid, _, ap, config) and
- toReturn = false
- )
- 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)
+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 storeFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate flowFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
) {
- storeExtCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, config) and
+ 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 `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(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flow0(node, toReturn, returnAp, ap, config) and
+ flowFwd(node, _, _, _, ap, config)
+}
+
+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(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flow(mid, toReturn, returnAp, ap, config)
+ )
+ or
+ 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(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(Content f |
+ flowStore(f, node, toReturn, returnAp, ap, config) and
+ flowConsCand(f, 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
+ // 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 storeFlowFwd(
+ Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+) {
+ storeCand2(node1, f, node2, config) and
+ flowFwdStore(node2, f, ap, _, _, _, config) and
ap0 = push(f, ap)
}
pragma[nomagic]
private predicate flowStore(
- Content f, NodeExt node, boolean toReturn, AccessPath ap, Configuration config
+ Content f, 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, f, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- readExtCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, config) and
+ readCand2(node1, f, node2, config) and
+ flowFwdRead(node2, f, ap, _, _, config) and
ap0 = pop(f, ap) and
- flowConsCandFwd(f, _, ap0, unbind(config))
+ flowFwdConsCand(f, _, 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)
+ exists(Node n, Node mid |
+ flow(mid, _, _, ap, config) and
+ readFlowFwd(n, f, 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 +2050,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) {
@@ -2293,17 +2230,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 +2256,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(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2331,23 +2269,9 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
}
-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
+ read(node1, f, node2) and
flow(node2, config)
}
@@ -2360,7 +2284,7 @@ private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Conte
pragma[nomagic]
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- storeDirect(node1, f, node2) and
+ store(node1, f, node2) and
flow(node2, config)
}
@@ -2399,11 +2323,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 +2339,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 +2363,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 +2438,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 +2479,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
@@ -2854,7 +2775,7 @@ private module FlowExploration {
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
) {
ap1 = mid.getAp() and
- storeDirect(mid.getNode(), f, node) and
+ store(mid.getNode(), f, node) and
ap2.getHead() = f and
ap2.len() = unbindInt(ap1.len() + 1) and
compatibleTypes(ap1.getType(), f.getType())
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..852f54974e2 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,140 @@ private module Cached {
/**
* The final flow-through calculation:
*
- * - Input/output access paths are abstracted with a `ContentOption` parameter
+ * - Input access paths are abstracted with a `ContentOption` parameter
* that represents the head of the access path. `TContentNone()` means that
* the access path is unrestricted.
* - 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.
*
* `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 any).
*/
- private predicate parameterValueFlow(
- ParameterNode p, Node node, ContentOption contentIn, ContentOption contentOut
- ) {
- parameterValueFlow0(p, node, contentIn, contentOut) and
+ predicate parameterValueFlow(ParameterNode p, Node node, ContentOption contentIn) {
+ parameterValueFlow0(p, node, contentIn) and
if node instanceof CastingNode
then
// normal flow through
contentIn = TContentNone() and
- contentOut = TContentNone() 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))
- )
else any()
}
pragma[nomagic]
- private predicate parameterValueFlow0(
- ParameterNode p, Node node, ContentOption contentIn, ContentOption contentOut
- ) {
+ private predicate parameterValueFlow0(ParameterNode p, Node node, ContentOption contentIn) {
p = node and
Cand::cand(p, _) and
- contentIn = TContentNone() and
- contentOut = TContentNone()
+ contentIn = TContentNone()
or
// local flow
exists(Node mid |
- parameterValueFlow(p, mid, contentIn, contentOut) and
+ parameterValueFlow(p, mid, contentIn) 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()
- )
- 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())
+ parameterValueFlow(p, mid, TContentNone()) and
+ readStep(mid, f, node) and
+ contentIn.getContent() = f and
+ Cand::parameterValueFlowReturnCand(p, _, true) and
+ compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType())
)
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, TContentNone()) and
+ argumentValueFlowsThrough(arg, contentIn, 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, contentIn) and
+ argumentValueFlowsThrough(arg, TContentNone(), node)
)
}
pragma[nomagic]
private predicate parameterValueFlowArg(
- ParameterNode p, ArgumentNode arg, ContentOption contentIn, ContentOption contentOut
+ ParameterNode p, ArgumentNode arg, ContentOption contentIn
) {
- parameterValueFlow(p, arg, contentIn, contentOut) and
- Cand::argumentValueFlowsThroughCand(arg, _, _, _)
+ parameterValueFlow(p, arg, contentIn) 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, ContentOption contentIn
) {
exists(ParameterNode param | viableParamArg(call, param, arg) |
- parameterValueFlowReturn(param, _, kind, contentIn, contentOut)
+ parameterValueFlowReturn(param, kind, contentIn)
)
}
/**
- * Holds if `arg` flows to `out` through `call` using only value-preserving steps,
+ * Holds if `arg` flows to `out` through a call using only value-preserving steps,
* 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).
+ * `contentIn` describes the content of `arg` that can flow to `out` (if any).
*/
- 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, ContentOption contentIn, Node out) {
+ exists(DataFlowCall call, ReturnKind kind |
+ argumentValueFlowsThrough0(call, arg, kind, contentIn) and
+ out = getAnOutNode(call, kind)
|
// normal flow through
contentIn = TContentNone() and
- contentOut = TContentNone() 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))
- )
)
}
- /**
- * Holds if `p` can flow to the pre-update node associated with post-update
- * node `n`, in the same callable, using only value-preserving steps.
- */
- cached
- predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) {
- parameterValueFlow(p, n.getPreUpdateNode(), TContentNone(), TContentNone())
- }
-
- pragma[nomagic]
- private predicate parameterValueFlowsToPostUpdate(
- ParameterNode p, PostUpdateNode n, ContentOption contentIn, ContentOption contentOut
- ) {
- parameterValueFlow(p, n, contentIn, contentOut) and
- contentOut.hasContent()
- }
-
/**
* Holds if `p` can flow to a return node of kind `kind` in the same
* callable using only value-preserving steps.
*
* `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).
+ * node (if any).
*/
- cached
- predicate parameterValueFlowReturn(
- ParameterNode p, Node ret, ReturnKindExt kind, ContentOption contentIn,
- ContentOption contentOut
+ private predicate parameterValueFlowReturn(
+ ParameterNode p, ReturnKind kind, ContentOption contentIn
) {
- 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, contentIn) 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(), TContentNone())
+ }
+
/**
* Holds if data can flow from `node1` to `node2` via a direct assignment to
* `f`.
@@ -469,14 +340,14 @@ 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) {
+ predicate store(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)
+ argumentValueFlowsThrough(n2, TContentSome(f), n1)
or
readStep(n2, f, n1)
)
@@ -520,6 +391,21 @@ 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 TAccessPathFront =
+ TFrontNil(DataFlowType t) or
+ TFrontHead(Content f)
+
+ cached
+ newtype TAccessPathFrontOption =
+ TAccessPathFrontNone() or
+ TAccessPathFrontSome(AccessPathFront apf)
}
/**
@@ -529,8 +415,7 @@ 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
}
}
@@ -538,7 +423,7 @@ newtype TContentOption =
TContentNone() or
TContentSome(Content f)
-class ContentOption extends TContentOption {
+private class ContentOption extends TContentOption {
Content getContent() { this = TContentSome(result) }
predicate hasContent() { exists(this.getContent()) }
@@ -678,6 +563,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 +585,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 +597,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 +611,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 +678,58 @@ 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()))
+ }
+}
+
+/**
+ * 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(Content f) { this = TFrontHead(f) }
+}
+
+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 }
+}
+
+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 }
+}
+
+/** 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..9587ea5f274 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
@@ -251,15 +251,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 +289,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
+ nodeCandFwd1IsStored(f, config) and
not inBarrier(node, config)
)
or
@@ -317,13 +313,34 @@ 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 f, Node node, boolean fromArg, Configuration config) {
+ exists(Node mid |
+ nodeCandFwd1(mid, fromArg, config) and
+ read(mid, f, node)
+ )
+}
+
+/**
+ * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
+ exists(Node mid, Node node |
+ not fullBarrier(node, config) and
+ useFieldFlow(config) and
+ nodeCandFwd1(mid, config) and
+ store(mid, f, node)
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1ReturnPosition(
ReturnPosition pos, boolean fromArg, Configuration config
@@ -335,43 +352,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 +368,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 +379,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) {
@@ -435,55 +422,43 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
// store
exists(Content f |
nodeCand1Store(f, node, toReturn, config) and
- readCand1(f, config)
+ nodeCand1IsRead(f, config)
)
or
// read
exists(Node mid, Content f |
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ read(node, f, mid) and
+ nodeCandFwd1IsStored(f, 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`.
*/
pragma[nomagic]
-private predicate readCand1(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content f, 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, f, mid) and
+ nodeCandFwd1IsStored(f, unbind(config)) and
nodeCand1(mid, _, config)
)
}
@@ -492,8 +467,8 @@ pragma[nomagic]
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
exists(Node mid |
nodeCand1(mid, toReturn, config) and
- storeCandFwd1(f, unbind(config)) and
- storeDirect(node, f, mid)
+ nodeCandFwd1IsStored(f, unbind(config)) and
+ store(node, f, mid)
)
}
@@ -501,13 +476,29 @@ private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configu
* Holds if `f` 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
+private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
+ nodeCand1IsRead(f, conf) and
nodeCand1Store(f, _, _, 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 +506,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 +543,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 +557,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)
@@ -576,419 +570,73 @@ private predicate parameterThroughFlowCand(ParameterNode p, Configuration config
pragma[nomagic]
private predicate store(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
+ nodeCand1IsReadAndStored(f, config) and
nodeCand1(n2, unbind(config)) and
- (
- storeDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentSome(f), n2)
- )
+ store(n1, f, n2)
}
pragma[nomagic]
private predicate read(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
+ nodeCand1IsReadAndStored(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)
- )
+ read(n1, f, n2)
}
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 predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ localFlowStep(node1, node2, 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[noinline]
+private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ additionalLocalFlowStep(node1, node2, 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
- 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)
- )
-}
-
-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
+private predicate viableReturnPosOutNodeCand1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
) {
- argumentFlowsThrough(arg.getNode(), out.getNode(), _, t, TSummaryTaint(), config)
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, 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[noinline]
-private ReturnPosition getReturnPosition1(ReturnNodeExt node, Configuration config) {
- result = getReturnPosition(node) and
- nodeCand1(node, 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 +647,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,117 +659,126 @@ 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, Content f |
+ nodeCandFwd2(mid, fromArg, argStored, _, config) and
+ store(mid, f, node, config) and
stored = true
)
or
// read
exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, config) and
- storeCandFwd2(f, stored, config)
+ nodeCandFwd2Read(f, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(f, 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)
+ )
)
)
}
@@ -1130,86 +787,148 @@ private predicate nodeCandFwd2(NodeExt node, boolean fromArg, boolean stored, Co
* Holds if `f` 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 f, 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
+ store(mid, f, 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 f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+) {
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, true, config) and
+ read(mid, f, 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)
+ nodeCand2Store(f, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(f, 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 f, boolean read0 |
+ read(node, f, mid, config) and
+ nodeCandFwd2IsStored(f, 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()
)
}
@@ -1217,32 +936,36 @@ private predicate nodeCand2(NodeExt node, boolean toReturn, boolean read, Config
* Holds if `f` 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 f, 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, f, mid, config) and
+ nodeCandFwd2IsStored(f, 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 f, 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))
+ exists(Node mid |
+ store(node, f, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, true, config) and
+ nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
+/**
+ * Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
+ */
pragma[nomagic]
-private predicate storeCand2(Content f, boolean stored, Configuration conf) {
- exists(NodeExt node |
- nodeCand2Store(f, node, _, stored, conf) and
- nodeCand2(node, _, stored, conf)
+private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
+ exists(Node node |
+ nodeCand2Store(f, node, _, _, stored, conf) and
+ nodeCand2(node, _, _, stored, conf)
)
}
@@ -1251,29 +974,76 @@ private predicate storeCand2(Content f, boolean stored, Configuration conf) {
* covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate readStoreCand(Content f, Configuration conf) {
+private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
exists(boolean apNonEmpty |
- storeCand2(f, apNonEmpty, conf) and
- readCand2(f, apNonEmpty, conf)
+ nodeCand2IsStored(f, apNonEmpty, conf) and
+ nodeCand2IsRead(f, apNonEmpty, conf)
)
}
-private predicate nodeCand2(NodeExt node, Configuration config) { nodeCand2(node, _, _, config) }
+pragma[nomagic]
+private predicate nodeCand2Out(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
+) {
+ 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 flowOutOfCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate nodeCand2In(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
) {
- flowOutOfCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ 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 +1054,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 +1072,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 +1086,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 +1109,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,307 +1146,365 @@ 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 f, Node node2, Configuration config) {
+ read(node1, f, node2, config) and
+ nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- readStoreCand(f, unbind(config))
+ nodeCand2IsReadAndStored(f, 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, Content f, Node node2, Configuration config) {
+ store(node1, f, node2, config) 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(f, 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
+ 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
+ // store
+ exists(Node mid, Content f |
+ flowCandFwd(mid, fromArg, argApf, _, config) and
+ storeCand2(mid, f, node, config) and
+ nodeCand2(node, _, _, true, unbind(config)) and
apf.headUsesContent(f)
)
or
+ // read
exists(Content f |
- flowCandFwdRead(f, node, fromArg, config) and
- consCandFwd(f, apf, config) and
- nodeCand2(node, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ flowCandFwdRead(f, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(f, 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
+private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n |
+ flowCandFwd(mid, _, _, apf, config) and
+ storeCand2(mid, f, n, config) and
+ nodeCand2(n, _, _, true, unbind(config)) and
compatibleTypes(apf.getType(), f.getType())
)
}
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
+private predicate flowCandFwdRead(
+ Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+) {
+ exists(Node mid, AccessPathFrontHead apf0 |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ readCand2(mid, f, node, config) and
apf0.headUsesContent(f)
)
}
+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 data can flow from a source to `node` with the given `apf` and
- * from there flow to a sink.
+ * Holds if an argument to `call` is reached in the flow covered by `flowCandFwd`.
*/
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 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 `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(
+ 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
- toReturn = false
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flowCand(mid, _, _, apf, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone()
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- additionalJumpStepExt(node, mid, config) and
- flowCand(mid, _, nil, config) and
+ 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
- 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
+ // store
exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, apf0, config) and
+ flowCandStore(node, f, toReturn, returnApf, apf0, config) and
apf0.headUsesContent(f) and
- consCand(f, apf, config)
+ flowCandConsCand(f, apf, config)
)
or
+ // read
exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, config) and
+ flowCandRead(node, f, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(f, apf0, config) and
apf.headUsesContent(f)
)
+ 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
+ // 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 flowCandRead(
- NodeExt node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
+ Node node, Content f, 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 |
+ readCand2(node, f, 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, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
+ Configuration config
) {
- exists(NodeExt mid |
- storeExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ storeCand2(node, f, mid, config) and
+ flowCand(mid, toReturn, returnApf, apf0, 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
+private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(f, apf, config) and
+ exists(Node n, AccessPathFrontHead apf0 |
+ flowCandFwd(n, _, _, apf0, config) and
apf0.headUsesContent(f) and
- flowCandRead(n, f, _, apf, config)
+ flowCandRead(n, f, _, _, 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
+ TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
TConsCons(Content f1, Content f2, int len) {
- consCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
}
/**
@@ -1778,292 +1611,396 @@ private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
/** Gets the access path obtained by pushing `f` onto `ap`. */
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, 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
+ // store
exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, config) and
+ flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
ap = push(f, ap0)
)
or
+ // read
exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, config) and
- flowConsCandFwd(f, apf, ap, config)
+ flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
+ flowFwdConsCand(f, apf, ap, config)
+ )
+ or
+ // 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, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFront apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
+ exists(Node mid, AccessPathFront apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
flowFwdStore1(mid, f, node, apf0, apf, config)
)
}
pragma[nomagic]
private predicate flowFwdStore0(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, Configuration config
+ Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
) {
- storeExtCand2(mid, f, node, config) and
- flowCand(mid, _, apf0, config)
+ storeCand2(mid, f, node, config) and
+ flowCand(mid, _, _, apf0, config)
}
pragma[noinline]
private predicate flowFwdStore1(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, AccessPathFrontHead apf,
+ Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
flowFwdStore0(mid, f, node, apf0, config) and
- consCand(f, apf0, config) and
+ flowCandConsCand(f, apf0, config) and
apf.headUsesContent(f) and
- flowCand(node, _, apf, unbind(config))
+ flowCand(node, _, _, apf, unbind(config))
}
pragma[nomagic]
private predicate flowFwdRead(
- NodeExt node, Content f, AccessPath ap0, boolean fromArg, Configuration config
+ Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
+ Configuration config
) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- readExtCand2(mid, f, node, config) and
+ exists(Node mid, AccessPathFrontHead apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
+ readCand2(mid, f, node, config) and
apf0.headUsesContent(f) and
- flowCand(node, _, _, unbind(config))
+ flowCand(node, _, _, _, unbind(config))
)
}
pragma[nomagic]
-private predicate flowConsCandFwd(
+private predicate flowFwdConsCand(
Content f, AccessPathFront apf, AccessPath ap, Configuration config
) {
- exists(NodeExt n |
- flowFwd(n, _, apf, ap, config) and
+ exists(Node n |
+ flowFwd(n, _, _, apf, ap, config) and
flowFwdStore1(n, f, _, apf, _, config)
)
}
-/**
- * Holds if data can flow from a source to `node` with the given `ap` and
- * from there flow to a sink.
- */
-private predicate flow(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flow0(node, toReturn, 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
- toReturn = false and
- ap instanceof AccessPathNil
- or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flow(mid, toReturn, 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
- ap instanceof AccessPathNil
- )
- or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flow(mid, _, ap, config) and
- toReturn = false
- )
- 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)
+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 storeFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate flowFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
) {
- storeExtCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, config) and
+ 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 `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(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flow0(node, toReturn, returnAp, ap, config) and
+ flowFwd(node, _, _, _, ap, config)
+}
+
+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(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flow(mid, toReturn, returnAp, ap, config)
+ )
+ or
+ 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(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(Content f |
+ flowStore(f, node, toReturn, returnAp, ap, config) and
+ flowConsCand(f, 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
+ // 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 storeFlowFwd(
+ Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+) {
+ storeCand2(node1, f, node2, config) and
+ flowFwdStore(node2, f, ap, _, _, _, config) and
ap0 = push(f, ap)
}
pragma[nomagic]
private predicate flowStore(
- Content f, NodeExt node, boolean toReturn, AccessPath ap, Configuration config
+ Content f, 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, f, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- readExtCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, config) and
+ readCand2(node1, f, node2, config) and
+ flowFwdRead(node2, f, ap, _, _, config) and
ap0 = pop(f, ap) and
- flowConsCandFwd(f, _, ap0, unbind(config))
+ flowFwdConsCand(f, _, 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)
+ exists(Node n, Node mid |
+ flow(mid, _, _, ap, config) and
+ readFlowFwd(n, f, 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 +2050,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) {
@@ -2293,17 +2230,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 +2256,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(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2331,23 +2269,9 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
}
-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
+ read(node1, f, node2) and
flow(node2, config)
}
@@ -2360,7 +2284,7 @@ private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Conte
pragma[nomagic]
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- storeDirect(node1, f, node2) and
+ store(node1, f, node2) and
flow(node2, config)
}
@@ -2399,11 +2323,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 +2339,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 +2363,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 +2438,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 +2479,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
@@ -2854,7 +2775,7 @@ private module FlowExploration {
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
) {
ap1 = mid.getAp() and
- storeDirect(mid.getNode(), f, node) and
+ store(mid.getNode(), f, node) and
ap2.getHead() = f and
ap2.len() = unbindInt(ap1.len() + 1) and
compatibleTypes(ap1.getType(), f.getType())
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..618c29ebb86 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
@@ -43,7 +43,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
@@ -299,7 +299,7 @@ private class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNo
override Node getPreUpdateNode() { result.asExpr() = pd.getDefinedExpr() }
- override Location getLocation() { result = pd.getLocation() }
+ override Location getLocation() { result = pd.getActualLocation() }
PartialDefinition getPartialDefinition() { result = pd }
@@ -496,8 +496,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 |
(
@@ -657,7 +655,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..d35bc4533b9 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
@@ -113,44 +113,39 @@ class FlowVar extends TFlowVar {
* ```
*/
private module PartialDefinitions {
- private newtype TPartialDefinition =
- TExplicitFieldStoreQualifier(Expr qualifier, ControlFlowNode node) {
- exists(FieldAccess fa | qualifier = fa.getQualifier() |
+ private predicate isInstanceFieldWrite(FieldAccess fa, ControlFlowNode node) {
+ assignmentLikeOperation(node, _, fa, _)
+ }
+
+ class PartialDefinition extends Expr {
+ ControlFlowNode node;
+
+ PartialDefinition() {
+ exists(FieldAccess fa | this = fa.getQualifier() |
+ // `fa = ...`, `fa += ...`, etc.
isInstanceFieldWrite(fa, node)
or
+ // `fa.a = ...`, `f(&fa)`, etc.
exists(PartialDefinition pd |
node = pd.getSubBasicBlockStart() and
fa = pd.getDefinedExpr()
)
)
- } or
- TExplicitCallQualifier(Expr qualifier) {
+ or
+ // `e.f(...)`
exists(Call call |
- qualifier = call.getQualifier() and
+ this = 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;
- ControlFlowNode node;
-
- PartialDefinition() {
- this = TExplicitFieldStoreQualifier(definedExpr, node)
+ ) and
+ node = this
or
- this = TExplicitCallQualifier(definedExpr) and node = definedExpr
- or
- this = TReferenceArgument(definedExpr, node)
+ // `f(e)`, `f(&e)`, etc.
+ referenceArgument(node, this)
}
- predicate partiallyDefines(Variable v) { definedExpr = v.getAnAccess() }
+ predicate partiallyDefines(Variable v) { this = v.getAnAccess() }
- predicate partiallyDefinesThis(ThisExpr e) { definedExpr = e }
+ predicate partiallyDefinesThis(ThisExpr e) { this = e }
/**
* Gets the subBasicBlock where this `PartialDefinition` is defined.
@@ -165,33 +160,29 @@ private module PartialDefinitions {
* ```
* The expression `x` is being partially defined.
*/
- Expr getDefinedExpr() { result = definedExpr }
+ Expr getDefinedExpr() { result = this }
- Location getLocation() {
- not exists(definedExpr.getLocation()) and result = definedExpr.getParent().getLocation()
+ /**
+ * 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
- definedExpr.getLocation() instanceof UnknownLocation and
- result = definedExpr.getParent().getLocation()
+ this.getLocation() instanceof UnknownLocation and
+ result = this.getParent().getLocation()
or
- result = definedExpr.getLocation() and not result instanceof UnknownLocation
+ result = this.getLocation() and not result instanceof UnknownLocation
}
-
- string toString() { result = "partial def of " + definedExpr }
}
/**
* A partial definition that's a definition by reference.
*/
- class DefinitionByReference extends PartialDefinition, TReferenceArgument {
+ class DefinitionByReference extends PartialDefinition {
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)
- }
+ DefinitionByReference() { referenceArgument(va, this) }
VariableAccess getVariableAccess() { result = va }
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Access.qll b/cpp/ql/src/semmle/code/cpp/exprs/Access.qll
index f68eaef5d8b..86a46c2531d 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/Access.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/Access.qll
@@ -338,7 +338,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/Call.qll b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll
index 6eb4ed52319..d22e090a9e6 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)`
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll b/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll
index 3a45d8597d5..0648e449157 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll
@@ -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.
@@ -699,7 +699,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 +762,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/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/ir/dataflow/DefaultTaintTracking.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
index 10aa162e3e0..dbeefae4880 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
@@ -2,6 +2,7 @@ import cpp
import semmle.code.cpp.security.Security
private import semmle.code.cpp.ir.dataflow.DataFlow
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.models.interfaces.Taint
@@ -143,7 +144,17 @@ private predicate writesVariable(StoreInstruction store, Variable var) {
}
/**
- * A variable that has any kind of upper-bound check anywhere in the program
+ * A variable that has any kind of upper-bound check anywhere in the program. This is
+ * biased towards being inclusive because there are a lot of valid ways of doing an
+ * upper bounds checks if we don't consider where it occurs, for example:
+ * ```
+ * if (x < 10) { sink(x); }
+ *
+ * if (10 > y) { sink(y); }
+ *
+ * if (z > 10) { z = 10; }
+ * sink(z);
+ * ```
*/
// TODO: This coarse overapproximation, ported from the old taint tracking
// library, could be replaced with an actual semantic check that a particular
@@ -152,10 +163,10 @@ private predicate writesVariable(StoreInstruction store, Variable var) {
// previously suppressed by this predicate by coincidence.
private predicate hasUpperBoundsCheck(Variable var) {
exists(RelationalOperation oper, VariableAccess access |
- oper.getLeftOperand() = access and
+ oper.getAnOperand() = access and
access.getTarget() = var and
// Comparing to 0 is not an upper bound check
- not oper.getRightOperand().getValue() = "0"
+ not oper.getAnOperand().getValue() = "0"
)
}
@@ -171,6 +182,7 @@ private predicate nodeIsBarrierIn(DataFlow::Node node) {
node = getNodeForSource(any(Expr e))
}
+cached
private predicate instructionTaintStep(Instruction i1, Instruction i2) {
// Expressions computed from tainted data are also tainted
exists(CallInstruction call, int argIndex | call = i2 |
@@ -187,6 +199,14 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
// Flow through pointer dereference
i2.(LoadInstruction).getSourceAddress() = i1
or
+ // Flow through partial reads of arrays and unions
+ i2.(LoadInstruction).getSourceValueOperand().getAnyDef() = i1 and
+ not i1.isResultConflated() and
+ (
+ i1.getResultType() instanceof ArrayType or
+ i1.getResultType() instanceof Union
+ )
+ or
// Unary instructions tend to preserve enough information in practice that we
// want taint to flow through.
// The exception is `FieldAddressInstruction`. Together with the rule for
@@ -195,11 +215,28 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
// unrelated field. This would happen across function boundaries, where the IR
// would not be able to match loads to stores.
i2.(UnaryInstruction).getUnary() = i1 and
- not i2 instanceof FieldAddressInstruction
+ (
+ not i2 instanceof FieldAddressInstruction
+ or
+ i2.(FieldAddressInstruction).getField().getDeclaringType() instanceof Union
+ )
or
- i2.(ChiInstruction).getPartial() = i1 and
+ // Flow out of definition-by-reference
+ i2.(ChiInstruction).getPartial() = i1.(WriteSideEffectInstruction) and
not i2.isResultConflated()
or
+ // Flow from an element to an array or union that contains it.
+ i2.(ChiInstruction).getPartial() = i1 and
+ not i2.isResultConflated() and
+ exists(Type t | i2.getResultLanguageType().hasType(t, false) |
+ t instanceof Union
+ or
+ t instanceof ArrayType
+ or
+ // Buffers of unknown size
+ t instanceof UnknownType
+ )
+ or
exists(BinaryInstruction bin |
bin = i2 and
predictableInstruction(i2.getAnOperand().getDef()) and
@@ -356,6 +393,16 @@ private Element adjustedSink(DataFlow::Node sink) {
result.(AssignOperation).getAnOperand() = sink.asExpr()
}
+/**
+ * Holds if `tainted` may contain taint from `source`.
+ *
+ * A tainted expression is either directly user input, or is
+ * computed from user input in a way that users can probably
+ * control the exact output of the computation.
+ *
+ * This doesn't include data flow through global variables.
+ * If you need that you must call `taintedIncludingGlobalVars`.
+ */
cached
predicate tainted(Expr source, Element tainted) {
exists(DefaultTaintTrackingCfg cfg, DataFlow::Node sink |
@@ -364,6 +411,21 @@ predicate tainted(Expr source, Element tainted) {
)
}
+/**
+ * Holds if `tainted` may contain taint from `source`, where the taint passed
+ * through a global variable named `globalVar`.
+ *
+ * A tainted expression is either directly user input, or is
+ * computed from user input in a way that users can probably
+ * control the exact output of the computation.
+ *
+ * This version gives the same results as tainted but also includes
+ * data flow through global variables.
+ *
+ * The parameter `globalVar` is the qualified name of the last global variable
+ * used to move the value from source to tainted. If the taint did not pass
+ * through a global variable, then `globalVar = ""`.
+ */
cached
predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
tainted(source, tainted) and
@@ -381,11 +443,245 @@ predicate taintedIncludingGlobalVars(Expr source, Element tainted, string global
)
}
+/**
+ * Gets the global variable whose qualified name is `id`. Use this predicate
+ * together with `taintedIncludingGlobalVars`. Example:
+ *
+ * ```
+ * exists(string varName |
+ * taintedIncludingGlobalVars(source, tainted, varName) and
+ * var = globalVarFromId(varName)
+ * )
+ * ```
+ */
GlobalOrNamespaceVariable globalVarFromId(string id) { id = result.getQualifiedName() }
+/**
+ * Resolve potential target function(s) for `call`.
+ *
+ * If `call` is a call through a function pointer (`ExprCall`) or
+ * targets a virtual method, simple data flow analysis is performed
+ * in order to identify target(s).
+ */
Function resolveCall(Call call) {
exists(CallInstruction callInstruction |
callInstruction.getAST() = call and
result = Dispatch::viableCallable(callInstruction)
)
}
+
+/**
+ * Provides definitions for augmenting source/sink pairs with data-flow paths
+ * between them. From a `@kind path-problem` query, import this module in the
+ * global scope, extend `TaintTrackingConfiguration`, and use `taintedWithPath`
+ * in place of `tainted`.
+ *
+ * Importing this module will also import the query predicates that contain the
+ * taint paths.
+ */
+module TaintedWithPath {
+ private newtype TSingleton = MkSingleton()
+
+ /**
+ * A taint-tracking configuration that matches sources and sinks in the same
+ * way as the `tainted` predicate.
+ *
+ * Override `isSink` and `taintThroughGlobals` as needed, but do not provide
+ * a characteristic predicate.
+ */
+ class TaintTrackingConfiguration extends TSingleton {
+ /** Override this to specify which elements are sinks in this configuration. */
+ abstract predicate isSink(Element e);
+
+ /**
+ * Override this predicate to `any()` to allow taint to flow through global
+ * variables.
+ */
+ predicate taintThroughGlobals() { none() }
+
+ /** Gets a textual representation of this element. */
+ string toString() { result = "TaintTrackingConfiguration" }
+ }
+
+ private class AdjustedConfiguration extends DataFlow3::Configuration {
+ AdjustedConfiguration() { this = "AdjustedConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(TaintTrackingConfiguration cfg | cfg.isSink(adjustedSink(sink)))
+ }
+
+ override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
+ instructionTaintStep(n1.asInstruction(), n2.asInstruction())
+ or
+ exists(TaintTrackingConfiguration cfg | cfg.taintThroughGlobals() |
+ writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
+ or
+ readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
+ )
+ }
+
+ override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
+
+ override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
+ }
+
+ /*
+ * A sink `Element` may map to multiple `DataFlowX::PathNode`s via (the
+ * inverse of) `adjustedSink`. For example, an `Expr` maps to all its
+ * conversions, and a `Variable` maps to all loads and stores from it. Because
+ * the path node is part of the tuple that constitutes the alert, this leads
+ * to duplicate alerts.
+ *
+ * To avoid showing duplicates, we edit the graph to replace the final node
+ * coming from the data-flow library with a node that matches exactly the
+ * `Element` sink that's requested.
+ *
+ * The same is done for sources.
+ */
+
+ private newtype TPathNode =
+ TWrapPathNode(DataFlow3::PathNode n) or
+ // There's a single newtype constructor for both sources and sinks since
+ // that makes it easiest to deal with the case where source = sink.
+ TEndpointPathNode(Element e) {
+ exists(AdjustedConfiguration cfg, DataFlow3::Node sourceNode, DataFlow3::Node sinkNode |
+ cfg.hasFlow(sourceNode, sinkNode)
+ |
+ sourceNode = getNodeForSource(e)
+ or
+ e = adjustedSink(sinkNode) and
+ exists(TaintTrackingConfiguration ttCfg | ttCfg.isSink(e))
+ )
+ }
+
+ /** An opaque type used for the nodes of a data-flow path. */
+ class PathNode extends TPathNode {
+ /** Gets a textual representation of this element. */
+ string toString() { none() }
+
+ /**
+ * 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
+ ) {
+ none()
+ }
+ }
+
+ private class WrapPathNode extends PathNode, TWrapPathNode {
+ DataFlow3::PathNode inner() { this = TWrapPathNode(result) }
+
+ override string toString() { result = this.inner().toString() }
+
+ override predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ this.inner().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ }
+ }
+
+ private class EndpointPathNode extends PathNode, TEndpointPathNode {
+ Expr inner() { this = TEndpointPathNode(result) }
+
+ override string toString() { result = this.inner().toString() }
+
+ override predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ this
+ .inner()
+ .getLocation()
+ .hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ }
+ }
+
+ /** A PathNode whose `Element` is a source. It may also be a sink. */
+ private class InitialPathNode extends EndpointPathNode {
+ InitialPathNode() { exists(getNodeForSource(this.inner())) }
+ }
+
+ /** A PathNode whose `Element` is a sink. It may also be a source. */
+ private class FinalPathNode extends EndpointPathNode {
+ FinalPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSink(this.inner())) }
+ }
+
+ /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
+ query predicate edges(PathNode a, PathNode b) {
+ DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), b.(WrapPathNode).inner())
+ or
+ // To avoid showing trivial-looking steps, we _replace_ the last node instead
+ // of adding an edge out of it.
+ exists(WrapPathNode sinkNode |
+ DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), sinkNode.inner()) and
+ b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
+ )
+ or
+ // Same for the first node
+ exists(WrapPathNode sourceNode |
+ DataFlow3::PathGraph::edges(sourceNode.inner(), b.(WrapPathNode).inner()) and
+ sourceNode.inner().getNode() = getNodeForSource(a.(InitialPathNode).inner())
+ )
+ or
+ // Finally, handle the case where the path goes directly from a source to a
+ // sink, meaning that they both need to be translated.
+ exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
+ DataFlow3::PathGraph::edges(sourceNode.inner(), sinkNode.inner()) and
+ sourceNode.inner().getNode() = getNodeForSource(a.(InitialPathNode).inner()) and
+ b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
+ )
+ }
+
+ /** Holds if `n` is a node in the graph of data flow path explanations. */
+ query predicate nodes(PathNode n, string key, string val) {
+ key = "semmle.label" and val = n.toString()
+ }
+
+ /**
+ * Holds if `tainted` may contain taint from `source`, where `sourceNode` and
+ * `sinkNode` are the corresponding `PathNode`s that can be used in a query
+ * to provide path explanations. Extend `TaintTrackingConfiguration` to use
+ * this predicate.
+ *
+ * A tainted expression is either directly user input, or is computed from
+ * user input in a way that users can probably control the exact output of
+ * the computation.
+ */
+ predicate taintedWithPath(Expr source, Element tainted, PathNode sourceNode, PathNode sinkNode) {
+ exists(AdjustedConfiguration cfg, DataFlow3::Node flowSource, DataFlow3::Node flowSink |
+ source = sourceNode.(InitialPathNode).inner() and
+ flowSource = getNodeForSource(source) and
+ cfg.hasFlow(flowSource, flowSink) and
+ tainted = adjustedSink(flowSink) and
+ tainted = sinkNode.(FinalPathNode).inner()
+ )
+ }
+
+ private predicate isGlobalVariablePathNode(WrapPathNode n) {
+ n.inner().getNode().asVariable() instanceof GlobalOrNamespaceVariable
+ }
+
+ private predicate edgesWithoutGlobals(PathNode a, PathNode b) {
+ edges(a, b) and
+ not isGlobalVariablePathNode(a) and
+ not isGlobalVariablePathNode(b)
+ }
+
+ /**
+ * Holds if `tainted` can be reached from a taint source without passing
+ * through a global variable.
+ */
+ predicate taintedWithoutGlobals(Element tainted) {
+ exists(PathNode sourceNode, FinalPathNode sinkNode |
+ sourceNode.(WrapPathNode).inner().getNode() = getNodeForSource(_) and
+ edgesWithoutGlobals+(sourceNode, sinkNode) and
+ tainted = sinkNode.inner()
+ )
+ }
+}
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 abb62b9a021..1dd1d9ac4cc 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
@@ -70,8 +70,7 @@ private module VirtualDispatch {
// Call return
exists(DataFlowCall call, ReturnKind returnKind |
other = getAnOutNode(call, returnKind) and
- src.(ReturnNode).getKind() = returnKind and
- call.getStaticCallTarget() = src.getEnclosingCallable()
+ returnNodeWithKindAndEnclosingCallable(src, returnKind, call.getStaticCallTarget())
) and
allowFromArg = false
or
@@ -125,6 +124,18 @@ private module VirtualDispatch {
}
}
+ /**
+ * A ReturnNode with its ReturnKind and its enclosing callable.
+ *
+ * Used to fix a join ordering issue in flowsFrom.
+ */
+ private predicate returnNodeWithKindAndEnclosingCallable(
+ ReturnNode node, ReturnKind kind, DataFlowCallable callable
+ ) {
+ node.getKind() = kind and
+ node.getEnclosingCallable() = callable
+ }
+
/** Call through a function pointer. */
private class DataSensitiveExprCall extends DataSensitiveCall {
DataSensitiveExprCall() { not exists(this.getStaticCallTarget()) }
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..9587ea5f274 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
@@ -251,15 +251,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 +289,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
+ nodeCandFwd1IsStored(f, config) and
not inBarrier(node, config)
)
or
@@ -317,13 +313,34 @@ 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 f, Node node, boolean fromArg, Configuration config) {
+ exists(Node mid |
+ nodeCandFwd1(mid, fromArg, config) and
+ read(mid, f, node)
+ )
+}
+
+/**
+ * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
+ exists(Node mid, Node node |
+ not fullBarrier(node, config) and
+ useFieldFlow(config) and
+ nodeCandFwd1(mid, config) and
+ store(mid, f, node)
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1ReturnPosition(
ReturnPosition pos, boolean fromArg, Configuration config
@@ -335,43 +352,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 +368,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 +379,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) {
@@ -435,55 +422,43 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
// store
exists(Content f |
nodeCand1Store(f, node, toReturn, config) and
- readCand1(f, config)
+ nodeCand1IsRead(f, config)
)
or
// read
exists(Node mid, Content f |
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ read(node, f, mid) and
+ nodeCandFwd1IsStored(f, 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`.
*/
pragma[nomagic]
-private predicate readCand1(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content f, 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, f, mid) and
+ nodeCandFwd1IsStored(f, unbind(config)) and
nodeCand1(mid, _, config)
)
}
@@ -492,8 +467,8 @@ pragma[nomagic]
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
exists(Node mid |
nodeCand1(mid, toReturn, config) and
- storeCandFwd1(f, unbind(config)) and
- storeDirect(node, f, mid)
+ nodeCandFwd1IsStored(f, unbind(config)) and
+ store(node, f, mid)
)
}
@@ -501,13 +476,29 @@ private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configu
* Holds if `f` 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
+private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
+ nodeCand1IsRead(f, conf) and
nodeCand1Store(f, _, _, 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 +506,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 +543,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 +557,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)
@@ -576,419 +570,73 @@ private predicate parameterThroughFlowCand(ParameterNode p, Configuration config
pragma[nomagic]
private predicate store(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
+ nodeCand1IsReadAndStored(f, config) and
nodeCand1(n2, unbind(config)) and
- (
- storeDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentSome(f), n2)
- )
+ store(n1, f, n2)
}
pragma[nomagic]
private predicate read(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
+ nodeCand1IsReadAndStored(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)
- )
+ read(n1, f, n2)
}
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 predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ localFlowStep(node1, node2, 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[noinline]
+private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ additionalLocalFlowStep(node1, node2, 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
- 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)
- )
-}
-
-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
+private predicate viableReturnPosOutNodeCand1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
) {
- argumentFlowsThrough(arg.getNode(), out.getNode(), _, t, TSummaryTaint(), config)
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, 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[noinline]
-private ReturnPosition getReturnPosition1(ReturnNodeExt node, Configuration config) {
- result = getReturnPosition(node) and
- nodeCand1(node, 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 +647,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,117 +659,126 @@ 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, Content f |
+ nodeCandFwd2(mid, fromArg, argStored, _, config) and
+ store(mid, f, node, config) and
stored = true
)
or
// read
exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, config) and
- storeCandFwd2(f, stored, config)
+ nodeCandFwd2Read(f, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(f, 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)
+ )
)
)
}
@@ -1130,86 +787,148 @@ private predicate nodeCandFwd2(NodeExt node, boolean fromArg, boolean stored, Co
* Holds if `f` 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 f, 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
+ store(mid, f, 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 f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+) {
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, true, config) and
+ read(mid, f, 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)
+ nodeCand2Store(f, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(f, 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 f, boolean read0 |
+ read(node, f, mid, config) and
+ nodeCandFwd2IsStored(f, 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()
)
}
@@ -1217,32 +936,36 @@ private predicate nodeCand2(NodeExt node, boolean toReturn, boolean read, Config
* Holds if `f` 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 f, 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, f, mid, config) and
+ nodeCandFwd2IsStored(f, 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 f, 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))
+ exists(Node mid |
+ store(node, f, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, true, config) and
+ nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
+/**
+ * Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
+ */
pragma[nomagic]
-private predicate storeCand2(Content f, boolean stored, Configuration conf) {
- exists(NodeExt node |
- nodeCand2Store(f, node, _, stored, conf) and
- nodeCand2(node, _, stored, conf)
+private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
+ exists(Node node |
+ nodeCand2Store(f, node, _, _, stored, conf) and
+ nodeCand2(node, _, _, stored, conf)
)
}
@@ -1251,29 +974,76 @@ private predicate storeCand2(Content f, boolean stored, Configuration conf) {
* covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate readStoreCand(Content f, Configuration conf) {
+private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
exists(boolean apNonEmpty |
- storeCand2(f, apNonEmpty, conf) and
- readCand2(f, apNonEmpty, conf)
+ nodeCand2IsStored(f, apNonEmpty, conf) and
+ nodeCand2IsRead(f, apNonEmpty, conf)
)
}
-private predicate nodeCand2(NodeExt node, Configuration config) { nodeCand2(node, _, _, config) }
+pragma[nomagic]
+private predicate nodeCand2Out(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
+) {
+ 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 flowOutOfCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate nodeCand2In(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
) {
- flowOutOfCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ 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 +1054,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 +1072,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 +1086,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 +1109,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,307 +1146,365 @@ 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 f, Node node2, Configuration config) {
+ read(node1, f, node2, config) and
+ nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- readStoreCand(f, unbind(config))
+ nodeCand2IsReadAndStored(f, 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, Content f, Node node2, Configuration config) {
+ store(node1, f, node2, config) 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(f, 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
+ 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
+ // store
+ exists(Node mid, Content f |
+ flowCandFwd(mid, fromArg, argApf, _, config) and
+ storeCand2(mid, f, node, config) and
+ nodeCand2(node, _, _, true, unbind(config)) and
apf.headUsesContent(f)
)
or
+ // read
exists(Content f |
- flowCandFwdRead(f, node, fromArg, config) and
- consCandFwd(f, apf, config) and
- nodeCand2(node, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ flowCandFwdRead(f, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(f, 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
+private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n |
+ flowCandFwd(mid, _, _, apf, config) and
+ storeCand2(mid, f, n, config) and
+ nodeCand2(n, _, _, true, unbind(config)) and
compatibleTypes(apf.getType(), f.getType())
)
}
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
+private predicate flowCandFwdRead(
+ Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+) {
+ exists(Node mid, AccessPathFrontHead apf0 |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ readCand2(mid, f, node, config) and
apf0.headUsesContent(f)
)
}
+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 data can flow from a source to `node` with the given `apf` and
- * from there flow to a sink.
+ * Holds if an argument to `call` is reached in the flow covered by `flowCandFwd`.
*/
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 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 `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(
+ 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
- toReturn = false
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flowCand(mid, _, _, apf, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone()
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- additionalJumpStepExt(node, mid, config) and
- flowCand(mid, _, nil, config) and
+ 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
- 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
+ // store
exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, apf0, config) and
+ flowCandStore(node, f, toReturn, returnApf, apf0, config) and
apf0.headUsesContent(f) and
- consCand(f, apf, config)
+ flowCandConsCand(f, apf, config)
)
or
+ // read
exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, config) and
+ flowCandRead(node, f, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(f, apf0, config) and
apf.headUsesContent(f)
)
+ 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
+ // 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 flowCandRead(
- NodeExt node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
+ Node node, Content f, 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 |
+ readCand2(node, f, 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, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
+ Configuration config
) {
- exists(NodeExt mid |
- storeExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ storeCand2(node, f, mid, config) and
+ flowCand(mid, toReturn, returnApf, apf0, 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
+private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(f, apf, config) and
+ exists(Node n, AccessPathFrontHead apf0 |
+ flowCandFwd(n, _, _, apf0, config) and
apf0.headUsesContent(f) and
- flowCandRead(n, f, _, apf, config)
+ flowCandRead(n, f, _, _, 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
+ TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
TConsCons(Content f1, Content f2, int len) {
- consCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
}
/**
@@ -1778,292 +1611,396 @@ private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
/** Gets the access path obtained by pushing `f` onto `ap`. */
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, 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
+ // store
exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, config) and
+ flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
ap = push(f, ap0)
)
or
+ // read
exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, config) and
- flowConsCandFwd(f, apf, ap, config)
+ flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
+ flowFwdConsCand(f, apf, ap, config)
+ )
+ or
+ // 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, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFront apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
+ exists(Node mid, AccessPathFront apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
flowFwdStore1(mid, f, node, apf0, apf, config)
)
}
pragma[nomagic]
private predicate flowFwdStore0(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, Configuration config
+ Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
) {
- storeExtCand2(mid, f, node, config) and
- flowCand(mid, _, apf0, config)
+ storeCand2(mid, f, node, config) and
+ flowCand(mid, _, _, apf0, config)
}
pragma[noinline]
private predicate flowFwdStore1(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, AccessPathFrontHead apf,
+ Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
flowFwdStore0(mid, f, node, apf0, config) and
- consCand(f, apf0, config) and
+ flowCandConsCand(f, apf0, config) and
apf.headUsesContent(f) and
- flowCand(node, _, apf, unbind(config))
+ flowCand(node, _, _, apf, unbind(config))
}
pragma[nomagic]
private predicate flowFwdRead(
- NodeExt node, Content f, AccessPath ap0, boolean fromArg, Configuration config
+ Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
+ Configuration config
) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- readExtCand2(mid, f, node, config) and
+ exists(Node mid, AccessPathFrontHead apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
+ readCand2(mid, f, node, config) and
apf0.headUsesContent(f) and
- flowCand(node, _, _, unbind(config))
+ flowCand(node, _, _, _, unbind(config))
)
}
pragma[nomagic]
-private predicate flowConsCandFwd(
+private predicate flowFwdConsCand(
Content f, AccessPathFront apf, AccessPath ap, Configuration config
) {
- exists(NodeExt n |
- flowFwd(n, _, apf, ap, config) and
+ exists(Node n |
+ flowFwd(n, _, _, apf, ap, config) and
flowFwdStore1(n, f, _, apf, _, config)
)
}
-/**
- * Holds if data can flow from a source to `node` with the given `ap` and
- * from there flow to a sink.
- */
-private predicate flow(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flow0(node, toReturn, 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
- toReturn = false and
- ap instanceof AccessPathNil
- or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flow(mid, toReturn, 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
- ap instanceof AccessPathNil
- )
- or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flow(mid, _, ap, config) and
- toReturn = false
- )
- 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)
+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 storeFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate flowFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
) {
- storeExtCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, config) and
+ 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 `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(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flow0(node, toReturn, returnAp, ap, config) and
+ flowFwd(node, _, _, _, ap, config)
+}
+
+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(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flow(mid, toReturn, returnAp, ap, config)
+ )
+ or
+ 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(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(Content f |
+ flowStore(f, node, toReturn, returnAp, ap, config) and
+ flowConsCand(f, 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
+ // 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 storeFlowFwd(
+ Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+) {
+ storeCand2(node1, f, node2, config) and
+ flowFwdStore(node2, f, ap, _, _, _, config) and
ap0 = push(f, ap)
}
pragma[nomagic]
private predicate flowStore(
- Content f, NodeExt node, boolean toReturn, AccessPath ap, Configuration config
+ Content f, 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, f, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- readExtCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, config) and
+ readCand2(node1, f, node2, config) and
+ flowFwdRead(node2, f, ap, _, _, config) and
ap0 = pop(f, ap) and
- flowConsCandFwd(f, _, ap0, unbind(config))
+ flowFwdConsCand(f, _, 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)
+ exists(Node n, Node mid |
+ flow(mid, _, _, ap, config) and
+ readFlowFwd(n, f, 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 +2050,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) {
@@ -2293,17 +2230,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 +2256,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(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2331,23 +2269,9 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
}
-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
+ read(node1, f, node2) and
flow(node2, config)
}
@@ -2360,7 +2284,7 @@ private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Conte
pragma[nomagic]
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- storeDirect(node1, f, node2) and
+ store(node1, f, node2) and
flow(node2, config)
}
@@ -2399,11 +2323,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 +2339,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 +2363,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 +2438,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 +2479,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
@@ -2854,7 +2775,7 @@ private module FlowExploration {
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
) {
ap1 = mid.getAp() and
- storeDirect(mid.getNode(), f, node) and
+ store(mid.getNode(), f, node) and
ap2.getHead() = f and
ap2.len() = unbindInt(ap1.len() + 1) and
compatibleTypes(ap1.getType(), f.getType())
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..9587ea5f274 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
@@ -251,15 +251,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 +289,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
+ nodeCandFwd1IsStored(f, config) and
not inBarrier(node, config)
)
or
@@ -317,13 +313,34 @@ 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 f, Node node, boolean fromArg, Configuration config) {
+ exists(Node mid |
+ nodeCandFwd1(mid, fromArg, config) and
+ read(mid, f, node)
+ )
+}
+
+/**
+ * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
+ exists(Node mid, Node node |
+ not fullBarrier(node, config) and
+ useFieldFlow(config) and
+ nodeCandFwd1(mid, config) and
+ store(mid, f, node)
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1ReturnPosition(
ReturnPosition pos, boolean fromArg, Configuration config
@@ -335,43 +352,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 +368,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 +379,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) {
@@ -435,55 +422,43 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
// store
exists(Content f |
nodeCand1Store(f, node, toReturn, config) and
- readCand1(f, config)
+ nodeCand1IsRead(f, config)
)
or
// read
exists(Node mid, Content f |
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ read(node, f, mid) and
+ nodeCandFwd1IsStored(f, 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`.
*/
pragma[nomagic]
-private predicate readCand1(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content f, 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, f, mid) and
+ nodeCandFwd1IsStored(f, unbind(config)) and
nodeCand1(mid, _, config)
)
}
@@ -492,8 +467,8 @@ pragma[nomagic]
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
exists(Node mid |
nodeCand1(mid, toReturn, config) and
- storeCandFwd1(f, unbind(config)) and
- storeDirect(node, f, mid)
+ nodeCandFwd1IsStored(f, unbind(config)) and
+ store(node, f, mid)
)
}
@@ -501,13 +476,29 @@ private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configu
* Holds if `f` 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
+private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
+ nodeCand1IsRead(f, conf) and
nodeCand1Store(f, _, _, 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 +506,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 +543,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 +557,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)
@@ -576,419 +570,73 @@ private predicate parameterThroughFlowCand(ParameterNode p, Configuration config
pragma[nomagic]
private predicate store(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
+ nodeCand1IsReadAndStored(f, config) and
nodeCand1(n2, unbind(config)) and
- (
- storeDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentSome(f), n2)
- )
+ store(n1, f, n2)
}
pragma[nomagic]
private predicate read(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
+ nodeCand1IsReadAndStored(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)
- )
+ read(n1, f, n2)
}
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 predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ localFlowStep(node1, node2, 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[noinline]
+private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ additionalLocalFlowStep(node1, node2, 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
- 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)
- )
-}
-
-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
+private predicate viableReturnPosOutNodeCand1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
) {
- argumentFlowsThrough(arg.getNode(), out.getNode(), _, t, TSummaryTaint(), config)
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, 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[noinline]
-private ReturnPosition getReturnPosition1(ReturnNodeExt node, Configuration config) {
- result = getReturnPosition(node) and
- nodeCand1(node, 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 +647,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,117 +659,126 @@ 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, Content f |
+ nodeCandFwd2(mid, fromArg, argStored, _, config) and
+ store(mid, f, node, config) and
stored = true
)
or
// read
exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, config) and
- storeCandFwd2(f, stored, config)
+ nodeCandFwd2Read(f, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(f, 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)
+ )
)
)
}
@@ -1130,86 +787,148 @@ private predicate nodeCandFwd2(NodeExt node, boolean fromArg, boolean stored, Co
* Holds if `f` 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 f, 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
+ store(mid, f, 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 f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+) {
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, true, config) and
+ read(mid, f, 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)
+ nodeCand2Store(f, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(f, 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 f, boolean read0 |
+ read(node, f, mid, config) and
+ nodeCandFwd2IsStored(f, 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()
)
}
@@ -1217,32 +936,36 @@ private predicate nodeCand2(NodeExt node, boolean toReturn, boolean read, Config
* Holds if `f` 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 f, 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, f, mid, config) and
+ nodeCandFwd2IsStored(f, 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 f, 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))
+ exists(Node mid |
+ store(node, f, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, true, config) and
+ nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
+/**
+ * Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
+ */
pragma[nomagic]
-private predicate storeCand2(Content f, boolean stored, Configuration conf) {
- exists(NodeExt node |
- nodeCand2Store(f, node, _, stored, conf) and
- nodeCand2(node, _, stored, conf)
+private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
+ exists(Node node |
+ nodeCand2Store(f, node, _, _, stored, conf) and
+ nodeCand2(node, _, _, stored, conf)
)
}
@@ -1251,29 +974,76 @@ private predicate storeCand2(Content f, boolean stored, Configuration conf) {
* covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate readStoreCand(Content f, Configuration conf) {
+private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
exists(boolean apNonEmpty |
- storeCand2(f, apNonEmpty, conf) and
- readCand2(f, apNonEmpty, conf)
+ nodeCand2IsStored(f, apNonEmpty, conf) and
+ nodeCand2IsRead(f, apNonEmpty, conf)
)
}
-private predicate nodeCand2(NodeExt node, Configuration config) { nodeCand2(node, _, _, config) }
+pragma[nomagic]
+private predicate nodeCand2Out(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
+) {
+ 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 flowOutOfCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate nodeCand2In(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
) {
- flowOutOfCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ 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 +1054,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 +1072,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 +1086,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 +1109,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,307 +1146,365 @@ 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 f, Node node2, Configuration config) {
+ read(node1, f, node2, config) and
+ nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- readStoreCand(f, unbind(config))
+ nodeCand2IsReadAndStored(f, 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, Content f, Node node2, Configuration config) {
+ store(node1, f, node2, config) 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(f, 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
+ 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
+ // store
+ exists(Node mid, Content f |
+ flowCandFwd(mid, fromArg, argApf, _, config) and
+ storeCand2(mid, f, node, config) and
+ nodeCand2(node, _, _, true, unbind(config)) and
apf.headUsesContent(f)
)
or
+ // read
exists(Content f |
- flowCandFwdRead(f, node, fromArg, config) and
- consCandFwd(f, apf, config) and
- nodeCand2(node, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ flowCandFwdRead(f, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(f, 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
+private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n |
+ flowCandFwd(mid, _, _, apf, config) and
+ storeCand2(mid, f, n, config) and
+ nodeCand2(n, _, _, true, unbind(config)) and
compatibleTypes(apf.getType(), f.getType())
)
}
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
+private predicate flowCandFwdRead(
+ Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+) {
+ exists(Node mid, AccessPathFrontHead apf0 |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ readCand2(mid, f, node, config) and
apf0.headUsesContent(f)
)
}
+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 data can flow from a source to `node` with the given `apf` and
- * from there flow to a sink.
+ * Holds if an argument to `call` is reached in the flow covered by `flowCandFwd`.
*/
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 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 `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(
+ 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
- toReturn = false
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flowCand(mid, _, _, apf, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone()
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- additionalJumpStepExt(node, mid, config) and
- flowCand(mid, _, nil, config) and
+ 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
- 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
+ // store
exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, apf0, config) and
+ flowCandStore(node, f, toReturn, returnApf, apf0, config) and
apf0.headUsesContent(f) and
- consCand(f, apf, config)
+ flowCandConsCand(f, apf, config)
)
or
+ // read
exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, config) and
+ flowCandRead(node, f, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(f, apf0, config) and
apf.headUsesContent(f)
)
+ 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
+ // 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 flowCandRead(
- NodeExt node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
+ Node node, Content f, 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 |
+ readCand2(node, f, 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, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
+ Configuration config
) {
- exists(NodeExt mid |
- storeExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ storeCand2(node, f, mid, config) and
+ flowCand(mid, toReturn, returnApf, apf0, 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
+private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(f, apf, config) and
+ exists(Node n, AccessPathFrontHead apf0 |
+ flowCandFwd(n, _, _, apf0, config) and
apf0.headUsesContent(f) and
- flowCandRead(n, f, _, apf, config)
+ flowCandRead(n, f, _, _, 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
+ TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
TConsCons(Content f1, Content f2, int len) {
- consCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
}
/**
@@ -1778,292 +1611,396 @@ private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
/** Gets the access path obtained by pushing `f` onto `ap`. */
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, 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
+ // store
exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, config) and
+ flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
ap = push(f, ap0)
)
or
+ // read
exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, config) and
- flowConsCandFwd(f, apf, ap, config)
+ flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
+ flowFwdConsCand(f, apf, ap, config)
+ )
+ or
+ // 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, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFront apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
+ exists(Node mid, AccessPathFront apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
flowFwdStore1(mid, f, node, apf0, apf, config)
)
}
pragma[nomagic]
private predicate flowFwdStore0(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, Configuration config
+ Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
) {
- storeExtCand2(mid, f, node, config) and
- flowCand(mid, _, apf0, config)
+ storeCand2(mid, f, node, config) and
+ flowCand(mid, _, _, apf0, config)
}
pragma[noinline]
private predicate flowFwdStore1(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, AccessPathFrontHead apf,
+ Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
flowFwdStore0(mid, f, node, apf0, config) and
- consCand(f, apf0, config) and
+ flowCandConsCand(f, apf0, config) and
apf.headUsesContent(f) and
- flowCand(node, _, apf, unbind(config))
+ flowCand(node, _, _, apf, unbind(config))
}
pragma[nomagic]
private predicate flowFwdRead(
- NodeExt node, Content f, AccessPath ap0, boolean fromArg, Configuration config
+ Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
+ Configuration config
) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- readExtCand2(mid, f, node, config) and
+ exists(Node mid, AccessPathFrontHead apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
+ readCand2(mid, f, node, config) and
apf0.headUsesContent(f) and
- flowCand(node, _, _, unbind(config))
+ flowCand(node, _, _, _, unbind(config))
)
}
pragma[nomagic]
-private predicate flowConsCandFwd(
+private predicate flowFwdConsCand(
Content f, AccessPathFront apf, AccessPath ap, Configuration config
) {
- exists(NodeExt n |
- flowFwd(n, _, apf, ap, config) and
+ exists(Node n |
+ flowFwd(n, _, _, apf, ap, config) and
flowFwdStore1(n, f, _, apf, _, config)
)
}
-/**
- * Holds if data can flow from a source to `node` with the given `ap` and
- * from there flow to a sink.
- */
-private predicate flow(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flow0(node, toReturn, 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
- toReturn = false and
- ap instanceof AccessPathNil
- or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flow(mid, toReturn, 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
- ap instanceof AccessPathNil
- )
- or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flow(mid, _, ap, config) and
- toReturn = false
- )
- 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)
+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 storeFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate flowFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
) {
- storeExtCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, config) and
+ 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 `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(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flow0(node, toReturn, returnAp, ap, config) and
+ flowFwd(node, _, _, _, ap, config)
+}
+
+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(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flow(mid, toReturn, returnAp, ap, config)
+ )
+ or
+ 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(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(Content f |
+ flowStore(f, node, toReturn, returnAp, ap, config) and
+ flowConsCand(f, 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
+ // 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 storeFlowFwd(
+ Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+) {
+ storeCand2(node1, f, node2, config) and
+ flowFwdStore(node2, f, ap, _, _, _, config) and
ap0 = push(f, ap)
}
pragma[nomagic]
private predicate flowStore(
- Content f, NodeExt node, boolean toReturn, AccessPath ap, Configuration config
+ Content f, 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, f, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- readExtCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, config) and
+ readCand2(node1, f, node2, config) and
+ flowFwdRead(node2, f, ap, _, _, config) and
ap0 = pop(f, ap) and
- flowConsCandFwd(f, _, ap0, unbind(config))
+ flowFwdConsCand(f, _, 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)
+ exists(Node n, Node mid |
+ flow(mid, _, _, ap, config) and
+ readFlowFwd(n, f, 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 +2050,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) {
@@ -2293,17 +2230,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 +2256,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(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2331,23 +2269,9 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
}
-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
+ read(node1, f, node2) and
flow(node2, config)
}
@@ -2360,7 +2284,7 @@ private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Conte
pragma[nomagic]
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- storeDirect(node1, f, node2) and
+ store(node1, f, node2) and
flow(node2, config)
}
@@ -2399,11 +2323,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 +2339,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 +2363,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 +2438,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 +2479,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
@@ -2854,7 +2775,7 @@ private module FlowExploration {
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
) {
ap1 = mid.getAp() and
- storeDirect(mid.getNode(), f, node) and
+ store(mid.getNode(), f, node) and
ap2.getHead() = f and
ap2.len() = unbindInt(ap1.len() + 1) and
compatibleTypes(ap1.getType(), f.getType())
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..9587ea5f274 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
@@ -251,15 +251,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 +289,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
+ nodeCandFwd1IsStored(f, config) and
not inBarrier(node, config)
)
or
@@ -317,13 +313,34 @@ 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 f, Node node, boolean fromArg, Configuration config) {
+ exists(Node mid |
+ nodeCandFwd1(mid, fromArg, config) and
+ read(mid, f, node)
+ )
+}
+
+/**
+ * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
+ exists(Node mid, Node node |
+ not fullBarrier(node, config) and
+ useFieldFlow(config) and
+ nodeCandFwd1(mid, config) and
+ store(mid, f, node)
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1ReturnPosition(
ReturnPosition pos, boolean fromArg, Configuration config
@@ -335,43 +352,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 +368,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 +379,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) {
@@ -435,55 +422,43 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
// store
exists(Content f |
nodeCand1Store(f, node, toReturn, config) and
- readCand1(f, config)
+ nodeCand1IsRead(f, config)
)
or
// read
exists(Node mid, Content f |
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ read(node, f, mid) and
+ nodeCandFwd1IsStored(f, 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`.
*/
pragma[nomagic]
-private predicate readCand1(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content f, 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, f, mid) and
+ nodeCandFwd1IsStored(f, unbind(config)) and
nodeCand1(mid, _, config)
)
}
@@ -492,8 +467,8 @@ pragma[nomagic]
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
exists(Node mid |
nodeCand1(mid, toReturn, config) and
- storeCandFwd1(f, unbind(config)) and
- storeDirect(node, f, mid)
+ nodeCandFwd1IsStored(f, unbind(config)) and
+ store(node, f, mid)
)
}
@@ -501,13 +476,29 @@ private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configu
* Holds if `f` 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
+private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
+ nodeCand1IsRead(f, conf) and
nodeCand1Store(f, _, _, 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 +506,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 +543,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 +557,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)
@@ -576,419 +570,73 @@ private predicate parameterThroughFlowCand(ParameterNode p, Configuration config
pragma[nomagic]
private predicate store(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
+ nodeCand1IsReadAndStored(f, config) and
nodeCand1(n2, unbind(config)) and
- (
- storeDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentSome(f), n2)
- )
+ store(n1, f, n2)
}
pragma[nomagic]
private predicate read(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
+ nodeCand1IsReadAndStored(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)
- )
+ read(n1, f, n2)
}
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 predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ localFlowStep(node1, node2, 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[noinline]
+private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ additionalLocalFlowStep(node1, node2, 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
- 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)
- )
-}
-
-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
+private predicate viableReturnPosOutNodeCand1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
) {
- argumentFlowsThrough(arg.getNode(), out.getNode(), _, t, TSummaryTaint(), config)
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, 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[noinline]
-private ReturnPosition getReturnPosition1(ReturnNodeExt node, Configuration config) {
- result = getReturnPosition(node) and
- nodeCand1(node, 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 +647,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,117 +659,126 @@ 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, Content f |
+ nodeCandFwd2(mid, fromArg, argStored, _, config) and
+ store(mid, f, node, config) and
stored = true
)
or
// read
exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, config) and
- storeCandFwd2(f, stored, config)
+ nodeCandFwd2Read(f, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(f, 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)
+ )
)
)
}
@@ -1130,86 +787,148 @@ private predicate nodeCandFwd2(NodeExt node, boolean fromArg, boolean stored, Co
* Holds if `f` 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 f, 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
+ store(mid, f, 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 f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+) {
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, true, config) and
+ read(mid, f, 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)
+ nodeCand2Store(f, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(f, 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 f, boolean read0 |
+ read(node, f, mid, config) and
+ nodeCandFwd2IsStored(f, 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()
)
}
@@ -1217,32 +936,36 @@ private predicate nodeCand2(NodeExt node, boolean toReturn, boolean read, Config
* Holds if `f` 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 f, 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, f, mid, config) and
+ nodeCandFwd2IsStored(f, 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 f, 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))
+ exists(Node mid |
+ store(node, f, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, true, config) and
+ nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
+/**
+ * Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
+ */
pragma[nomagic]
-private predicate storeCand2(Content f, boolean stored, Configuration conf) {
- exists(NodeExt node |
- nodeCand2Store(f, node, _, stored, conf) and
- nodeCand2(node, _, stored, conf)
+private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
+ exists(Node node |
+ nodeCand2Store(f, node, _, _, stored, conf) and
+ nodeCand2(node, _, _, stored, conf)
)
}
@@ -1251,29 +974,76 @@ private predicate storeCand2(Content f, boolean stored, Configuration conf) {
* covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate readStoreCand(Content f, Configuration conf) {
+private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
exists(boolean apNonEmpty |
- storeCand2(f, apNonEmpty, conf) and
- readCand2(f, apNonEmpty, conf)
+ nodeCand2IsStored(f, apNonEmpty, conf) and
+ nodeCand2IsRead(f, apNonEmpty, conf)
)
}
-private predicate nodeCand2(NodeExt node, Configuration config) { nodeCand2(node, _, _, config) }
+pragma[nomagic]
+private predicate nodeCand2Out(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
+) {
+ 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 flowOutOfCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate nodeCand2In(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
) {
- flowOutOfCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ 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 +1054,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 +1072,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 +1086,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 +1109,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,307 +1146,365 @@ 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 f, Node node2, Configuration config) {
+ read(node1, f, node2, config) and
+ nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- readStoreCand(f, unbind(config))
+ nodeCand2IsReadAndStored(f, 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, Content f, Node node2, Configuration config) {
+ store(node1, f, node2, config) 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(f, 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
+ 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
+ // store
+ exists(Node mid, Content f |
+ flowCandFwd(mid, fromArg, argApf, _, config) and
+ storeCand2(mid, f, node, config) and
+ nodeCand2(node, _, _, true, unbind(config)) and
apf.headUsesContent(f)
)
or
+ // read
exists(Content f |
- flowCandFwdRead(f, node, fromArg, config) and
- consCandFwd(f, apf, config) and
- nodeCand2(node, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ flowCandFwdRead(f, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(f, 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
+private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n |
+ flowCandFwd(mid, _, _, apf, config) and
+ storeCand2(mid, f, n, config) and
+ nodeCand2(n, _, _, true, unbind(config)) and
compatibleTypes(apf.getType(), f.getType())
)
}
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
+private predicate flowCandFwdRead(
+ Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+) {
+ exists(Node mid, AccessPathFrontHead apf0 |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ readCand2(mid, f, node, config) and
apf0.headUsesContent(f)
)
}
+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 data can flow from a source to `node` with the given `apf` and
- * from there flow to a sink.
+ * Holds if an argument to `call` is reached in the flow covered by `flowCandFwd`.
*/
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 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 `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(
+ 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
- toReturn = false
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flowCand(mid, _, _, apf, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone()
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- additionalJumpStepExt(node, mid, config) and
- flowCand(mid, _, nil, config) and
+ 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
- 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
+ // store
exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, apf0, config) and
+ flowCandStore(node, f, toReturn, returnApf, apf0, config) and
apf0.headUsesContent(f) and
- consCand(f, apf, config)
+ flowCandConsCand(f, apf, config)
)
or
+ // read
exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, config) and
+ flowCandRead(node, f, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(f, apf0, config) and
apf.headUsesContent(f)
)
+ 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
+ // 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 flowCandRead(
- NodeExt node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
+ Node node, Content f, 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 |
+ readCand2(node, f, 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, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
+ Configuration config
) {
- exists(NodeExt mid |
- storeExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ storeCand2(node, f, mid, config) and
+ flowCand(mid, toReturn, returnApf, apf0, 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
+private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(f, apf, config) and
+ exists(Node n, AccessPathFrontHead apf0 |
+ flowCandFwd(n, _, _, apf0, config) and
apf0.headUsesContent(f) and
- flowCandRead(n, f, _, apf, config)
+ flowCandRead(n, f, _, _, 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
+ TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
TConsCons(Content f1, Content f2, int len) {
- consCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
}
/**
@@ -1778,292 +1611,396 @@ private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
/** Gets the access path obtained by pushing `f` onto `ap`. */
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, 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
+ // store
exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, config) and
+ flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
ap = push(f, ap0)
)
or
+ // read
exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, config) and
- flowConsCandFwd(f, apf, ap, config)
+ flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
+ flowFwdConsCand(f, apf, ap, config)
+ )
+ or
+ // 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, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFront apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
+ exists(Node mid, AccessPathFront apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
flowFwdStore1(mid, f, node, apf0, apf, config)
)
}
pragma[nomagic]
private predicate flowFwdStore0(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, Configuration config
+ Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
) {
- storeExtCand2(mid, f, node, config) and
- flowCand(mid, _, apf0, config)
+ storeCand2(mid, f, node, config) and
+ flowCand(mid, _, _, apf0, config)
}
pragma[noinline]
private predicate flowFwdStore1(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, AccessPathFrontHead apf,
+ Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
flowFwdStore0(mid, f, node, apf0, config) and
- consCand(f, apf0, config) and
+ flowCandConsCand(f, apf0, config) and
apf.headUsesContent(f) and
- flowCand(node, _, apf, unbind(config))
+ flowCand(node, _, _, apf, unbind(config))
}
pragma[nomagic]
private predicate flowFwdRead(
- NodeExt node, Content f, AccessPath ap0, boolean fromArg, Configuration config
+ Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
+ Configuration config
) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- readExtCand2(mid, f, node, config) and
+ exists(Node mid, AccessPathFrontHead apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
+ readCand2(mid, f, node, config) and
apf0.headUsesContent(f) and
- flowCand(node, _, _, unbind(config))
+ flowCand(node, _, _, _, unbind(config))
)
}
pragma[nomagic]
-private predicate flowConsCandFwd(
+private predicate flowFwdConsCand(
Content f, AccessPathFront apf, AccessPath ap, Configuration config
) {
- exists(NodeExt n |
- flowFwd(n, _, apf, ap, config) and
+ exists(Node n |
+ flowFwd(n, _, _, apf, ap, config) and
flowFwdStore1(n, f, _, apf, _, config)
)
}
-/**
- * Holds if data can flow from a source to `node` with the given `ap` and
- * from there flow to a sink.
- */
-private predicate flow(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flow0(node, toReturn, 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
- toReturn = false and
- ap instanceof AccessPathNil
- or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flow(mid, toReturn, 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
- ap instanceof AccessPathNil
- )
- or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flow(mid, _, ap, config) and
- toReturn = false
- )
- 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)
+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 storeFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate flowFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
) {
- storeExtCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, config) and
+ 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 `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(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flow0(node, toReturn, returnAp, ap, config) and
+ flowFwd(node, _, _, _, ap, config)
+}
+
+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(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flow(mid, toReturn, returnAp, ap, config)
+ )
+ or
+ 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(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(Content f |
+ flowStore(f, node, toReturn, returnAp, ap, config) and
+ flowConsCand(f, 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
+ // 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 storeFlowFwd(
+ Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+) {
+ storeCand2(node1, f, node2, config) and
+ flowFwdStore(node2, f, ap, _, _, _, config) and
ap0 = push(f, ap)
}
pragma[nomagic]
private predicate flowStore(
- Content f, NodeExt node, boolean toReturn, AccessPath ap, Configuration config
+ Content f, 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, f, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- readExtCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, config) and
+ readCand2(node1, f, node2, config) and
+ flowFwdRead(node2, f, ap, _, _, config) and
ap0 = pop(f, ap) and
- flowConsCandFwd(f, _, ap0, unbind(config))
+ flowFwdConsCand(f, _, 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)
+ exists(Node n, Node mid |
+ flow(mid, _, _, ap, config) and
+ readFlowFwd(n, f, 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 +2050,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) {
@@ -2293,17 +2230,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 +2256,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(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2331,23 +2269,9 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
}
-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
+ read(node1, f, node2) and
flow(node2, config)
}
@@ -2360,7 +2284,7 @@ private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Conte
pragma[nomagic]
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- storeDirect(node1, f, node2) and
+ store(node1, f, node2) and
flow(node2, config)
}
@@ -2399,11 +2323,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 +2339,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 +2363,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 +2438,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 +2479,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
@@ -2854,7 +2775,7 @@ private module FlowExploration {
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
) {
ap1 = mid.getAp() and
- storeDirect(mid.getNode(), f, node) and
+ store(mid.getNode(), f, node) and
ap2.getHead() = f and
ap2.len() = unbindInt(ap1.len() + 1) and
compatibleTypes(ap1.getType(), f.getType())
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..9587ea5f274 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
@@ -251,15 +251,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 +289,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
+ nodeCandFwd1IsStored(f, config) and
not inBarrier(node, config)
)
or
@@ -317,13 +313,34 @@ 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 f, Node node, boolean fromArg, Configuration config) {
+ exists(Node mid |
+ nodeCandFwd1(mid, fromArg, config) and
+ read(mid, f, node)
+ )
+}
+
+/**
+ * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
+ */
+pragma[nomagic]
+private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
+ exists(Node mid, Node node |
+ not fullBarrier(node, config) and
+ useFieldFlow(config) and
+ nodeCandFwd1(mid, config) and
+ store(mid, f, node)
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1ReturnPosition(
ReturnPosition pos, boolean fromArg, Configuration config
@@ -335,43 +352,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 +368,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 +379,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) {
@@ -435,55 +422,43 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
// store
exists(Content f |
nodeCand1Store(f, node, toReturn, config) and
- readCand1(f, config)
+ nodeCand1IsRead(f, config)
)
or
// read
exists(Node mid, Content f |
- readDirect(node, f, mid) and
- storeCandFwd1(f, unbind(config)) and
+ read(node, f, mid) and
+ nodeCandFwd1IsStored(f, 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`.
*/
pragma[nomagic]
-private predicate readCand1(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content f, 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, f, mid) and
+ nodeCandFwd1IsStored(f, unbind(config)) and
nodeCand1(mid, _, config)
)
}
@@ -492,8 +467,8 @@ pragma[nomagic]
private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
exists(Node mid |
nodeCand1(mid, toReturn, config) and
- storeCandFwd1(f, unbind(config)) and
- storeDirect(node, f, mid)
+ nodeCandFwd1IsStored(f, unbind(config)) and
+ store(node, f, mid)
)
}
@@ -501,13 +476,29 @@ private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configu
* Holds if `f` 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
+private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
+ nodeCand1IsRead(f, conf) and
nodeCand1Store(f, _, _, 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 +506,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 +543,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 +557,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)
@@ -576,419 +570,73 @@ private predicate parameterThroughFlowCand(ParameterNode p, Configuration config
pragma[nomagic]
private predicate store(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
+ nodeCand1IsReadAndStored(f, config) and
nodeCand1(n2, unbind(config)) and
- (
- storeDirect(n1, f, n2) or
- argumentValueFlowsThrough(_, n1, TContentNone(), TContentSome(f), n2)
- )
+ store(n1, f, n2)
}
pragma[nomagic]
private predicate read(Node n1, Content f, Node n2, Configuration config) {
- readStoreCand1(f, config) and
+ nodeCand1IsReadAndStored(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)
- )
+ read(n1, f, n2)
}
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 predicate localFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ localFlowStep(node1, node2, 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[noinline]
+private predicate additionalLocalFlowStepNodeCand1(Node node1, Node node2, Configuration config) {
+ nodeCand1(node1, config) and
+ additionalLocalFlowStep(node1, node2, 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
- 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)
- )
-}
-
-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
+private predicate viableReturnPosOutNodeCand1(
+ DataFlowCall call, ReturnPosition pos, Node out, Configuration config
) {
- argumentFlowsThrough(arg.getNode(), out.getNode(), _, t, TSummaryTaint(), config)
+ nodeCand1(out, _, config) and
+ viableReturnPosOutNodeCandFwd1(call, pos, out, 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[noinline]
-private ReturnPosition getReturnPosition1(ReturnNodeExt node, Configuration config) {
- result = getReturnPosition(node) and
- nodeCand1(node, 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 +647,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,117 +659,126 @@ 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, Content f |
+ nodeCandFwd2(mid, fromArg, argStored, _, config) and
+ store(mid, f, node, config) and
stored = true
)
or
// read
exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, config) and
- storeCandFwd2(f, stored, config)
+ nodeCandFwd2Read(f, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(f, 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)
+ )
)
)
}
@@ -1130,86 +787,148 @@ private predicate nodeCandFwd2(NodeExt node, boolean fromArg, boolean stored, Co
* Holds if `f` 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 f, 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
+ store(mid, f, 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 f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+) {
+ exists(Node mid |
+ nodeCandFwd2(mid, fromArg, argStored, true, config) and
+ read(mid, f, 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)
+ nodeCand2Store(f, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(f, 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 f, boolean read0 |
+ read(node, f, mid, config) and
+ nodeCandFwd2IsStored(f, 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()
)
}
@@ -1217,32 +936,36 @@ private predicate nodeCand2(NodeExt node, boolean toReturn, boolean read, Config
* Holds if `f` 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 f, 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, f, mid, config) and
+ nodeCandFwd2IsStored(f, 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 f, 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))
+ exists(Node mid |
+ store(node, f, mid, config) and
+ nodeCand2(mid, toReturn, returnRead, true, config) and
+ nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
+/**
+ * Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
+ */
pragma[nomagic]
-private predicate storeCand2(Content f, boolean stored, Configuration conf) {
- exists(NodeExt node |
- nodeCand2Store(f, node, _, stored, conf) and
- nodeCand2(node, _, stored, conf)
+private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
+ exists(Node node |
+ nodeCand2Store(f, node, _, _, stored, conf) and
+ nodeCand2(node, _, _, stored, conf)
)
}
@@ -1251,29 +974,76 @@ private predicate storeCand2(Content f, boolean stored, Configuration conf) {
* covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate readStoreCand(Content f, Configuration conf) {
+private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
exists(boolean apNonEmpty |
- storeCand2(f, apNonEmpty, conf) and
- readCand2(f, apNonEmpty, conf)
+ nodeCand2IsStored(f, apNonEmpty, conf) and
+ nodeCand2IsRead(f, apNonEmpty, conf)
)
}
-private predicate nodeCand2(NodeExt node, Configuration config) { nodeCand2(node, _, _, config) }
+pragma[nomagic]
+private predicate nodeCand2Out(
+ DataFlowCall call, ReturnNodeExt ret, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
+) {
+ 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 flowOutOfCallableNodeCand2(
- NodeExt node1, NodeExt node2, boolean allowsFieldFlow, Configuration config
+private predicate nodeCand2In(
+ DataFlowCall call, ArgumentNode arg, boolean toReturn, BooleanOption returnRead, boolean read,
+ Configuration config
) {
- flowOutOfCallableNodeCand1(node1, node2, allowsFieldFlow, config) and
+ 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 +1054,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 +1072,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 +1086,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 +1109,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,307 +1146,365 @@ 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 f, Node node2, Configuration config) {
+ read(node1, f, node2, config) and
+ nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- readStoreCand(f, unbind(config))
+ nodeCand2IsReadAndStored(f, 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, Content f, Node node2, Configuration config) {
+ store(node1, f, node2, config) 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(f, 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
+ 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
+ // store
+ exists(Node mid, Content f |
+ flowCandFwd(mid, fromArg, argApf, _, config) and
+ storeCand2(mid, f, node, config) and
+ nodeCand2(node, _, _, true, unbind(config)) and
apf.headUsesContent(f)
)
or
+ // read
exists(Content f |
- flowCandFwdRead(f, node, fromArg, config) and
- consCandFwd(f, apf, config) and
- nodeCand2(node, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
+ flowCandFwdRead(f, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(f, 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
+private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n |
+ flowCandFwd(mid, _, _, apf, config) and
+ storeCand2(mid, f, n, config) and
+ nodeCand2(n, _, _, true, unbind(config)) and
compatibleTypes(apf.getType(), f.getType())
)
}
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
+private predicate flowCandFwdRead(
+ Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+) {
+ exists(Node mid, AccessPathFrontHead apf0 |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ readCand2(mid, f, node, config) and
apf0.headUsesContent(f)
)
}
+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 data can flow from a source to `node` with the given `apf` and
- * from there flow to a sink.
+ * Holds if an argument to `call` is reached in the flow covered by `flowCandFwd`.
*/
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 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 `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(
+ 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
- toReturn = false
+ exists(Node mid |
+ jumpStep(node, mid, config) and
+ flowCand(mid, _, _, apf, config) and
+ toReturn = false and
+ returnApf = TAccessPathFrontNone()
)
or
- exists(NodeExt mid, AccessPathFrontNil nil |
- flowCandFwd(node, _, apf, config) and
- additionalJumpStepExt(node, mid, config) and
- flowCand(mid, _, nil, config) and
+ 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
- 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
+ // store
exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, apf0, config) and
+ flowCandStore(node, f, toReturn, returnApf, apf0, config) and
apf0.headUsesContent(f) and
- consCand(f, apf, config)
+ flowCandConsCand(f, apf, config)
)
or
+ // read
exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, config) and
+ flowCandRead(node, f, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(f, apf0, config) and
apf.headUsesContent(f)
)
+ 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
+ // 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 flowCandRead(
- NodeExt node, Content f, boolean toReturn, AccessPathFront apf0, Configuration config
+ Node node, Content f, 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 |
+ readCand2(node, f, 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, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
+ Configuration config
) {
- exists(NodeExt mid |
- storeExtCand2(node, f, mid, config) and
- flowCand(mid, toReturn, apf0, config)
+ exists(Node mid |
+ storeCand2(node, f, mid, config) and
+ flowCand(mid, toReturn, returnApf, apf0, 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
+private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(f, apf, config) and
+ exists(Node n, AccessPathFrontHead apf0 |
+ flowCandFwd(n, _, _, apf0, config) and
apf0.headUsesContent(f) and
- flowCandRead(n, f, _, apf, config)
+ flowCandRead(n, f, _, _, 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
+ TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
TConsCons(Content f1, Content f2, int len) {
- consCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
}
/**
@@ -1778,292 +1611,396 @@ private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
/** Gets the access path obtained by pushing `f` onto `ap`. */
private AccessPath push(Content f, AccessPath ap) { ap = pop(f, 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
+ // store
exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, config) and
+ flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
ap = push(f, ap0)
)
or
+ // read
exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, config) and
- flowConsCandFwd(f, apf, ap, config)
+ flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
+ flowFwdConsCand(f, apf, ap, config)
+ )
+ or
+ // 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, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
+ AccessPathOption argAp, Configuration config
) {
- exists(NodeExt mid, AccessPathFront apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
+ exists(Node mid, AccessPathFront apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
flowFwdStore1(mid, f, node, apf0, apf, config)
)
}
pragma[nomagic]
private predicate flowFwdStore0(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, Configuration config
+ Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
) {
- storeExtCand2(mid, f, node, config) and
- flowCand(mid, _, apf0, config)
+ storeCand2(mid, f, node, config) and
+ flowCand(mid, _, _, apf0, config)
}
pragma[noinline]
private predicate flowFwdStore1(
- NodeExt mid, Content f, NodeExt node, AccessPathFront apf0, AccessPathFrontHead apf,
+ Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
flowFwdStore0(mid, f, node, apf0, config) and
- consCand(f, apf0, config) and
+ flowCandConsCand(f, apf0, config) and
apf.headUsesContent(f) and
- flowCand(node, _, apf, unbind(config))
+ flowCand(node, _, _, apf, unbind(config))
}
pragma[nomagic]
private predicate flowFwdRead(
- NodeExt node, Content f, AccessPath ap0, boolean fromArg, Configuration config
+ Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
+ Configuration config
) {
- exists(NodeExt mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, apf0, ap0, config) and
- readExtCand2(mid, f, node, config) and
+ exists(Node mid, AccessPathFrontHead apf0 |
+ flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
+ readCand2(mid, f, node, config) and
apf0.headUsesContent(f) and
- flowCand(node, _, _, unbind(config))
+ flowCand(node, _, _, _, unbind(config))
)
}
pragma[nomagic]
-private predicate flowConsCandFwd(
+private predicate flowFwdConsCand(
Content f, AccessPathFront apf, AccessPath ap, Configuration config
) {
- exists(NodeExt n |
- flowFwd(n, _, apf, ap, config) and
+ exists(Node n |
+ flowFwd(n, _, _, apf, ap, config) and
flowFwdStore1(n, f, _, apf, _, config)
)
}
-/**
- * Holds if data can flow from a source to `node` with the given `ap` and
- * from there flow to a sink.
- */
-private predicate flow(NodeExt node, boolean toReturn, AccessPath ap, Configuration config) {
- flow0(node, toReturn, 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
- toReturn = false and
- ap instanceof AccessPathNil
- or
- exists(NodeExt mid |
- localFlowBigStepExt(node, mid, true, _, config) and
- flow(mid, toReturn, 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
- ap instanceof AccessPathNil
- )
- or
- exists(NodeExt mid |
- jumpStepExt(node, mid, config) and
- flow(mid, _, ap, config) and
- toReturn = false
- )
- 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)
+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 storeFwd(
- NodeExt node1, Content f, NodeExt node2, AccessPath ap, AccessPath ap0, Configuration config
+private predicate flowFwdOut(
+ DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
+ AccessPath ap, Configuration config
) {
- storeExtCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, config) and
+ 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 `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(
+ Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config
+) {
+ flow0(node, toReturn, returnAp, ap, config) and
+ flowFwd(node, _, _, _, ap, config)
+}
+
+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(Node mid |
+ localFlowBigStep(node, mid, true, _, config, _) and
+ flow(mid, toReturn, returnAp, ap, config)
+ )
+ or
+ 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(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(Content f |
+ flowStore(f, node, toReturn, returnAp, ap, config) and
+ flowConsCand(f, 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
+ // 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 storeFlowFwd(
+ Node node1, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+) {
+ storeCand2(node1, f, node2, config) and
+ flowFwdStore(node2, f, ap, _, _, _, config) and
ap0 = push(f, ap)
}
pragma[nomagic]
private predicate flowStore(
- Content f, NodeExt node, boolean toReturn, AccessPath ap, Configuration config
+ Content f, 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, f, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
) {
- readExtCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, config) and
+ readCand2(node1, f, node2, config) and
+ flowFwdRead(node2, f, ap, _, _, config) and
ap0 = pop(f, ap) and
- flowConsCandFwd(f, _, ap0, unbind(config))
+ flowFwdConsCand(f, _, 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)
+ exists(Node n, Node mid |
+ flow(mid, _, _, ap, config) and
+ readFlowFwd(n, f, 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 +2050,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) {
@@ -2293,17 +2230,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 +2256,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(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2331,23 +2269,9 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx()
}
-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
+ read(node1, f, node2) and
flow(node2, config)
}
@@ -2360,7 +2284,7 @@ private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Conte
pragma[nomagic]
private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- storeDirect(node1, f, node2) and
+ store(node1, f, node2) and
flow(node2, config)
}
@@ -2399,11 +2323,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 +2339,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 +2363,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 +2438,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 +2479,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
@@ -2854,7 +2775,7 @@ private module FlowExploration {
PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
) {
ap1 = mid.getAp() and
- storeDirect(mid.getNode(), f, node) and
+ store(mid.getNode(), f, node) and
ap2.getHead() = f and
ap2.len() = unbindInt(ap1.len() + 1) and
compatibleTypes(ap1.getType(), f.getType())
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..852f54974e2 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,140 @@ private module Cached {
/**
* The final flow-through calculation:
*
- * - Input/output access paths are abstracted with a `ContentOption` parameter
+ * - Input access paths are abstracted with a `ContentOption` parameter
* that represents the head of the access path. `TContentNone()` means that
* the access path is unrestricted.
* - 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.
*
* `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 any).
*/
- private predicate parameterValueFlow(
- ParameterNode p, Node node, ContentOption contentIn, ContentOption contentOut
- ) {
- parameterValueFlow0(p, node, contentIn, contentOut) and
+ predicate parameterValueFlow(ParameterNode p, Node node, ContentOption contentIn) {
+ parameterValueFlow0(p, node, contentIn) and
if node instanceof CastingNode
then
// normal flow through
contentIn = TContentNone() and
- contentOut = TContentNone() 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))
- )
else any()
}
pragma[nomagic]
- private predicate parameterValueFlow0(
- ParameterNode p, Node node, ContentOption contentIn, ContentOption contentOut
- ) {
+ private predicate parameterValueFlow0(ParameterNode p, Node node, ContentOption contentIn) {
p = node and
Cand::cand(p, _) and
- contentIn = TContentNone() and
- contentOut = TContentNone()
+ contentIn = TContentNone()
or
// local flow
exists(Node mid |
- parameterValueFlow(p, mid, contentIn, contentOut) and
+ parameterValueFlow(p, mid, contentIn) 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()
- )
- 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())
+ parameterValueFlow(p, mid, TContentNone()) and
+ readStep(mid, f, node) and
+ contentIn.getContent() = f and
+ Cand::parameterValueFlowReturnCand(p, _, true) and
+ compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType())
)
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, TContentNone()) and
+ argumentValueFlowsThrough(arg, contentIn, 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, contentIn) and
+ argumentValueFlowsThrough(arg, TContentNone(), node)
)
}
pragma[nomagic]
private predicate parameterValueFlowArg(
- ParameterNode p, ArgumentNode arg, ContentOption contentIn, ContentOption contentOut
+ ParameterNode p, ArgumentNode arg, ContentOption contentIn
) {
- parameterValueFlow(p, arg, contentIn, contentOut) and
- Cand::argumentValueFlowsThroughCand(arg, _, _, _)
+ parameterValueFlow(p, arg, contentIn) 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, ContentOption contentIn
) {
exists(ParameterNode param | viableParamArg(call, param, arg) |
- parameterValueFlowReturn(param, _, kind, contentIn, contentOut)
+ parameterValueFlowReturn(param, kind, contentIn)
)
}
/**
- * Holds if `arg` flows to `out` through `call` using only value-preserving steps,
+ * Holds if `arg` flows to `out` through a call using only value-preserving steps,
* 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).
+ * `contentIn` describes the content of `arg` that can flow to `out` (if any).
*/
- 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, ContentOption contentIn, Node out) {
+ exists(DataFlowCall call, ReturnKind kind |
+ argumentValueFlowsThrough0(call, arg, kind, contentIn) and
+ out = getAnOutNode(call, kind)
|
// normal flow through
contentIn = TContentNone() and
- contentOut = TContentNone() 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))
- )
)
}
- /**
- * Holds if `p` can flow to the pre-update node associated with post-update
- * node `n`, in the same callable, using only value-preserving steps.
- */
- cached
- predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) {
- parameterValueFlow(p, n.getPreUpdateNode(), TContentNone(), TContentNone())
- }
-
- pragma[nomagic]
- private predicate parameterValueFlowsToPostUpdate(
- ParameterNode p, PostUpdateNode n, ContentOption contentIn, ContentOption contentOut
- ) {
- parameterValueFlow(p, n, contentIn, contentOut) and
- contentOut.hasContent()
- }
-
/**
* Holds if `p` can flow to a return node of kind `kind` in the same
* callable using only value-preserving steps.
*
* `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).
+ * node (if any).
*/
- cached
- predicate parameterValueFlowReturn(
- ParameterNode p, Node ret, ReturnKindExt kind, ContentOption contentIn,
- ContentOption contentOut
+ private predicate parameterValueFlowReturn(
+ ParameterNode p, ReturnKind kind, ContentOption contentIn
) {
- 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, contentIn) 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(), TContentNone())
+ }
+
/**
* Holds if data can flow from `node1` to `node2` via a direct assignment to
* `f`.
@@ -469,14 +340,14 @@ 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) {
+ predicate store(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)
+ argumentValueFlowsThrough(n2, TContentSome(f), n1)
or
readStep(n2, f, n1)
)
@@ -520,6 +391,21 @@ 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 TAccessPathFront =
+ TFrontNil(DataFlowType t) or
+ TFrontHead(Content f)
+
+ cached
+ newtype TAccessPathFrontOption =
+ TAccessPathFrontNone() or
+ TAccessPathFrontSome(AccessPathFront apf)
}
/**
@@ -529,8 +415,7 @@ 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
}
}
@@ -538,7 +423,7 @@ newtype TContentOption =
TContentNone() or
TContentSome(Content f)
-class ContentOption extends TContentOption {
+private class ContentOption extends TContentOption {
Content getContent() { this = TContentSome(result) }
predicate hasContent() { exists(this.getContent()) }
@@ -678,6 +563,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 +585,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 +597,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 +611,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 +678,58 @@ 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()))
+ }
+}
+
+/**
+ * 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(Content f) { this = TFrontHead(f) }
+}
+
+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 }
+}
+
+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 }
+}
+
+/** 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 b0f2f1e24ba..97118da117c 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
@@ -36,7 +36,9 @@ class ArgumentNode extends InstructionNode {
DataFlowCall getCall() { this.argumentOf(result, _) }
}
-private newtype TReturnKind = TNormalReturnKind()
+private newtype TReturnKind =
+ TNormalReturnKind() or
+ TIndirectReturnKind(ParameterIndex index)
/**
* A return kind. A return kind describes how a value can be returned
@@ -44,23 +46,76 @@ private newtype TReturnKind = TNormalReturnKind()
*/
class ReturnKind extends TReturnKind {
/** Gets a textual representation of this return kind. */
- string toString() { result = "return" }
+ abstract string toString();
+}
+
+private class NormalReturnKind extends ReturnKind, TNormalReturnKind {
+ override string toString() { result = "return" }
+}
+
+private class IndirectReturnKind extends ReturnKind, TIndirectReturnKind {
+ ParameterIndex index;
+
+ IndirectReturnKind() { this = TIndirectReturnKind(index) }
+
+ override string toString() { result = "outparam[" + index.toString() + "]" }
}
/** A data flow node that occurs as the result of a `ReturnStmt`. */
class ReturnNode extends InstructionNode {
- ReturnNode() { exists(ReturnValueInstruction ret | this.getInstruction() = ret.getReturnValue()) }
+ Instruction primary;
+
+ ReturnNode() {
+ exists(ReturnValueInstruction ret | instr = ret.getReturnValue() and primary = ret)
+ or
+ exists(ReturnIndirectionInstruction rii |
+ instr = rii.getSideEffectOperand().getAnyDef() and primary = rii
+ )
+ }
/** Gets the kind of this returned value. */
- ReturnKind getKind() { result = TNormalReturnKind() }
+ abstract ReturnKind getKind();
+}
+
+class ReturnValueNode extends ReturnNode {
+ override ReturnValueInstruction primary;
+
+ override ReturnKind getKind() { result = TNormalReturnKind() }
+}
+
+class ReturnIndirectionNode extends ReturnNode {
+ override ReturnIndirectionInstruction primary;
+
+ override ReturnKind getKind() { result = TIndirectReturnKind(primary.getParameter().getIndex()) }
}
/** A data flow node that represents the output of a call. */
class OutNode extends InstructionNode {
- override CallInstruction instr;
+ OutNode() {
+ instr instanceof CallInstruction or
+ instr instanceof WriteSideEffectInstruction
+ }
/** Gets the underlying call. */
- DataFlowCall getCall() { result = instr }
+ abstract DataFlowCall getCall();
+
+ abstract ReturnKind getReturnKind();
+}
+
+private class CallOutNode extends OutNode {
+ override CallInstruction instr;
+
+ override DataFlowCall getCall() { result = instr }
+
+ override ReturnKind getReturnKind() { result instanceof NormalReturnKind }
+}
+
+private class SideEffectOutNode extends OutNode {
+ override WriteSideEffectInstruction instr;
+
+ override DataFlowCall getCall() { result = instr.getPrimaryInstruction() }
+
+ override ReturnKind getReturnKind() { result = TIndirectReturnKind(instr.getIndex()) }
}
/**
@@ -69,7 +124,7 @@ class OutNode extends InstructionNode {
*/
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
result.getCall() = call and
- kind = TNormalReturnKind()
+ result.getReturnKind() = kind
}
/**
@@ -137,13 +192,32 @@ private class ArrayContent extends Content, TArrayContent {
override Type getType() { 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()
+ )
+}
+
+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()
+ )
+}
+
/**
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
* Thus, `node2` references an object with a field `f` that contains the
* value of `node1`.
*/
predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
- none() // stub implementation
+ storeStepNoChi(node1, f, node2) or
+ storeStepChi(node1, f, node2)
}
/**
@@ -152,7 +226,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()
+ )
}
/**
@@ -166,7 +245,7 @@ Type getErasedRepr(Type t) {
}
/** Gets a string representation of a type returned by `getErasedRepr`. */
-string ppReprType(Type t) { result = t.toString() }
+string ppReprType(Type t) { none() } // stub implementation
/**
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
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 ae3a521f964..b3ab52aea47 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
@@ -63,6 +63,18 @@ 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).getInstruction().getUnconvertedResultExpression()
+ }
+
/**
* DEPRECATED: See UninitializedNode.
*
@@ -96,6 +108,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;
@@ -157,19 +172,20 @@ int getArgumentPosOfSideEffect(int index) {
/**
* The value of a parameter at function entry, viewed as a node in a data
- * flow graph. This type includes implicit parameters.
+ * 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`, `InstanceParameterNode`, or
+ * `ExplicitParameterNode`, `ThisParameterNode`, or
* `ParameterIndirectionNode`.
*/
class ParameterNode extends InstructionNode {
ParameterNode() {
// To avoid making this class abstract, we enumerate its values here
- instr instanceof InitializeThisInstruction
- or
instr instanceof InitializeParameterInstruction
or
+ instr instanceof InitializeThisInstruction
+ or
instr instanceof InitializeIndirectionInstruction
}
@@ -178,19 +194,7 @@ class ParameterNode extends InstructionNode {
* implicit `this` parameter is considered to have position `-1`, and
* pointer-indirection parameters are at further negative positions.
*/
- predicate isParameterOf(Function f, int pos) { none() } // overridden in subclasses
-}
-
-/** An implicit `this` parameter. */
-class InstanceParameterNode extends ParameterNode {
- override InitializeThisInstruction instr;
-
- override predicate isParameterOf(Function f, int pos) {
- pos = -1 and
- instr.getEnclosingFunction() = f
- }
-
- override string toString() { result = "this" }
+ predicate isParameterOf(Function f, int pos) { none() } // overridden by subclasses
}
/** An explicit positional parameter, not including `this` or `...`. */
@@ -204,7 +208,18 @@ class ExplicitParameterNode extends ParameterNode {
/** Gets the `Parameter` associated with this node. */
Parameter getParameter() { result = instr.getParameter() }
- override string toString() { result = this.getParameter().toString() }
+ override string toString() { result = instr.getParameter().toString() }
+}
+
+/** An implicit `this` parameter. */
+class ThisParameterNode extends ParameterNode {
+ override InitializeThisInstruction instr;
+
+ override predicate isParameterOf(Function f, int pos) {
+ pos = -1 and instr.getEnclosingFunction() = f
+ }
+
+ override string toString() { result = "this" }
}
/** A virtual parameter to model the pointed-to object of a pointer parameter. */
@@ -262,6 +277,57 @@ 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 { }
+
+private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode {
+ override ChiInstruction instr;
+
+ ExplicitFieldStoreQualifierNode() {
+ not instr.isResultConflated() and
+ exists(StoreInstruction store, FieldInstruction field |
+ 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() }
+}
+
+/**
+ * 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;
+
+ ExplicitSingleFieldStoreQualifierNode() {
+ exists(FieldAddressInstruction field |
+ field = instr.getDestinationAddress() and
+ not exists(ChiInstruction chi | chi.getPartial() = instr)
+ )
+ }
+
+ override Node getPreUpdateNode() { none() }
+}
+
/**
* 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.
@@ -297,6 +363,17 @@ class DefinitionByReferenceNode extends InstructionNode {
Parameter getParameter() {
exists(CallInstruction ci | result = ci.getStaticCallTarget().getParameter(instr.getIndex()))
}
+
+ override string toString() {
+ // This string should be unique enough to be helpful but common enough to
+ // avoid storing too many different strings.
+ result =
+ instr.getPrimaryInstruction().(CallInstruction).getStaticCallTarget().getName() +
+ " output argument"
+ or
+ not exists(instr.getPrimaryInstruction().(CallInstruction).getStaticCallTarget()) and
+ result = "output argument"
+ }
}
/**
@@ -335,6 +412,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 }
/**
@@ -347,7 +428,7 @@ ExprNode exprNode(Expr e) { result.getExpr() = e }
* Gets the `Node` corresponding to `e`, if any. Here, `e` may be a
* `Conversion`.
*/
-ExprNode convertedExprNode(Expr e) { result.getExpr() = e }
+ExprNode convertedExprNode(Expr e) { result.getConvertedExpr() = e }
/**
* Gets the `Node` corresponding to the value of `p` at function entry.
@@ -381,6 +462,16 @@ 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
@@ -424,6 +515,36 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
// for now.
iTo.getAnOperand().(ChiTotalOperand).getDef() = iFrom
or
+ // The next two rules allow flow from partial definitions in setters to succeeding loads in the caller.
+ // First, we add flow from write side-effects to non-conflated chi instructions through their
+ // partial operands. 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.
+ iTo.getAnOperand().(ChiPartialOperand).getDef() = iFrom.(WriteSideEffectInstruction) and
+ not iTo.isResultConflated()
+ or
+ // Next, we add flow from non-conflated chi instructions to loads (even when they are not precise).
+ // This ensures that loads of `p->x` gets data flow from the `WriteSideEffectInstruction` above.
+ exists(ChiInstruction chi | iFrom = chi |
+ not chi.isResultConflated() and
+ iTo.(LoadInstruction).getSourceValueOperand().getAnyDef() = chi
+ )
+ or
+ // Flow from stores to structs with a single field to a load of that field.
+ iTo.(LoadInstruction).getSourceValueOperand().getAnyDef() = iFrom and
+ exists(int size, Type type |
+ type = iFrom.getResultType() and
+ iTo.getResultType().getSize() = size and
+ getFieldSizeOfClass(iTo.getResultType(), type, size)
+ )
+ or
// Flow through modeled functions
modelFlow(iFrom, iTo)
}
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/implementation/IRType.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll
index 833c929ecc5..9a75ca19154 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,7 +106,7 @@ 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)
@@ -133,7 +135,7 @@ class IRNumericType extends IRSizedType {
IRNumericType() {
this = TIRSignedIntegerType(byteSize) or
this = TIRUnsignedIntegerType(byteSize) or
- this = TIRFloatingPointType(byteSize)
+ this = TIRFloatingPointType(byteSize, _, _)
}
}
@@ -171,14 +173,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"
+ }
}
/**
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..dce1717bdc9 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
@@ -101,23 +101,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 +130,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/IRSanity.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll
index 94d0192fe18..edf4bc00259 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll
@@ -149,6 +149,26 @@ module InstructionSanity {
count(instr.getBlock().getAPredecessor()) < 2
}
+ /**
+ * Holds if a memory operand is connected to a definition with an unmodeled result, other than
+ * `UnmodeledDefinition` itself.
+ */
+ query predicate memoryOperandDefinitionIsUnmodeled(
+ Instruction instr, string message, IRFunction func, string funcText
+ ) {
+ exists(MemoryOperand operand, Instruction def |
+ operand = instr.getAnOperand() and
+ not operand instanceof UnmodeledUseOperand and
+ def = operand.getAnyDef() and
+ not def.isResultModeled() and
+ not def instanceof UnmodeledDefinitionInstruction and
+ message =
+ "Memory operand definition has unmodeled result, but is not the `UnmodeledDefinition` instruction in function '$@'" and
+ func = instr.getEnclosingIRFunction() and
+ funcText = Language::getIdentityString(func.getFunction())
+ )
+ }
+
/**
* Holds if operand `operand` consumes a value that was defined in
* a different function.
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 fc9d0758125..780f636ff10 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
@@ -190,14 +190,15 @@ 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)
}
/**
- * 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)
@@ -525,7 +526,7 @@ class ReturnValueInstruction extends ReturnInstruction {
final Instruction getReturnValue() { result = getReturnValueOperand().getDef() }
}
-class ReturnIndirectionInstruction extends Instruction {
+class ReturnIndirectionInstruction extends VariableInstruction {
ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection }
final SideEffectOperand getSideEffectOperand() { result = getAnOperand() }
@@ -535,6 +536,12 @@ class ReturnIndirectionInstruction extends Instruction {
final AddressOperand getSourceAddressOperand() { result = getAnOperand() }
final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() }
+
+ /**
+ * Gets the parameter for which this instruction reads the final pointed-to value within the
+ * function.
+ */
+ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
}
class CopyInstruction extends Instruction {
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/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/raw/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll
index 49cb4dd6dc4..dce1717bdc9 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
@@ -101,23 +101,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 +130,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/IRSanity.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll
index 94d0192fe18..edf4bc00259 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll
@@ -149,6 +149,26 @@ module InstructionSanity {
count(instr.getBlock().getAPredecessor()) < 2
}
+ /**
+ * Holds if a memory operand is connected to a definition with an unmodeled result, other than
+ * `UnmodeledDefinition` itself.
+ */
+ query predicate memoryOperandDefinitionIsUnmodeled(
+ Instruction instr, string message, IRFunction func, string funcText
+ ) {
+ exists(MemoryOperand operand, Instruction def |
+ operand = instr.getAnOperand() and
+ not operand instanceof UnmodeledUseOperand and
+ def = operand.getAnyDef() and
+ not def.isResultModeled() and
+ not def instanceof UnmodeledDefinitionInstruction and
+ message =
+ "Memory operand definition has unmodeled result, but is not the `UnmodeledDefinition` instruction in function '$@'" and
+ func = instr.getEnclosingIRFunction() and
+ funcText = Language::getIdentityString(func.getFunction())
+ )
+ }
+
/**
* Holds if operand `operand` consumes a value that was defined in
* a different function.
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 fc9d0758125..780f636ff10 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
@@ -190,14 +190,15 @@ 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)
}
/**
- * 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)
@@ -525,7 +526,7 @@ class ReturnValueInstruction extends ReturnInstruction {
final Instruction getReturnValue() { result = getReturnValueOperand().getDef() }
}
-class ReturnIndirectionInstruction extends Instruction {
+class ReturnIndirectionInstruction extends VariableInstruction {
ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection }
final SideEffectOperand getSideEffectOperand() { result = getAnOperand() }
@@ -535,6 +536,12 @@ class ReturnIndirectionInstruction extends Instruction {
final AddressOperand getSourceAddressOperand() { result = getAnOperand() }
final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() }
+
+ /**
+ * Gets the parameter for which this instruction reads the final pointed-to value within the
+ * function.
+ */
+ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
}
class CopyInstruction extends Instruction {
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/internal/TranslatedCall.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll
index 088cb7fa4cd..22f104e12c8 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
@@ -324,6 +324,16 @@ class TranslatedFunctionCall extends TranslatedCallExpr, TranslatedDirectCall {
override predicate hasWriteSideEffect() {
not expr.getTarget().(SideEffectFunction).hasOnlySpecificWriteSideEffects()
}
+
+ override Instruction getQualifierResult() {
+ hasQualifier() and
+ result = getQualifier().getResult()
+ }
+
+ override predicate hasQualifier() {
+ exists(getQualifier()) and
+ not exists(MemberFunction func | expr.getTarget() = func and func.isStatic())
+ }
}
/**
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 c3788f4c8da..ab354a3dca1 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
@@ -463,7 +467,9 @@ newtype TTranslatedElement =
expr = call.getArgument(n).getFullyConverted()
or
expr = call.getQualifier().getFullyConverted() and
- n = -1
+ n = -1 and
+ // Exclude calls to static member functions. They don't modify the qualifier
+ not exists(MemberFunction func | func = call.getTarget() and func.isStatic())
) and
(
call.getTarget().(SideEffectFunction).hasSpecificReadSideEffect(n, _) and
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..1d305ce46d0 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
@@ -1100,13 +1100,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
@@ -1735,20 +1758,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
(
(
@@ -1843,13 +1866,13 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont
)
}
- 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 +1882,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 +1967,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 getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ result = super.getInstructionOperand(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())
+ }
}
/**
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 1bfc1c8275f..67b9622c3be 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.
@@ -312,7 +317,7 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
/**
* Holds if the function has a non-`void` return type.
*/
- final predicate hasReturnValue() { not func.getUnspecifiedType() instanceof VoidType }
+ final predicate hasReturnValue() { hasReturnValue(func) }
/**
* Gets the single `UnmodeledDefinition` instruction for this function.
@@ -454,7 +459,7 @@ abstract class TranslatedParameter extends TranslatedElement {
result = getInstruction(InitializerVariableAddressTag())
or
operandTag instanceof LoadOperandTag and
- result = getInstruction(InitializerStoreTag())
+ result = getTranslatedFunction(getFunction()).getUnmodeledDefinitionInstruction()
)
or
tag = InitializerIndirectStoreTag() and
@@ -744,4 +749,9 @@ class TranslatedReadEffect extends TranslatedElement, TTranslatedReadEffect {
operandTag = sideEffectOperand() and
result = getUnknownType()
}
+
+ final override IRVariable getInstructionVariable(InstructionTag tag) {
+ tag = OnlyInstructionTag() and
+ result = getIRUserVariable(getFunction(), param)
+ }
}
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..a4c9d487437 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.
*/
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..dce1717bdc9 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
@@ -101,23 +101,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 +130,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/IRSanity.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll
index 94d0192fe18..edf4bc00259 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll
@@ -149,6 +149,26 @@ module InstructionSanity {
count(instr.getBlock().getAPredecessor()) < 2
}
+ /**
+ * Holds if a memory operand is connected to a definition with an unmodeled result, other than
+ * `UnmodeledDefinition` itself.
+ */
+ query predicate memoryOperandDefinitionIsUnmodeled(
+ Instruction instr, string message, IRFunction func, string funcText
+ ) {
+ exists(MemoryOperand operand, Instruction def |
+ operand = instr.getAnOperand() and
+ not operand instanceof UnmodeledUseOperand and
+ def = operand.getAnyDef() and
+ not def.isResultModeled() and
+ not def instanceof UnmodeledDefinitionInstruction and
+ message =
+ "Memory operand definition has unmodeled result, but is not the `UnmodeledDefinition` instruction in function '$@'" and
+ func = instr.getEnclosingIRFunction() and
+ funcText = Language::getIdentityString(func.getFunction())
+ )
+ }
+
/**
* Holds if operand `operand` consumes a value that was defined in
* a different function.
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 fc9d0758125..780f636ff10 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
@@ -190,14 +190,15 @@ 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)
}
/**
- * 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)
@@ -525,7 +526,7 @@ class ReturnValueInstruction extends ReturnInstruction {
final Instruction getReturnValue() { result = getReturnValueOperand().getDef() }
}
-class ReturnIndirectionInstruction extends Instruction {
+class ReturnIndirectionInstruction extends VariableInstruction {
ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection }
final SideEffectOperand getSideEffectOperand() { result = getAnOperand() }
@@ -535,6 +536,12 @@ class ReturnIndirectionInstruction extends Instruction {
final AddressOperand getSourceAddressOperand() { result = getAnOperand() }
final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() }
+
+ /**
+ * Gets the parameter for which this instruction reads the final pointed-to value within the
+ * function.
+ */
+ final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
}
class CopyInstruction extends Instruction {
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/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/internal/CppType.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll
index 9e8044ede4f..d297097abd9 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll
@@ -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
@@ -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
@@ -438,15 +449,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
))
}
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/models/implementations/Allocation.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll
index c6766983889..782800d0fa2 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll
@@ -1,3 +1,9 @@
+/**
+ * Provides implementation classes modelling various methods of allocation
+ * (`malloc`, `new` etc). See `semmle.code.cpp.models.interfaces.Allocation`
+ * for usage information.
+ */
+
import semmle.code.cpp.models.interfaces.Allocation
/**
@@ -83,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
)
)
}
@@ -163,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
)
)
}
@@ -249,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`.
*/
@@ -266,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
@@ -274,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() }
}
@@ -292,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()) }
}
@@ -312,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 d2e4951e436..2ef355bf398 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll
@@ -1,4 +1,10 @@
-import semmle.code.cpp.models.interfaces.Allocation
+/**
+ * Provides implementation classes modelling various methods of deallocation
+ * (`free`, `delete` etc). See `semmle.code.cpp.models.interfaces.Deallocation`
+ * for usage information.
+ */
+
+import semmle.code.cpp.models.interfaces.Deallocation
/**
* A deallocation function such as `free`.
@@ -13,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/Gets.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll
index aa4091fd7f2..e5e45729e0d 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,9 @@ 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()
+ }
}
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/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/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/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/SimpleRangeAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll
index 22e5f5ac83e..7751b47cb6c 100644
--- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll
+++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll
@@ -118,36 +118,79 @@ 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
+ )
+ }
+}
+
/** 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 +288,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 +697,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 = left / 2.pow(right)
+ )
}
/** Only to be called by `getTruncatedUpperBounds`. */
@@ -794,6 +864,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 = left / 2.pow(right)
+ )
}
/**
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/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/security/TaintTrackingImpl.qll
index 3a37b43b319..a24820b277f 100644
--- a/cpp/ql/src/semmle/code/cpp/security/TaintTrackingImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/TaintTrackingImpl.qll
@@ -328,14 +328,24 @@ GlobalOrNamespaceVariable globalVarFromId(string id) {
}
/**
- * A variable that has any kind of upper-bound check anywhere in the program
+ * A variable that has any kind of upper-bound check anywhere in the program. This is
+ * biased towards being inclusive because there are a lot of valid ways of doing an
+ * upper bounds checks if we don't consider where it occurs, for example:
+ * ```
+ * if (x < 10) { sink(x); }
+ *
+ * if (10 > y) { sink(y); }
+ *
+ * if (z > 10) { z = 10; }
+ * sink(z);
+ * ```
*/
private predicate hasUpperBoundsCheck(Variable var) {
exists(RelationalOperation oper, VariableAccess access |
- oper.getLeftOperand() = access and
+ oper.getAnOperand() = access and
access.getTarget() = var and
// Comparing to 0 is not an upper bound check
- not oper.getRightOperand().getValue() = "0"
+ not oper.getAnOperand().getValue() = "0"
)
}
diff --git a/cpp/ql/src/semmlecode.cpp.dbscheme b/cpp/ql/src/semmlecode.cpp.dbscheme
index d6ca4ebb768..874439f4c50 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(
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..9783a92a762
--- /dev/null
+++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/arraylengthanalysis/test.cpp
@@ -0,0 +1,90 @@
+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
+}
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..235ec0451e7 100644
--- a/cpp/ql/test/library-tests/allocators/allocators.ql
+++ b/cpp/ql/test/library-tests/allocators/allocators.ql
@@ -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/clang_ms/element.expected b/cpp/ql/test/library-tests/clang_ms/element.expected
index 16241c2e057..8580209ee9f 100644
--- a/cpp/ql/test/library-tests/clang_ms/element.expected
+++ b/cpp/ql/test/library-tests/clang_ms/element.expected
@@ -61,6 +61,7 @@
| file://:0:0:0:0 | auto |
| file://:0:0:0:0 | bool |
| file://:0:0:0:0 | char |
+| file://:0:0:0:0 | char8_t |
| file://:0:0:0:0 | char16_t |
| file://:0:0:0:0 | char32_t |
| file://:0:0:0:0 | const |
@@ -90,6 +91,7 @@
| file://:0:0:0:0 | implicit_int |
| file://:0:0:0:0 | inline |
| file://:0:0:0:0 | int |
+| file://:0:0:0:0 | is_consteval |
| file://:0:0:0:0 | is_constexpr |
| file://:0:0:0:0 | is_thread_local |
| file://:0:0:0:0 | long |
diff --git a/cpp/ql/test/library-tests/conditions/elements.expected b/cpp/ql/test/library-tests/conditions/elements.expected
index 4e94d425a8a..1e2ca174e66 100644
--- a/cpp/ql/test/library-tests/conditions/elements.expected
+++ b/cpp/ql/test/library-tests/conditions/elements.expected
@@ -38,6 +38,7 @@
| file://:0:0:0:0 | auto |
| file://:0:0:0:0 | bool |
| file://:0:0:0:0 | char |
+| file://:0:0:0:0 | char8_t |
| file://:0:0:0:0 | char16_t |
| file://:0:0:0:0 | char32_t |
| file://:0:0:0:0 | const |
@@ -65,6 +66,7 @@
| file://:0:0:0:0 | initializer for |
| file://:0:0:0:0 | inline |
| file://:0:0:0:0 | int |
+| file://:0:0:0:0 | is_consteval |
| file://:0:0:0:0 | is_constexpr |
| file://:0:0:0:0 | is_thread_local |
| file://:0:0:0:0 | long |
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/defaulttainttracking.cpp b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/defaulttainttracking.cpp
index 19496381fba..5dde846a559 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/defaulttainttracking.cpp
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/defaulttainttracking.cpp
@@ -88,6 +88,16 @@ void test_std_move() {
sink(std::move(getenv("VAR")));
}
+void flow_to_outparam(char ** ret, char *arg) {
+ *ret = arg;
+}
+
+void test_outparams() {
+ char *p2 = nullptr;
+ flow_to_outparam(&p2, getenv("VAR"));
+ sink(p2); // tainted
+}
+
struct Point {
int x;
int y;
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected
index 2b8cd83e6fe..cbcc1071902 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected
@@ -101,15 +101,20 @@
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:18:88:23 | call to getenv |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:18:88:30 | (reference to) |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | test_diff.cpp:1:11:1:20 | p#0 |
-| defaulttainttracking.cpp:103:9:103:14 | call to getenv | defaulttainttracking.cpp:10:11:10:13 | p#0 |
-| defaulttainttracking.cpp:103:9:103:14 | call to getenv | defaulttainttracking.cpp:96:10:96:13 | this |
-| defaulttainttracking.cpp:103:9:103:14 | call to getenv | defaulttainttracking.cpp:97:10:97:13 | this |
-| defaulttainttracking.cpp:103:9:103:14 | call to getenv | defaulttainttracking.cpp:103:9:103:14 | call to getenv |
-| defaulttainttracking.cpp:103:9:103:14 | call to getenv | defaulttainttracking.cpp:103:9:103:24 | (int)... |
-| defaulttainttracking.cpp:103:9:103:14 | call to getenv | defaulttainttracking.cpp:103:9:103:24 | access to array |
-| defaulttainttracking.cpp:103:9:103:14 | call to getenv | defaulttainttracking.cpp:104:10:104:10 | x |
-| defaulttainttracking.cpp:103:9:103:14 | call to getenv | defaulttainttracking.cpp:106:3:106:3 | p |
-| defaulttainttracking.cpp:103:9:103:14 | call to getenv | test_diff.cpp:2:11:2:13 | p#0 |
+| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:9:11:9:20 | p#0 |
+| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:91:42:91:44 | arg |
+| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:92:12:92:14 | arg |
+| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:96:11:96:12 | p2 |
+| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:97:27:97:32 | call to getenv |
+| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:98:10:98:11 | (const char *)... |
+| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:98:10:98:11 | p2 |
+| defaulttainttracking.cpp:97:27:97:32 | call to getenv | test_diff.cpp:1:11:1:20 | p#0 |
+| defaulttainttracking.cpp:113:9:113:14 | call to getenv | defaulttainttracking.cpp:10:11:10:13 | p#0 |
+| defaulttainttracking.cpp:113:9:113:14 | call to getenv | defaulttainttracking.cpp:113:9:113:14 | call to getenv |
+| defaulttainttracking.cpp:113:9:113:14 | call to getenv | defaulttainttracking.cpp:113:9:113:24 | (int)... |
+| defaulttainttracking.cpp:113:9:113:14 | call to getenv | defaulttainttracking.cpp:113:9:113:24 | access to array |
+| defaulttainttracking.cpp:113:9:113:14 | call to getenv | defaulttainttracking.cpp:114:10:114:10 | x |
+| defaulttainttracking.cpp:113:9:113:14 | call to getenv | test_diff.cpp:2:11:2:13 | p#0 |
| globals.cpp:5:20:5:25 | call to getenv | globals.cpp:2:17:2:25 | sinkParam |
| globals.cpp:5:20:5:25 | call to getenv | globals.cpp:5:12:5:16 | local |
| globals.cpp:5:20:5:25 | call to getenv | globals.cpp:5:20:5:25 | call to getenv |
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected
index 3e152a00a14..1023863c460 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected
@@ -15,13 +15,18 @@
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:32 | (reference dereference) | IR only |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:18:88:30 | (reference to) | IR only |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | test_diff.cpp:1:11:1:20 | p#0 | IR only |
-| defaulttainttracking.cpp:103:9:103:14 | call to getenv | defaulttainttracking.cpp:10:11:10:13 | p#0 | IR only |
-| defaulttainttracking.cpp:103:9:103:14 | call to getenv | defaulttainttracking.cpp:96:10:96:13 | this | IR only |
-| defaulttainttracking.cpp:103:9:103:14 | call to getenv | defaulttainttracking.cpp:97:10:97:13 | this | IR only |
-| defaulttainttracking.cpp:103:9:103:14 | call to getenv | defaulttainttracking.cpp:103:5:103:5 | x | AST only |
-| defaulttainttracking.cpp:103:9:103:14 | call to getenv | defaulttainttracking.cpp:104:10:104:10 | x | IR only |
-| defaulttainttracking.cpp:103:9:103:14 | call to getenv | defaulttainttracking.cpp:106:3:106:3 | p | IR only |
-| defaulttainttracking.cpp:103:9:103:14 | call to getenv | test_diff.cpp:2:11:2:13 | p#0 | IR only |
+| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:9:11:9:20 | p#0 | IR only |
+| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:91:31:91:33 | ret | AST only |
+| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:92:5:92:8 | * ... | AST only |
+| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:92:6:92:8 | ret | AST only |
+| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:96:11:96:12 | p2 | IR only |
+| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:98:10:98:11 | (const char *)... | IR only |
+| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:98:10:98:11 | p2 | IR only |
+| defaulttainttracking.cpp:97:27:97:32 | call to getenv | test_diff.cpp:1:11:1:20 | p#0 | IR only |
+| defaulttainttracking.cpp:113:9:113:14 | call to getenv | defaulttainttracking.cpp:10:11:10:13 | p#0 | IR only |
+| defaulttainttracking.cpp:113:9:113:14 | call to getenv | defaulttainttracking.cpp:113:5:113:5 | x | AST only |
+| defaulttainttracking.cpp:113:9:113:14 | call to getenv | defaulttainttracking.cpp:114:10:114:10 | x | IR only |
+| defaulttainttracking.cpp:113:9:113:14 | call to getenv | test_diff.cpp:2:11:2:13 | p#0 | IR only |
| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:13:5:13:11 | global1 | AST only |
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:23:5:23:11 | global2 | AST only |
| test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:11:104:20 | (...) | IR only |
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected
index f4bd52174f0..1e91d340250 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected
@@ -31,6 +31,8 @@ localCallNodes
postIsNotPre
postHasUniquePre
uniquePostUpdate
+| ref.cpp:83:5:83:17 | Chi | Node has multiple PostUpdateNodes. |
+| ref.cpp:109:5:109:22 | Chi | Node has multiple PostUpdateNodes. |
postIsInSameCallable
reverseRead
storeIsPostUpdate
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow.expected
index c88a62fc2f1..4bd0b9fdc4d 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow.expected
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow.expected
@@ -44,7 +44,6 @@
| test.cpp:383:12:383:13 | 0 | test.cpp:384:33:384:35 | tmp |
| test.cpp:383:12:383:13 | 0 | test.cpp:385:8:385:10 | tmp |
| test.cpp:384:10:384:13 | & ... | test.cpp:384:3:384:8 | call to memcpy |
-| test.cpp:384:10:384:13 | ref arg & ... | test.cpp:384:3:384:8 | call to memcpy |
| test.cpp:384:10:384:13 | ref arg & ... | test.cpp:384:33:384:35 | tmp |
| test.cpp:384:10:384:13 | ref arg & ... | test.cpp:385:8:385:10 | tmp |
| test.cpp:384:11:384:13 | tmp | test.cpp:384:10:384:13 | & ... |
@@ -59,7 +58,6 @@
| test.cpp:389:12:389:13 | 0 | test.cpp:394:10:394:12 | tmp |
| test.cpp:390:19:390:21 | tmp | test.cpp:390:18:390:21 | & ... |
| test.cpp:391:10:391:13 | & ... | test.cpp:391:3:391:8 | call to memcpy |
-| test.cpp:391:10:391:13 | ref arg & ... | test.cpp:391:3:391:8 | call to memcpy |
| test.cpp:391:10:391:13 | ref arg & ... | test.cpp:391:33:391:35 | tmp |
| test.cpp:391:10:391:13 | ref arg & ... | test.cpp:392:8:392:10 | tmp |
| test.cpp:391:10:391:13 | ref arg & ... | test.cpp:394:10:394:12 | tmp |
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp
index 6c445fb76c1..666edf7a177 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp
@@ -460,3 +460,13 @@ void throughStmtExpr(int source1, int clean1) {
});
sink(local); // tainted
}
+
+void intOutparamSource(int *p) {
+ *p = source();
+}
+
+void viaOutparam() {
+ int x = 0;
+ intOutparamSource(&x);
+ sink(x); // tainted [FALSE NEGATIVE]
+}
\ No newline at end of file
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected
index 97a2eca5b2d..06aea8625d0 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected
@@ -30,10 +30,6 @@
| ref.cpp:53:17:53:18 | ref.cpp:62:10:62:11 | AST only |
| ref.cpp:53:21:53:22 | ref.cpp:65:10:65:11 | AST only |
| ref.cpp:55:23:55:28 | ref.cpp:56:10:56:11 | AST only |
-| ref.cpp:94:15:94:20 | ref.cpp:129:13:129:15 | AST only |
-| ref.cpp:109:15:109:20 | ref.cpp:132:13:132:15 | AST only |
-| ref.cpp:122:23:122:28 | ref.cpp:123:13:123:15 | AST only |
-| ref.cpp:125:19:125:24 | ref.cpp:126:13:126:15 | AST only |
| test.cpp:75:7:75:8 | test.cpp:76:8:76:9 | AST only |
| test.cpp:83:7:83:8 | test.cpp:84:8:84:18 | AST only |
| test.cpp:83:7:83:8 | test.cpp:86:8:86:9 | AST only |
@@ -44,9 +40,6 @@
| test.cpp:359:13:359:18 | test.cpp:365:10:365:14 | AST only |
| test.cpp:373:13:373:18 | test.cpp:369:10:369:14 | AST only |
| test.cpp:373:13:373:18 | test.cpp:375:10:375:14 | AST only |
-| test.cpp:382:48:382:54 | test.cpp:385:8:385:10 | AST only |
-| test.cpp:388:53:388:59 | test.cpp:392:8:392:10 | AST only |
-| test.cpp:388:53:388:59 | test.cpp:394:10:394:12 | AST only |
| test.cpp:399:7:399:9 | test.cpp:401:8:401:10 | AST only |
| test.cpp:405:7:405:9 | test.cpp:408:8:408:10 | AST only |
| test.cpp:416:7:416:11 | test.cpp:418:8:418:12 | AST only |
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected
index 7ea30c7e4ee..26567df5831 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected
@@ -40,6 +40,10 @@
| globals.cpp:19:10:19:24 | flowTestGlobal2 | globals.cpp:23:23:23:28 | call to source |
| lambdas.cpp:35:8:35:8 | a | lambdas.cpp:8:10:8:15 | call to source |
| lambdas.cpp:41:8:41:8 | (reference dereference) | lambdas.cpp:8:10:8:15 | call to source |
+| ref.cpp:123:13:123:15 | val | ref.cpp:122:23:122:28 | call to source |
+| ref.cpp:126:13:126:15 | val | ref.cpp:125:19:125:24 | call to source |
+| ref.cpp:129:13:129:15 | val | ref.cpp:94:15:94:20 | call to source |
+| ref.cpp:132:13:132:15 | val | ref.cpp:109:15:109:20 | call to source |
| test.cpp:7:8:7:9 | t1 | test.cpp:6:12:6:17 | call to source |
| test.cpp:9:8:9:9 | t1 | test.cpp:6:12:6:17 | call to source |
| test.cpp:10:8:10:9 | t2 | test.cpp:6:12:6:17 | call to source |
@@ -62,6 +66,9 @@
| test.cpp:266:12:266:12 | x | test.cpp:265:22:265:27 | call to source |
| test.cpp:289:14:289:14 | x | test.cpp:305:17:305:22 | call to source |
| test.cpp:318:7:318:7 | x | test.cpp:314:4:314:9 | call to source |
+| test.cpp:385:8:385:10 | tmp | test.cpp:382:48:382:54 | source1 |
+| test.cpp:392:8:392:10 | tmp | test.cpp:388:53:388:59 | source1 |
+| test.cpp:394:10:394:12 | tmp | test.cpp:388:53:388:59 | source1 |
| test.cpp:450:9:450:22 | (statement expression) | test.cpp:449:26:449:32 | source1 |
| test.cpp:461:8:461:12 | local | test.cpp:449:26:449:32 | source1 |
| true_upon_entry.cpp:13:8:13:8 | x | true_upon_entry.cpp:9:11:9:16 | call to source |
diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected
index 1714355138c..227023fda16 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected
+++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected
@@ -19,6 +19,7 @@ unreachableNodeCCtx
localCallNodes
postIsNotPre
postHasUniquePre
+| simple.cpp:65:5:65:22 | Store | PostUpdateNode should have one pre-update node but has 0. |
uniquePostUpdate
postIsInSameCallable
reverseRead
diff --git a/cpp/ql/test/library-tests/dataflow/fields/flow.expected b/cpp/ql/test/library-tests/dataflow/fields/flow.expected
index ca851dc8974..a03bd0a8cb8 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/flow.expected
+++ b/cpp/ql/test/library-tests/dataflow/fields/flow.expected
@@ -221,6 +221,15 @@ edges
| simple.cpp:48:9:48:9 | g [b_] | simple.cpp:26:15:26:15 | f [b_] |
| simple.cpp:51:9:51:9 | h [a_] | simple.cpp:26:15:26:15 | f [a_] |
| simple.cpp:51:9:51:9 | h [b_] | simple.cpp:26:15:26:15 | f [b_] |
+| simple.cpp:65:5:65:5 | a [post update] [i] | simple.cpp:67:10:67:11 | a2 [i] |
+| simple.cpp:65:5:65:22 | ... = ... | simple.cpp:65:5:65:5 | a [post update] [i] |
+| simple.cpp:65:11:65:20 | call to user_input | simple.cpp:65:5:65:22 | ... = ... |
+| simple.cpp:67:10:67:11 | a2 [i] | simple.cpp:67:13:67:13 | i |
+| simple.cpp:83:9:83:10 | f2 [post update] [f1] | simple.cpp:83:9:83:10 | this [post update] [f2, f1] |
+| simple.cpp:83:9:83:10 | this [post update] [f2, f1] | simple.cpp:84:14:84:20 | this [f2, f1] |
+| simple.cpp:83:9:83:28 | ... = ... | simple.cpp:83:9:83:10 | f2 [post update] [f1] |
+| simple.cpp:83:17:83:26 | call to user_input | simple.cpp:83:9:83:28 | ... = ... |
+| simple.cpp:84:14:84:20 | this [f2, f1] | simple.cpp:84:14:84:20 | call to getf2f1 |
| struct_init.c:14:24:14:25 | ab [a] | struct_init.c:15:8:15:9 | ab [a] |
| struct_init.c:15:8:15:9 | ab [a] | struct_init.c:15:12:15:12 | a |
| struct_init.c:20:17:20:36 | {...} [a] | struct_init.c:22:8:22:9 | ab [a] |
@@ -504,6 +513,17 @@ nodes
| simple.cpp:48:9:48:9 | g [b_] | semmle.label | g [b_] |
| simple.cpp:51:9:51:9 | h [a_] | semmle.label | h [a_] |
| simple.cpp:51:9:51:9 | h [b_] | semmle.label | h [b_] |
+| simple.cpp:65:5:65:5 | a [post update] [i] | semmle.label | a [post update] [i] |
+| simple.cpp:65:5:65:22 | ... = ... | semmle.label | ... = ... |
+| simple.cpp:65:11:65:20 | call to user_input | semmle.label | call to user_input |
+| simple.cpp:67:10:67:11 | a2 [i] | semmle.label | a2 [i] |
+| simple.cpp:67:13:67:13 | i | semmle.label | i |
+| simple.cpp:83:9:83:10 | f2 [post update] [f1] | semmle.label | f2 [post update] [f1] |
+| simple.cpp:83:9:83:10 | this [post update] [f2, f1] | semmle.label | this [post update] [f2, f1] |
+| simple.cpp:83:9:83:28 | ... = ... | semmle.label | ... = ... |
+| simple.cpp:83:17:83:26 | call to user_input | semmle.label | call to user_input |
+| simple.cpp:84:14:84:20 | call to getf2f1 | semmle.label | call to getf2f1 |
+| simple.cpp:84:14:84:20 | this [f2, f1] | semmle.label | this [f2, f1] |
| struct_init.c:14:24:14:25 | ab [a] | semmle.label | ab [a] |
| struct_init.c:15:8:15:9 | ab [a] | semmle.label | ab [a] |
| struct_init.c:15:12:15:12 | a | semmle.label | a |
@@ -580,6 +600,8 @@ nodes
| simple.cpp:28:12:28:12 | call to a | simple.cpp:41:12:41:21 | call to user_input | simple.cpp:28:12:28:12 | call to a | call to a flows from $@ | simple.cpp:41:12:41:21 | call to user_input | call to user_input |
| simple.cpp:29:12:29:12 | call to b | simple.cpp:40:12:40:21 | call to user_input | simple.cpp:29:12:29:12 | call to b | call to b flows from $@ | simple.cpp:40:12:40:21 | call to user_input | call to user_input |
| simple.cpp:29:12:29:12 | call to b | simple.cpp:42:12:42:21 | call to user_input | simple.cpp:29:12:29:12 | call to b | call to b flows from $@ | simple.cpp:42:12:42:21 | call to user_input | call to user_input |
+| simple.cpp:67:13:67:13 | i | simple.cpp:65:11:65:20 | call to user_input | simple.cpp:67:13:67:13 | i | i flows from $@ | simple.cpp:65:11:65:20 | call to user_input | call to user_input |
+| simple.cpp:84:14:84:20 | call to getf2f1 | simple.cpp:83:17:83:26 | call to user_input | simple.cpp:84:14:84:20 | call to getf2f1 | call to getf2f1 flows from $@ | simple.cpp:83:17:83:26 | call to user_input | call to user_input |
| struct_init.c:15:12:15:12 | a | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:20:20:20:29 | call to user_input | call to user_input |
| struct_init.c:15:12:15:12 | a | struct_init.c:27:7:27:16 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:27:7:27:16 | call to user_input | call to user_input |
| struct_init.c:15:12:15:12 | a | struct_init.c:40:20:40:29 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:40:20:40:29 | call to user_input | call to user_input |
diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected
new file mode 100644
index 00000000000..418ad5169c2
--- /dev/null
+++ b/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected
@@ -0,0 +1,134 @@
+edges
+| A.cpp:98:12:98:18 | new | A.cpp:100:5:100:13 | Store |
+| A.cpp:100:5:100:13 | Chi [a] | A.cpp:101:8:101:9 | BufferReadSideEffect [a] |
+| A.cpp:100:5:100:13 | Store | A.cpp:100:5:100:13 | Chi [a] |
+| A.cpp:101:8:101:9 | BufferReadSideEffect [a] | A.cpp:103:14:103:14 | *c [a] |
+| A.cpp:103:14:103:14 | *c [a] | A.cpp:107:16:107:16 | a |
+| A.cpp:103:14:103:14 | *c [a] | A.cpp:107:16:107:16 | a |
+| A.cpp:107:16:107:16 | a | A.cpp:107:12:107:16 | (void *)... |
+| A.cpp:142:7:142:20 | Chi [c] | A.cpp:151:18:151:18 | D output argument [c] |
+| A.cpp:142:7:142:20 | Store | A.cpp:142:7:142:20 | Chi [c] |
+| A.cpp:142:14:142:20 | new | A.cpp:142:7:142:20 | Store |
+| A.cpp:151:18:151:18 | Chi [c] | A.cpp:154:13:154:13 | c |
+| A.cpp:151:18:151:18 | Chi [c] | A.cpp:154:13:154:13 | c |
+| A.cpp:151:18:151:18 | D output argument [c] | A.cpp:151:18:151:18 | Chi [c] |
+| A.cpp:154:13:154:13 | c | A.cpp:154:10:154:13 | (void *)... |
+| aliasing.cpp:9:3:9:22 | Chi [m1] | aliasing.cpp:25:17:25:19 | pointerSetter output argument [m1] |
+| aliasing.cpp:9:3:9:22 | Store | aliasing.cpp:9:3:9:22 | Chi [m1] |
+| aliasing.cpp:9:11:9:20 | call to user_input | aliasing.cpp:9:3:9:22 | Store |
+| aliasing.cpp:13:3:13:21 | Chi [m1] | aliasing.cpp:26:19:26:20 | referenceSetter output argument [m1] |
+| aliasing.cpp:13:3:13:21 | Store | aliasing.cpp:13:3:13:21 | Chi [m1] |
+| aliasing.cpp:13:10:13:19 | call to user_input | aliasing.cpp:13:3:13:21 | Store |
+| aliasing.cpp:25:17:25:19 | Chi [m1] | aliasing.cpp:29:11:29:12 | m1 |
+| aliasing.cpp:25:17:25:19 | pointerSetter output argument [m1] | aliasing.cpp:25:17:25:19 | Chi [m1] |
+| aliasing.cpp:26:19:26:20 | Chi [m1] | aliasing.cpp:30:11:30:12 | m1 |
+| aliasing.cpp:26:19:26:20 | referenceSetter output argument [m1] | aliasing.cpp:26:19:26:20 | Chi [m1] |
+| aliasing.cpp:37:13:37:22 | call to user_input | aliasing.cpp:38:11:38:12 | m1 |
+| aliasing.cpp:42:11:42:20 | call to user_input | aliasing.cpp:43:13:43:14 | m1 |
+| aliasing.cpp:60:3:60:22 | Chi [m1] | aliasing.cpp:61:13:61:14 | Store [m1] |
+| aliasing.cpp:60:3:60:22 | Store | aliasing.cpp:60:3:60:22 | Chi [m1] |
+| aliasing.cpp:60:11:60:20 | call to user_input | aliasing.cpp:60:3:60:22 | Store |
+| aliasing.cpp:61:13:61:14 | Store [m1] | aliasing.cpp:62:14:62:15 | m1 |
+| aliasing.cpp:79:11:79:20 | call to user_input | aliasing.cpp:80:12:80:13 | m1 |
+| aliasing.cpp:86:10:86:19 | call to user_input | aliasing.cpp:87:12:87:13 | m1 |
+| aliasing.cpp:92:12:92:21 | call to user_input | aliasing.cpp:93:12:93:13 | m1 |
+| by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] | by_reference.cpp:69:22:69:23 | BufferReadSideEffect [a] |
+| by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] |
+| by_reference.cpp:69:22:69:23 | BufferReadSideEffect [a] | by_reference.cpp:69:8:69:20 | call to nonMemberGetA |
+| simple.cpp:65:5:65:22 | Store [i] | simple.cpp:66:12:66:12 | Store [i] |
+| simple.cpp:65:11:65:20 | call to user_input | simple.cpp:65:5:65:22 | Store [i] |
+| simple.cpp:66:12:66:12 | Store [i] | simple.cpp:67:13:67:13 | i |
+| struct_init.c:14:24:14:25 | *ab [a] | struct_init.c:15:12:15:12 | a |
+| struct_init.c:20:20:20:29 | Chi [a] | struct_init.c:24:10:24:12 | BufferReadSideEffect [a] |
+| struct_init.c:20:20:20:29 | Store | struct_init.c:20:20:20:29 | Chi [a] |
+| struct_init.c:20:20:20:29 | call to user_input | struct_init.c:20:20:20:29 | Store |
+| struct_init.c:20:20:20:29 | call to user_input | struct_init.c:22:11:22:11 | a |
+| struct_init.c:24:10:24:12 | BufferReadSideEffect [a] | struct_init.c:14:24:14:25 | *ab [a] |
+| struct_init.c:27:7:27:16 | Chi [a] | struct_init.c:36:10:36:24 | BufferReadSideEffect [a] |
+| struct_init.c:27:7:27:16 | Store | struct_init.c:27:7:27:16 | Chi [a] |
+| struct_init.c:27:7:27:16 | call to user_input | struct_init.c:27:7:27:16 | Store |
+| struct_init.c:27:7:27:16 | call to user_input | struct_init.c:31:23:31:23 | a |
+| struct_init.c:36:10:36:24 | BufferReadSideEffect [a] | struct_init.c:14:24:14:25 | *ab [a] |
+nodes
+| A.cpp:98:12:98:18 | new | semmle.label | new |
+| A.cpp:100:5:100:13 | Chi [a] | semmle.label | Chi [a] |
+| A.cpp:100:5:100:13 | Store | semmle.label | Store |
+| A.cpp:101:8:101:9 | BufferReadSideEffect [a] | semmle.label | BufferReadSideEffect [a] |
+| A.cpp:103:14:103:14 | *c [a] | semmle.label | *c [a] |
+| A.cpp:107:12:107:16 | (void *)... | semmle.label | (void *)... |
+| A.cpp:107:16:107:16 | a | semmle.label | a |
+| A.cpp:107:16:107:16 | a | semmle.label | a |
+| A.cpp:142:7:142:20 | Chi [c] | semmle.label | Chi [c] |
+| A.cpp:142:7:142:20 | Store | semmle.label | Store |
+| A.cpp:142:14:142:20 | new | semmle.label | new |
+| A.cpp:151:18:151:18 | Chi [c] | semmle.label | Chi [c] |
+| A.cpp:151:18:151:18 | D output argument [c] | semmle.label | D output argument [c] |
+| A.cpp:154:10:154:13 | (void *)... | semmle.label | (void *)... |
+| A.cpp:154:13:154:13 | c | semmle.label | c |
+| A.cpp:154:13:154:13 | c | semmle.label | c |
+| aliasing.cpp:9:3:9:22 | Chi [m1] | semmle.label | Chi [m1] |
+| aliasing.cpp:9:3:9:22 | Store | semmle.label | Store |
+| aliasing.cpp:9:11:9:20 | call to user_input | semmle.label | call to user_input |
+| aliasing.cpp:13:3:13:21 | Chi [m1] | semmle.label | Chi [m1] |
+| aliasing.cpp:13:3:13:21 | Store | semmle.label | Store |
+| aliasing.cpp:13:10:13:19 | call to user_input | semmle.label | call to user_input |
+| aliasing.cpp:25:17:25:19 | Chi [m1] | semmle.label | Chi [m1] |
+| aliasing.cpp:25:17:25:19 | pointerSetter output argument [m1] | semmle.label | pointerSetter output argument [m1] |
+| aliasing.cpp:26:19:26:20 | Chi [m1] | semmle.label | Chi [m1] |
+| aliasing.cpp:26:19:26:20 | referenceSetter output argument [m1] | semmle.label | referenceSetter output argument [m1] |
+| aliasing.cpp:29:11:29:12 | m1 | semmle.label | m1 |
+| aliasing.cpp:30:11:30:12 | m1 | semmle.label | m1 |
+| aliasing.cpp:37:13:37:22 | call to user_input | semmle.label | call to user_input |
+| aliasing.cpp:38:11:38:12 | m1 | semmle.label | m1 |
+| aliasing.cpp:42:11:42:20 | call to user_input | semmle.label | call to user_input |
+| aliasing.cpp:43:13:43:14 | m1 | semmle.label | m1 |
+| aliasing.cpp:60:3:60:22 | Chi [m1] | semmle.label | Chi [m1] |
+| aliasing.cpp:60:3:60:22 | Store | semmle.label | Store |
+| aliasing.cpp:60:11:60:20 | call to user_input | semmle.label | call to user_input |
+| aliasing.cpp:61:13:61:14 | Store [m1] | semmle.label | Store [m1] |
+| aliasing.cpp:62:14:62:15 | m1 | semmle.label | m1 |
+| aliasing.cpp:79:11:79:20 | call to user_input | semmle.label | call to user_input |
+| aliasing.cpp:80:12:80:13 | m1 | semmle.label | m1 |
+| aliasing.cpp:86:10:86:19 | call to user_input | semmle.label | call to user_input |
+| aliasing.cpp:87:12:87:13 | m1 | semmle.label | m1 |
+| aliasing.cpp:92:12:92:21 | call to user_input | semmle.label | call to user_input |
+| aliasing.cpp:93:12:93:13 | m1 | semmle.label | m1 |
+| by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] | semmle.label | nonMemberSetA output argument [a] |
+| by_reference.cpp:68:21:68:30 | call to user_input | semmle.label | call to user_input |
+| by_reference.cpp:69:8:69:20 | call to nonMemberGetA | semmle.label | call to nonMemberGetA |
+| by_reference.cpp:69:22:69:23 | BufferReadSideEffect [a] | semmle.label | BufferReadSideEffect [a] |
+| simple.cpp:65:5:65:22 | Store [i] | semmle.label | Store [i] |
+| simple.cpp:65:11:65:20 | call to user_input | semmle.label | call to user_input |
+| simple.cpp:66:12:66:12 | Store [i] | semmle.label | Store [i] |
+| simple.cpp:67:13:67:13 | i | semmle.label | i |
+| struct_init.c:14:24:14:25 | *ab [a] | semmle.label | *ab [a] |
+| struct_init.c:15:12:15:12 | a | semmle.label | a |
+| struct_init.c:20:20:20:29 | Chi [a] | semmle.label | Chi [a] |
+| struct_init.c:20:20:20:29 | Store | semmle.label | Store |
+| struct_init.c:20:20:20:29 | call to user_input | semmle.label | call to user_input |
+| struct_init.c:22:11:22:11 | a | semmle.label | a |
+| struct_init.c:24:10:24:12 | BufferReadSideEffect [a] | semmle.label | BufferReadSideEffect [a] |
+| struct_init.c:27:7:27:16 | Chi [a] | semmle.label | Chi [a] |
+| struct_init.c:27:7:27:16 | Store | semmle.label | Store |
+| struct_init.c:27:7:27:16 | call to user_input | semmle.label | call to user_input |
+| struct_init.c:31:23:31:23 | a | semmle.label | a |
+| struct_init.c:36:10:36:24 | BufferReadSideEffect [a] | semmle.label | BufferReadSideEffect [a] |
+#select
+| A.cpp:107:12:107:16 | (void *)... | A.cpp:98:12:98:18 | new | A.cpp:107:12:107:16 | (void *)... | (void *)... flows from $@ | A.cpp:98:12:98:18 | new | new |
+| A.cpp:107:16:107:16 | a | A.cpp:98:12:98:18 | new | A.cpp:107:16:107:16 | a | a flows from $@ | A.cpp:98:12:98:18 | new | new |
+| A.cpp:154:10:154:13 | (void *)... | A.cpp:142:14:142:20 | new | A.cpp:154:10:154:13 | (void *)... | (void *)... flows from $@ | A.cpp:142:14:142:20 | new | new |
+| A.cpp:154:13:154:13 | c | A.cpp:142:14:142:20 | new | A.cpp:154:13:154:13 | c | c flows from $@ | A.cpp:142:14:142:20 | new | new |
+| aliasing.cpp:29:11:29:12 | m1 | aliasing.cpp:9:11:9:20 | call to user_input | aliasing.cpp:29:11:29:12 | m1 | m1 flows from $@ | aliasing.cpp:9:11:9:20 | call to user_input | call to user_input |
+| aliasing.cpp:30:11:30:12 | m1 | aliasing.cpp:13:10:13:19 | call to user_input | aliasing.cpp:30:11:30:12 | m1 | m1 flows from $@ | aliasing.cpp:13:10:13:19 | call to user_input | call to user_input |
+| aliasing.cpp:38:11:38:12 | m1 | aliasing.cpp:37:13:37:22 | call to user_input | aliasing.cpp:38:11:38:12 | m1 | m1 flows from $@ | aliasing.cpp:37:13:37:22 | call to user_input | call to user_input |
+| aliasing.cpp:43:13:43:14 | m1 | aliasing.cpp:42:11:42:20 | call to user_input | aliasing.cpp:43:13:43:14 | m1 | m1 flows from $@ | aliasing.cpp:42:11:42:20 | call to user_input | call to user_input |
+| aliasing.cpp:62:14:62:15 | m1 | aliasing.cpp:60:11:60:20 | call to user_input | aliasing.cpp:62:14:62:15 | m1 | m1 flows from $@ | aliasing.cpp:60:11:60:20 | call to user_input | call to user_input |
+| aliasing.cpp:80:12:80:13 | m1 | aliasing.cpp:79:11:79:20 | call to user_input | aliasing.cpp:80:12:80:13 | m1 | m1 flows from $@ | aliasing.cpp:79:11:79:20 | call to user_input | call to user_input |
+| aliasing.cpp:87:12:87:13 | m1 | aliasing.cpp:86:10:86:19 | call to user_input | aliasing.cpp:87:12:87:13 | m1 | m1 flows from $@ | aliasing.cpp:86:10:86:19 | call to user_input | call to user_input |
+| aliasing.cpp:93:12:93:13 | m1 | aliasing.cpp:92:12:92:21 | call to user_input | aliasing.cpp:93:12:93:13 | m1 | m1 flows from $@ | aliasing.cpp:92:12:92:21 | call to user_input | call to user_input |
+| by_reference.cpp:69:8:69:20 | call to nonMemberGetA | by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:69:8:69:20 | call to nonMemberGetA | call to nonMemberGetA flows from $@ | by_reference.cpp:68:21:68:30 | call to user_input | call to user_input |
+| simple.cpp:67:13:67:13 | i | simple.cpp:65:11:65:20 | call to user_input | simple.cpp:67:13:67:13 | i | i flows from $@ | simple.cpp:65:11:65:20 | call to user_input | call to user_input |
+| struct_init.c:15:12:15:12 | a | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:20:20:20:29 | call to user_input | call to user_input |
+| struct_init.c:15:12:15:12 | a | struct_init.c:27:7:27:16 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:27:7:27:16 | call to user_input | call to user_input |
+| struct_init.c:22:11:22:11 | a | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:22:11:22:11 | a | a flows from $@ | struct_init.c:20:20:20:29 | call to user_input | call to user_input |
+| struct_init.c:31:23:31:23 | a | struct_init.c:27:7:27:16 | call to user_input | struct_init.c:31:23:31:23 | a | a flows from $@ | struct_init.c:27:7:27:16 | call to user_input | call to user_input |
diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-flow.ql b/cpp/ql/test/library-tests/dataflow/fields/ir-flow.ql
new file mode 100644
index 00000000000..098c6b6bd27
--- /dev/null
+++ b/cpp/ql/test/library-tests/dataflow/fields/ir-flow.ql
@@ -0,0 +1,46 @@
+/**
+ * @kind path-problem
+ */
+
+import semmle.code.cpp.ir.dataflow.DataFlow
+import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
+import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
+import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl
+import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon
+import semmle.code.cpp.ir.IR
+import DataFlow::PathGraph
+import cpp
+
+class Conf extends DataFlow::Configuration {
+ Conf() { this = "FieldFlowConf" }
+
+ override predicate isSource(Node src) {
+ src.asExpr() instanceof NewExpr
+ or
+ src.asExpr().(Call).getTarget().hasName("user_input")
+ or
+ exists(FunctionCall fc |
+ fc.getAnArgument() = src.asDefiningArgument() and
+ fc.getTarget().hasName("argument_source")
+ )
+ }
+
+ override predicate isSink(Node sink) {
+ exists(Call c |
+ c.getTarget().hasName("sink") and
+ c.getAnArgument() = sink.asExpr()
+ )
+ }
+
+ override predicate isAdditionalFlowStep(Node a, Node b) {
+ b.asPartialDefinition() =
+ any(Call c | c.getTarget().hasName("insert") and c.getAnArgument() = a.asExpr())
+ .getQualifier()
+ or
+ b.asExpr().(AddressOfExpr).getOperand() = a.asExpr()
+ }
+}
+
+from DataFlow::PathNode src, DataFlow::PathNode sink, Conf conf
+where conf.hasFlowPath(src, sink)
+select sink, src, sink, sink + " flows from $@", src, src.toString()
diff --git a/cpp/ql/test/library-tests/dataflow/fields/simple.cpp b/cpp/ql/test/library-tests/dataflow/fields/simple.cpp
index 6f36fa6551d..e286fda9350 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/simple.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/simple.cpp
@@ -53,4 +53,36 @@ void foo()
// Nothing should alert
bar(i);
}
+
+struct A
+{
+ int i;
+};
+
+void single_field_test()
+{
+ A a;
+ a.i = user_input();
+ A a2 = a;
+ sink(a2.i);
+}
+
+struct C {
+ int f1;
+};
+
+struct C2
+{
+ C f2;
+
+ int getf2f1() {
+ return f2.f1;
+ }
+
+ void m() {
+ f2.f1 = user_input();
+ sink(getf2f1()); // flow
+ }
+};
+
} // namespace Simple
diff --git a/cpp/ql/test/library-tests/dataflow/partialdefinitions/partialdefinitions.ql b/cpp/ql/test/library-tests/dataflow/partialdefinitions/partialdefinitions.ql
index db34eb16cb8..bc14c655159 100644
--- a/cpp/ql/test/library-tests/dataflow/partialdefinitions/partialdefinitions.ql
+++ b/cpp/ql/test/library-tests/dataflow/partialdefinitions/partialdefinitions.ql
@@ -1,4 +1,5 @@
import semmle.code.cpp.dataflow.internal.FlowVar
from PartialDefinition def
-select def, def.getDefinedExpr(), def.getSubBasicBlockStart()
+select def.getActualLocation().toString(), "partial def of " + def.toString(), def.getDefinedExpr(),
+ def.getSubBasicBlockStart()
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
index 72433bccfb4..524884a2d20 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
@@ -152,6 +152,16 @@
| stl.cpp:105:2:105:4 | ss1 [post update] | stl.cpp:110:7:110:9 | ss1 | |
| stl.cpp:106:2:106:4 | ss2 [post update] | stl.cpp:109:7:109:9 | ss2 | |
| stl.cpp:106:2:106:4 | ss2 [post update] | stl.cpp:111:7:111:9 | ss2 | |
+| stl.cpp:124:16:124:28 | call to basic_string | stl.cpp:125:7:125:11 | path1 | |
+| stl.cpp:124:17:124:26 | call to user_input | stl.cpp:124:16:124:28 | call to basic_string | TAINT |
+| stl.cpp:125:7:125:11 | path1 | stl.cpp:125:13:125:17 | call to c_str | TAINT |
+| stl.cpp:128:10:128:19 | call to user_input | stl.cpp:128:10:128:21 | call to basic_string | TAINT |
+| stl.cpp:128:10:128:21 | call to basic_string | stl.cpp:128:2:128:21 | ... = ... | |
+| stl.cpp:128:10:128:21 | call to basic_string | stl.cpp:129:7:129:11 | path2 | |
+| stl.cpp:129:7:129:11 | path2 | stl.cpp:129:13:129:17 | call to c_str | TAINT |
+| stl.cpp:131:15:131:24 | call to user_input | stl.cpp:131:15:131:27 | call to basic_string | TAINT |
+| stl.cpp:131:15:131:27 | call to basic_string | stl.cpp:132:7:132:11 | path3 | |
+| stl.cpp:132:7:132:11 | path3 | stl.cpp:132:13:132:17 | call to c_str | TAINT |
| taint.cpp:4:27:4:33 | source1 | taint.cpp:6:13:6:19 | source1 | |
| taint.cpp:4:40:4:45 | clean1 | taint.cpp:5:8:5:13 | clean1 | |
| taint.cpp:4:40:4:45 | clean1 | taint.cpp:6:3:6:8 | clean1 | |
@@ -289,7 +299,6 @@
| taint.cpp:165:24:165:24 | 0 | taint.cpp:165:22:165:25 | {...} | TAINT |
| taint.cpp:168:8:168:14 | ref arg tainted | taint.cpp:172:18:172:24 | tainted | |
| taint.cpp:170:10:170:15 | buffer | taint.cpp:170:3:170:8 | call to strcpy | |
-| taint.cpp:170:10:170:15 | ref arg buffer | taint.cpp:170:3:170:8 | call to strcpy | |
| taint.cpp:170:10:170:15 | ref arg buffer | taint.cpp:171:8:171:13 | buffer | |
| taint.cpp:170:10:170:15 | ref arg buffer | taint.cpp:172:10:172:15 | buffer | |
| taint.cpp:170:10:170:15 | ref arg buffer | taint.cpp:173:8:173:13 | buffer | |
@@ -299,7 +308,6 @@
| taint.cpp:171:8:171:13 | ref arg buffer | taint.cpp:173:8:173:13 | buffer | |
| taint.cpp:172:10:172:15 | buffer | taint.cpp:172:3:172:8 | call to strcat | |
| taint.cpp:172:10:172:15 | buffer | taint.cpp:172:10:172:15 | ref arg buffer | TAINT |
-| taint.cpp:172:10:172:15 | ref arg buffer | taint.cpp:172:3:172:8 | call to strcat | |
| taint.cpp:172:10:172:15 | ref arg buffer | taint.cpp:173:8:173:13 | buffer | |
| taint.cpp:172:18:172:24 | tainted | taint.cpp:172:10:172:15 | ref arg buffer | TAINT |
| taint.cpp:180:19:180:19 | p | taint.cpp:181:9:181:9 | p | |
@@ -310,7 +318,6 @@
| taint.cpp:193:6:193:6 | x | taint.cpp:194:10:194:10 | x | |
| taint.cpp:193:6:193:6 | x | taint.cpp:195:7:195:7 | x | |
| taint.cpp:194:9:194:10 | & ... | taint.cpp:194:2:194:7 | call to memcpy | |
-| taint.cpp:194:9:194:10 | ref arg & ... | taint.cpp:194:2:194:7 | call to memcpy | |
| taint.cpp:194:9:194:10 | ref arg & ... | taint.cpp:195:7:195:7 | x | |
| taint.cpp:194:10:194:10 | x | taint.cpp:194:9:194:10 | & ... | |
| taint.cpp:194:13:194:18 | source | taint.cpp:194:2:194:7 | call to memcpy | TAINT |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/stl.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/stl.cpp
index 54755871f6a..d92bb39d158 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/stl.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/stl.cpp
@@ -110,3 +110,24 @@ void test_stringstream_int(int source)
sink(ss1.str());
sink(ss2.str()); // tainted [NOT DETECTED]
}
+
+using namespace std;
+
+char *user_input() {
+ return source();
+}
+
+void sink(const char *filename, const char *mode);
+
+void test_strings2()
+{
+ string path1 = user_input();
+ sink(path1.c_str(), "r"); // tainted
+
+ string path2;
+ path2 = user_input();
+ sink(path2.c_str(), "r"); // tainted
+
+ string path3(user_input());
+ sink(path3.c_str(), "r"); // tainted
+}
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
index f8365ae63c7..59193d81722 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
@@ -11,6 +11,9 @@
| stl.cpp:71:7:71:7 | a | stl.cpp:67:12:67:17 | call to source |
| stl.cpp:73:7:73:7 | c | stl.cpp:69:16:69:21 | call to source |
| stl.cpp:75:9:75:13 | call to c_str | stl.cpp:69:16:69:21 | call to source |
+| stl.cpp:125:13:125:17 | call to c_str | stl.cpp:117:10:117:15 | call to source |
+| stl.cpp:129:13:129:17 | call to c_str | stl.cpp:117:10:117:15 | call to source |
+| stl.cpp:132:13:132:17 | call to c_str | stl.cpp:117:10:117:15 | call to source |
| taint.cpp:8:8:8:13 | clean1 | taint.cpp:4:27:4:33 | source1 |
| taint.cpp:16:8:16:14 | source1 | taint.cpp:12:22:12:27 | call to source |
| taint.cpp:17:8:17:16 | ++ ... | taint.cpp:12:22:12:27 | call to source |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
index e3203a8d6c4..0c8d17b9cb0 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
@@ -10,6 +10,9 @@
| format.cpp:106:8:106:14 | format.cpp:105:38:105:52 | AST only |
| stl.cpp:73:7:73:7 | stl.cpp:69:16:69:21 | AST only |
| stl.cpp:75:9:75:13 | stl.cpp:69:16:69:21 | AST only |
+| stl.cpp:125:13:125:17 | stl.cpp:117:10:117:15 | AST only |
+| stl.cpp:129:13:129:17 | stl.cpp:117:10:117:15 | AST only |
+| stl.cpp:132:13:132:17 | stl.cpp:117:10:117:15 | AST only |
| taint.cpp:41:7:41:13 | taint.cpp:35:12:35:17 | AST only |
| taint.cpp:42:7:42:13 | taint.cpp:35:12:35:17 | AST only |
| taint.cpp:43:7:43:13 | taint.cpp:37:22:37:27 | AST only |
@@ -19,12 +22,14 @@
| taint.cpp:93:11:93:11 | taint.cpp:71:22:71:27 | AST only |
| taint.cpp:94:11:94:11 | taint.cpp:72:7:72:12 | AST only |
| taint.cpp:109:7:109:13 | taint.cpp:105:12:105:17 | IR only |
+| taint.cpp:110:7:110:13 | taint.cpp:105:12:105:17 | IR only |
+| taint.cpp:111:7:111:13 | taint.cpp:106:12:106:17 | IR only |
+| taint.cpp:112:7:112:13 | taint.cpp:106:12:106:17 | IR only |
| taint.cpp:130:7:130:9 | taint.cpp:127:8:127:13 | IR only |
| taint.cpp:137:7:137:9 | taint.cpp:120:11:120:16 | AST only |
| taint.cpp:173:8:173:13 | taint.cpp:164:19:164:24 | AST only |
| taint.cpp:195:7:195:7 | taint.cpp:192:23:192:28 | AST only |
| taint.cpp:195:7:195:7 | taint.cpp:193:6:193:6 | AST only |
-| taint.cpp:216:7:216:7 | taint.cpp:207:6:207:11 | AST only |
| taint.cpp:229:3:229:6 | taint.cpp:223:10:223:15 | AST only |
| taint.cpp:233:8:233:8 | taint.cpp:223:10:223:15 | AST only |
| taint.cpp:236:3:236:6 | taint.cpp:223:10:223:15 | AST only |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected
index 4e1563cd5a6..78a15c3d9a8 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected
@@ -4,6 +4,9 @@
| taint.cpp:16:8:16:14 | source1 | taint.cpp:12:22:12:27 | call to source |
| taint.cpp:17:8:17:16 | ++ ... | taint.cpp:12:22:12:27 | call to source |
| taint.cpp:109:7:109:13 | access to array | taint.cpp:105:12:105:17 | call to source |
+| taint.cpp:110:7:110:13 | access to array | taint.cpp:105:12:105:17 | call to source |
+| taint.cpp:111:7:111:13 | access to array | taint.cpp:106:12:106:17 | call to source |
+| taint.cpp:112:7:112:13 | access to array | taint.cpp:106:12:106:17 | call to source |
| taint.cpp:129:7:129:9 | * ... | taint.cpp:120:11:120:16 | call to source |
| taint.cpp:130:7:130:9 | * ... | taint.cpp:127:8:127:13 | call to source |
| taint.cpp:134:7:134:9 | * ... | taint.cpp:120:11:120:16 | call to source |
@@ -13,6 +16,7 @@
| taint.cpp:181:8:181:9 | * ... | taint.cpp:185:11:185:16 | call to source |
| taint.cpp:210:7:210:7 | x | taint.cpp:207:6:207:11 | call to source |
| taint.cpp:215:7:215:7 | x | taint.cpp:207:6:207:11 | call to source |
+| taint.cpp:216:7:216:7 | y | taint.cpp:207:6:207:11 | call to source |
| taint.cpp:250:8:250:8 | a | taint.cpp:223:10:223:15 | call to source |
| taint.cpp:256:8:256:8 | (reference dereference) | taint.cpp:223:10:223:15 | call to source |
| taint.cpp:280:7:280:7 | t | taint.cpp:275:6:275:11 | call to source |
diff --git a/cpp/ql/test/library-tests/defuse/isAddressOfAccess.expected b/cpp/ql/test/library-tests/defuse/isAddressOfAccess.expected
index ff7ee26843d..8a57ff7206e 100644
--- a/cpp/ql/test/library-tests/defuse/isAddressOfAccess.expected
+++ b/cpp/ql/test/library-tests/defuse/isAddressOfAccess.expected
@@ -11,9 +11,9 @@
| addressOf.cpp:38:20:38:20 | i | non-const address |
| addressOf.cpp:40:15:40:15 | i | non-const address |
| addressOf.cpp:42:19:42:22 | iref | non-const address |
-| addressOf.cpp:48:3:48:4 | f1 | |
+| addressOf.cpp:48:3:48:4 | f1 | const address |
| addressOf.cpp:49:15:49:22 | captured | non-const address |
-| addressOf.cpp:50:3:50:4 | f2 | |
+| addressOf.cpp:50:3:50:4 | f2 | const address |
| addressOf.cpp:51:10:51:17 | captured | |
| addressOf.cpp:56:16:56:16 | i | |
| addressOf.cpp:56:19:56:19 | i | |
diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected
index f6d9fdc8a35..a840ec593d7 100644
--- a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected
+++ b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected
@@ -234,6 +234,1487 @@ clang.cpp:
# 6| 0: [VariableAccess] globalInt
# 6| Type = [IntType] int
# 6| ValueCategory = lvalue
+complex.c:
+# 1| [TopLevelFunction] void complex_literals()
+# 1| params:
+# 1| body: [Block] { ... }
+# 2| 0: [DeclStmt] declaration
+# 2| 0: [VariableDeclarationEntry] definition of cf
+# 2| Type = [ArithmeticType] _Complex float
+# 2| init: [Initializer] initializer for cf
+# 2| expr: [CStyleCast] (_Complex float)...
+# 2| Conversion = [FloatingPointConversion] floating point conversion
+# 2| Type = [ArithmeticType] _Complex float
+# 2| ValueCategory = prvalue
+# 2| expr: [Literal] 2.0
+# 2| Type = [DoubleType] double
+# 2| Value = [Literal] 2.0
+# 2| ValueCategory = prvalue
+# 3| 1: [ExprStmt] ExprStmt
+# 3| 0: [AssignExpr] ... = ...
+# 3| Type = [ArithmeticType] _Complex float
+# 3| ValueCategory = prvalue
+# 3| 0: [VariableAccess] cf
+# 3| Type = [ArithmeticType] _Complex float
+# 3| ValueCategory = lvalue
+# 3| 1: [CStyleCast] (_Complex float)...
+# 3| Conversion = [FloatingPointConversion] floating point conversion
+# 3| Type = [ArithmeticType] _Complex float
+# 3| ValueCategory = prvalue
+# 3| expr: [Literal] 1.0i
+# 3| Type = [ArithmeticType] _Imaginary float
+# 3| Value = [Literal] 1.0i
+# 3| ValueCategory = prvalue
+# 4| 2: [DeclStmt] declaration
+# 4| 0: [VariableDeclarationEntry] definition of cd
+# 4| Type = [ArithmeticType] _Complex double
+# 4| init: [Initializer] initializer for cd
+# 4| expr: [CStyleCast] (_Complex double)...
+# 4| Conversion = [FloatingPointConversion] floating point conversion
+# 4| Type = [ArithmeticType] _Complex double
+# 4| ValueCategory = prvalue
+# 4| expr: [Literal] 3.0
+# 4| Type = [DoubleType] double
+# 4| Value = [Literal] 3.0
+# 4| ValueCategory = prvalue
+# 5| 3: [ExprStmt] ExprStmt
+# 5| 0: [AssignExpr] ... = ...
+# 5| Type = [ArithmeticType] _Complex double
+# 5| ValueCategory = prvalue
+# 5| 0: [VariableAccess] cd
+# 5| Type = [ArithmeticType] _Complex double
+# 5| ValueCategory = lvalue
+# 5| 1: [CStyleCast] (_Complex double)...
+# 5| Conversion = [FloatingPointConversion] floating point conversion
+# 5| Type = [ArithmeticType] _Complex double
+# 5| ValueCategory = prvalue
+# 5| expr: [Literal] 1.0i
+# 5| Type = [ArithmeticType] _Imaginary float
+# 5| Value = [Literal] 1.0i
+# 5| ValueCategory = prvalue
+# 6| 4: [DeclStmt] declaration
+# 6| 0: [VariableDeclarationEntry] definition of cld
+# 6| Type = [ArithmeticType] _Complex long double
+# 6| init: [Initializer] initializer for cld
+# 6| expr: [CStyleCast] (_Complex long double)...
+# 6| Conversion = [FloatingPointConversion] floating point conversion
+# 6| Type = [ArithmeticType] _Complex long double
+# 6| ValueCategory = prvalue
+# 6| expr: [Literal] 5.0
+# 6| Type = [DoubleType] double
+# 6| Value = [Literal] 5.0
+# 6| ValueCategory = prvalue
+# 7| 5: [ExprStmt] ExprStmt
+# 7| 0: [AssignExpr] ... = ...
+# 7| Type = [ArithmeticType] _Complex long double
+# 7| ValueCategory = prvalue
+# 7| 0: [VariableAccess] cld
+# 7| Type = [ArithmeticType] _Complex long double
+# 7| ValueCategory = lvalue
+# 7| 1: [CStyleCast] (_Complex long double)...
+# 7| Conversion = [FloatingPointConversion] floating point conversion
+# 7| Type = [ArithmeticType] _Complex long double
+# 7| ValueCategory = prvalue
+# 7| expr: [Literal] 1.0i
+# 7| Type = [ArithmeticType] _Imaginary float
+# 7| Value = [Literal] 1.0i
+# 7| ValueCategory = prvalue
+# 9| 6: [DeclStmt] declaration
+# 9| 0: [VariableDeclarationEntry] definition of jf
+# 9| Type = [ArithmeticType] _Imaginary float
+# 9| init: [Initializer] initializer for jf
+# 9| expr: [Literal] 1.0i
+# 9| Type = [ArithmeticType] _Imaginary float
+# 9| Value = [Literal] 1.0i
+# 9| ValueCategory = prvalue
+# 10| 7: [DeclStmt] declaration
+# 10| 0: [VariableDeclarationEntry] definition of jd
+# 10| Type = [ArithmeticType] _Imaginary double
+# 10| init: [Initializer] initializer for jd
+# 10| expr: [CStyleCast] (_Imaginary double)...
+# 10| Conversion = [FloatingPointConversion] floating point conversion
+# 10| Type = [ArithmeticType] _Imaginary double
+# 10| ValueCategory = prvalue
+# 10| expr: [Literal] 1.0i
+# 10| Type = [ArithmeticType] _Imaginary float
+# 10| Value = [Literal] 1.0i
+# 10| ValueCategory = prvalue
+# 11| 8: [DeclStmt] declaration
+# 11| 0: [VariableDeclarationEntry] definition of jld
+# 11| Type = [ArithmeticType] _Imaginary long double
+# 11| init: [Initializer] initializer for jld
+# 11| expr: [CStyleCast] (_Imaginary long double)...
+# 11| Conversion = [FloatingPointConversion] floating point conversion
+# 11| Type = [ArithmeticType] _Imaginary long double
+# 11| ValueCategory = prvalue
+# 11| expr: [Literal] 1.0i
+# 11| Type = [ArithmeticType] _Imaginary float
+# 11| Value = [Literal] 1.0i
+# 11| ValueCategory = prvalue
+# 12| 9: [ReturnStmt] return ...
+# 14| [TopLevelFunction] void complex_arithmetic()
+# 14| params:
+# 14| body: [Block] { ... }
+# 15| 0: [DeclStmt] declaration
+# 15| 0: [VariableDeclarationEntry] definition of f1
+# 15| Type = [FloatType] float
+# 15| init: [Initializer] initializer for f1
+# 15| expr: [CStyleCast] (float)...
+# 15| Conversion = [FloatingPointConversion] floating point conversion
+# 15| Type = [FloatType] float
+# 15| Value = [CStyleCast] 5.0
+# 15| ValueCategory = prvalue
+# 15| expr: [Literal] 5.0
+# 15| Type = [DoubleType] double
+# 15| Value = [Literal] 5.0
+# 15| ValueCategory = prvalue
+# 16| 1: [DeclStmt] declaration
+# 16| 0: [VariableDeclarationEntry] definition of f2
+# 16| Type = [FloatType] float
+# 16| init: [Initializer] initializer for f2
+# 16| expr: [CStyleCast] (float)...
+# 16| Conversion = [FloatingPointConversion] floating point conversion
+# 16| Type = [FloatType] float
+# 16| Value = [CStyleCast] 7.0
+# 16| ValueCategory = prvalue
+# 16| expr: [Literal] 7.0
+# 16| Type = [DoubleType] double
+# 16| Value = [Literal] 7.0
+# 16| ValueCategory = prvalue
+# 17| 2: [DeclStmt] declaration
+# 17| 0: [VariableDeclarationEntry] definition of f3
+# 17| Type = [FloatType] float
+# 18| 3: [DeclStmt] declaration
+# 18| 0: [VariableDeclarationEntry] definition of cf1
+# 18| Type = [ArithmeticType] _Complex float
+# 18| init: [Initializer] initializer for cf1
+# 18| expr: [CStyleCast] (_Complex float)...
+# 18| Conversion = [FloatingPointConversion] floating point conversion
+# 18| Type = [ArithmeticType] _Complex float
+# 18| ValueCategory = prvalue
+# 18| expr: [Literal] 2.0
+# 18| Type = [DoubleType] double
+# 18| Value = [Literal] 2.0
+# 18| ValueCategory = prvalue
+# 19| 4: [DeclStmt] declaration
+# 19| 0: [VariableDeclarationEntry] definition of cf2
+# 19| Type = [ArithmeticType] _Complex float
+# 19| init: [Initializer] initializer for cf2
+# 19| expr: [CStyleCast] (_Complex float)...
+# 19| Conversion = [FloatingPointConversion] floating point conversion
+# 19| Type = [ArithmeticType] _Complex float
+# 19| ValueCategory = prvalue
+# 19| expr: [Literal] 1.0i
+# 19| Type = [ArithmeticType] _Imaginary float
+# 19| Value = [Literal] 1.0i
+# 19| ValueCategory = prvalue
+# 20| 5: [DeclStmt] declaration
+# 20| 0: [VariableDeclarationEntry] definition of cf3
+# 20| Type = [ArithmeticType] _Complex float
+# 21| 6: [DeclStmt] declaration
+# 21| 0: [VariableDeclarationEntry] definition of jf1
+# 21| Type = [ArithmeticType] _Imaginary float
+# 21| init: [Initializer] initializer for jf1
+# 21| expr: [Literal] 1.0i
+# 21| Type = [ArithmeticType] _Imaginary float
+# 21| Value = [Literal] 1.0i
+# 21| ValueCategory = prvalue
+# 22| 7: [DeclStmt] declaration
+# 22| 0: [VariableDeclarationEntry] definition of jf2
+# 22| Type = [ArithmeticType] _Imaginary float
+# 22| init: [Initializer] initializer for jf2
+# 22| expr: [Literal] 1.0i
+# 22| Type = [ArithmeticType] _Imaginary float
+# 22| Value = [Literal] 1.0i
+# 22| ValueCategory = prvalue
+# 23| 8: [DeclStmt] declaration
+# 23| 0: [VariableDeclarationEntry] definition of jf3
+# 23| Type = [ArithmeticType] _Imaginary float
+# 26| 9: [ExprStmt] ExprStmt
+# 26| 0: [AssignExpr] ... = ...
+# 26| Type = [ArithmeticType] _Complex float
+# 26| ValueCategory = prvalue
+# 26| 0: [VariableAccess] cf3
+# 26| Type = [ArithmeticType] _Complex float
+# 26| ValueCategory = lvalue
+# 26| 1: [UnaryPlusExpr] + ...
+# 26| Type = [ArithmeticType] _Complex float
+# 26| ValueCategory = prvalue
+# 26| 0: [VariableAccess] cf1
+# 26| Type = [ArithmeticType] _Complex float
+# 26| ValueCategory = prvalue(load)
+# 27| 10: [ExprStmt] ExprStmt
+# 27| 0: [AssignExpr] ... = ...
+# 27| Type = [ArithmeticType] _Complex float
+# 27| ValueCategory = prvalue
+# 27| 0: [VariableAccess] cf3
+# 27| Type = [ArithmeticType] _Complex float
+# 27| ValueCategory = lvalue
+# 27| 1: [UnaryMinusExpr] - ...
+# 27| Type = [ArithmeticType] _Complex float
+# 27| ValueCategory = prvalue
+# 27| 0: [VariableAccess] cf1
+# 27| Type = [ArithmeticType] _Complex float
+# 27| ValueCategory = prvalue(load)
+# 30| 11: [ExprStmt] ExprStmt
+# 30| 0: [AssignExpr] ... = ...
+# 30| Type = [ArithmeticType] _Complex float
+# 30| ValueCategory = prvalue
+# 30| 0: [VariableAccess] cf3
+# 30| Type = [ArithmeticType] _Complex float
+# 30| ValueCategory = lvalue
+# 30| 1: [AddExpr] ... + ...
+# 30| Type = [ArithmeticType] _Complex float
+# 30| ValueCategory = prvalue
+# 30| 0: [VariableAccess] cf1
+# 30| Type = [ArithmeticType] _Complex float
+# 30| ValueCategory = prvalue(load)
+# 30| 1: [VariableAccess] cf2
+# 30| Type = [ArithmeticType] _Complex float
+# 30| ValueCategory = prvalue(load)
+# 31| 12: [ExprStmt] ExprStmt
+# 31| 0: [AssignExpr] ... = ...
+# 31| Type = [ArithmeticType] _Complex float
+# 31| ValueCategory = prvalue
+# 31| 0: [VariableAccess] cf3
+# 31| Type = [ArithmeticType] _Complex float
+# 31| ValueCategory = lvalue
+# 31| 1: [SubExpr] ... - ...
+# 31| Type = [ArithmeticType] _Complex float
+# 31| ValueCategory = prvalue
+# 31| 0: [VariableAccess] cf1
+# 31| Type = [ArithmeticType] _Complex float
+# 31| ValueCategory = prvalue(load)
+# 31| 1: [VariableAccess] cf2
+# 31| Type = [ArithmeticType] _Complex float
+# 31| ValueCategory = prvalue(load)
+# 32| 13: [ExprStmt] ExprStmt
+# 32| 0: [AssignExpr] ... = ...
+# 32| Type = [ArithmeticType] _Complex float
+# 32| ValueCategory = prvalue
+# 32| 0: [VariableAccess] cf3
+# 32| Type = [ArithmeticType] _Complex float
+# 32| ValueCategory = lvalue
+# 32| 1: [MulExpr] ... * ...
+# 32| Type = [ArithmeticType] _Complex float
+# 32| ValueCategory = prvalue
+# 32| 0: [VariableAccess] cf1
+# 32| Type = [ArithmeticType] _Complex float
+# 32| ValueCategory = prvalue(load)
+# 32| 1: [VariableAccess] cf2
+# 32| Type = [ArithmeticType] _Complex float
+# 32| ValueCategory = prvalue(load)
+# 33| 14: [ExprStmt] ExprStmt
+# 33| 0: [AssignExpr] ... = ...
+# 33| Type = [ArithmeticType] _Complex float
+# 33| ValueCategory = prvalue
+# 33| 0: [VariableAccess] cf3
+# 33| Type = [ArithmeticType] _Complex float
+# 33| ValueCategory = lvalue
+# 33| 1: [DivExpr] ... / ...
+# 33| Type = [ArithmeticType] _Complex float
+# 33| ValueCategory = prvalue
+# 33| 0: [VariableAccess] cf1
+# 33| Type = [ArithmeticType] _Complex float
+# 33| ValueCategory = prvalue(load)
+# 33| 1: [VariableAccess] cf2
+# 33| Type = [ArithmeticType] _Complex float
+# 33| ValueCategory = prvalue(load)
+# 36| 15: [ExprStmt] ExprStmt
+# 36| 0: [AssignExpr] ... = ...
+# 36| Type = [ArithmeticType] _Imaginary float
+# 36| ValueCategory = prvalue
+# 36| 0: [VariableAccess] jf3
+# 36| Type = [ArithmeticType] _Imaginary float
+# 36| ValueCategory = lvalue
+# 36| 1: [UnaryPlusExpr] + ...
+# 36| Type = [ArithmeticType] _Imaginary float
+# 36| ValueCategory = prvalue
+# 36| 0: [VariableAccess] jf1
+# 36| Type = [ArithmeticType] _Imaginary float
+# 36| ValueCategory = prvalue(load)
+# 37| 16: [ExprStmt] ExprStmt
+# 37| 0: [AssignExpr] ... = ...
+# 37| Type = [ArithmeticType] _Imaginary float
+# 37| ValueCategory = prvalue
+# 37| 0: [VariableAccess] jf3
+# 37| Type = [ArithmeticType] _Imaginary float
+# 37| ValueCategory = lvalue
+# 37| 1: [UnaryMinusExpr] - ...
+# 37| Type = [ArithmeticType] _Imaginary float
+# 37| ValueCategory = prvalue
+# 37| 0: [VariableAccess] jf1
+# 37| Type = [ArithmeticType] _Imaginary float
+# 37| ValueCategory = prvalue(load)
+# 40| 17: [ExprStmt] ExprStmt
+# 40| 0: [AssignExpr] ... = ...
+# 40| Type = [ArithmeticType] _Imaginary float
+# 40| ValueCategory = prvalue
+# 40| 0: [VariableAccess] jf3
+# 40| Type = [ArithmeticType] _Imaginary float
+# 40| ValueCategory = lvalue
+# 40| 1: [AddExpr] ... + ...
+# 40| Type = [ArithmeticType] _Imaginary float
+# 40| ValueCategory = prvalue
+# 40| 0: [VariableAccess] jf1
+# 40| Type = [ArithmeticType] _Imaginary float
+# 40| ValueCategory = prvalue(load)
+# 40| 1: [VariableAccess] jf2
+# 40| Type = [ArithmeticType] _Imaginary float
+# 40| ValueCategory = prvalue(load)
+# 41| 18: [ExprStmt] ExprStmt
+# 41| 0: [AssignExpr] ... = ...
+# 41| Type = [ArithmeticType] _Imaginary float
+# 41| ValueCategory = prvalue
+# 41| 0: [VariableAccess] jf3
+# 41| Type = [ArithmeticType] _Imaginary float
+# 41| ValueCategory = lvalue
+# 41| 1: [SubExpr] ... - ...
+# 41| Type = [ArithmeticType] _Imaginary float
+# 41| ValueCategory = prvalue
+# 41| 0: [VariableAccess] jf1
+# 41| Type = [ArithmeticType] _Imaginary float
+# 41| ValueCategory = prvalue(load)
+# 41| 1: [VariableAccess] jf2
+# 41| Type = [ArithmeticType] _Imaginary float
+# 41| ValueCategory = prvalue(load)
+# 42| 19: [ExprStmt] ExprStmt
+# 42| 0: [AssignExpr] ... = ...
+# 42| Type = [FloatType] float
+# 42| ValueCategory = prvalue
+# 42| 0: [VariableAccess] f3
+# 42| Type = [FloatType] float
+# 42| ValueCategory = lvalue
+# 42| 1: [ImaginaryMulExpr] ... * ...
+# 42| Type = [FloatType] float
+# 42| ValueCategory = prvalue
+# 42| 0: [VariableAccess] jf1
+# 42| Type = [ArithmeticType] _Imaginary float
+# 42| ValueCategory = prvalue(load)
+# 42| 1: [VariableAccess] jf2
+# 42| Type = [ArithmeticType] _Imaginary float
+# 42| ValueCategory = prvalue(load)
+# 43| 20: [ExprStmt] ExprStmt
+# 43| 0: [AssignExpr] ... = ...
+# 43| Type = [FloatType] float
+# 43| ValueCategory = prvalue
+# 43| 0: [VariableAccess] f3
+# 43| Type = [FloatType] float
+# 43| ValueCategory = lvalue
+# 43| 1: [DivExpr] ... / ...
+# 43| Type = [FloatType] float
+# 43| ValueCategory = prvalue
+# 43| 0: [VariableAccess] jf1
+# 43| Type = [ArithmeticType] _Imaginary float
+# 43| ValueCategory = prvalue(load)
+# 43| 1: [VariableAccess] jf2
+# 43| Type = [ArithmeticType] _Imaginary float
+# 43| ValueCategory = prvalue(load)
+# 46| 21: [ExprStmt] ExprStmt
+# 46| 0: [AssignExpr] ... = ...
+# 46| Type = [ArithmeticType] _Complex float
+# 46| ValueCategory = prvalue
+# 46| 0: [VariableAccess] cf3
+# 46| Type = [ArithmeticType] _Complex float
+# 46| ValueCategory = lvalue
+# 46| 1: [ImaginaryRealAddExpr] ... + ...
+# 46| Type = [ArithmeticType] _Complex float
+# 46| ValueCategory = prvalue
+# 46| 0: [VariableAccess] jf1
+# 46| Type = [ArithmeticType] _Imaginary float
+# 46| ValueCategory = prvalue(load)
+# 46| 1: [VariableAccess] f2
+# 46| Type = [FloatType] float
+# 46| ValueCategory = prvalue(load)
+# 47| 22: [ExprStmt] ExprStmt
+# 47| 0: [AssignExpr] ... = ...
+# 47| Type = [ArithmeticType] _Complex float
+# 47| ValueCategory = prvalue
+# 47| 0: [VariableAccess] cf3
+# 47| Type = [ArithmeticType] _Complex float
+# 47| ValueCategory = lvalue
+# 47| 1: [ImaginaryRealSubExpr] ... - ...
+# 47| Type = [ArithmeticType] _Complex float
+# 47| ValueCategory = prvalue
+# 47| 0: [VariableAccess] jf1
+# 47| Type = [ArithmeticType] _Imaginary float
+# 47| ValueCategory = prvalue(load)
+# 47| 1: [VariableAccess] f2
+# 47| Type = [FloatType] float
+# 47| ValueCategory = prvalue(load)
+# 48| 23: [ExprStmt] ExprStmt
+# 48| 0: [AssignExpr] ... = ...
+# 48| Type = [ArithmeticType] _Imaginary float
+# 48| ValueCategory = prvalue
+# 48| 0: [VariableAccess] jf3
+# 48| Type = [ArithmeticType] _Imaginary float
+# 48| ValueCategory = lvalue
+# 48| 1: [MulExpr] ... * ...
+# 48| Type = [ArithmeticType] _Imaginary float
+# 48| ValueCategory = prvalue
+# 48| 0: [VariableAccess] jf1
+# 48| Type = [ArithmeticType] _Imaginary float
+# 48| ValueCategory = prvalue(load)
+# 48| 1: [VariableAccess] f2
+# 48| Type = [FloatType] float
+# 48| ValueCategory = prvalue(load)
+# 49| 24: [ExprStmt] ExprStmt
+# 49| 0: [AssignExpr] ... = ...
+# 49| Type = [ArithmeticType] _Imaginary float
+# 49| ValueCategory = prvalue
+# 49| 0: [VariableAccess] jf3
+# 49| Type = [ArithmeticType] _Imaginary float
+# 49| ValueCategory = lvalue
+# 49| 1: [DivExpr] ... / ...
+# 49| Type = [ArithmeticType] _Imaginary float
+# 49| ValueCategory = prvalue
+# 49| 0: [VariableAccess] jf1
+# 49| Type = [ArithmeticType] _Imaginary float
+# 49| ValueCategory = prvalue(load)
+# 49| 1: [VariableAccess] f2
+# 49| Type = [FloatType] float
+# 49| ValueCategory = prvalue(load)
+# 52| 25: [ExprStmt] ExprStmt
+# 52| 0: [AssignExpr] ... = ...
+# 52| Type = [ArithmeticType] _Complex float
+# 52| ValueCategory = prvalue
+# 52| 0: [VariableAccess] cf3
+# 52| Type = [ArithmeticType] _Complex float
+# 52| ValueCategory = lvalue
+# 52| 1: [RealImaginaryAddExpr] ... + ...
+# 52| Type = [ArithmeticType] _Complex float
+# 52| ValueCategory = prvalue
+# 52| 0: [VariableAccess] f1
+# 52| Type = [FloatType] float
+# 52| ValueCategory = prvalue(load)
+# 52| 1: [VariableAccess] jf2
+# 52| Type = [ArithmeticType] _Imaginary float
+# 52| ValueCategory = prvalue(load)
+# 53| 26: [ExprStmt] ExprStmt
+# 53| 0: [AssignExpr] ... = ...
+# 53| Type = [ArithmeticType] _Complex float
+# 53| ValueCategory = prvalue
+# 53| 0: [VariableAccess] cf3
+# 53| Type = [ArithmeticType] _Complex float
+# 53| ValueCategory = lvalue
+# 53| 1: [RealImaginarySubExpr] ... - ...
+# 53| Type = [ArithmeticType] _Complex float
+# 53| ValueCategory = prvalue
+# 53| 0: [VariableAccess] f1
+# 53| Type = [FloatType] float
+# 53| ValueCategory = prvalue(load)
+# 53| 1: [VariableAccess] jf2
+# 53| Type = [ArithmeticType] _Imaginary float
+# 53| ValueCategory = prvalue(load)
+# 54| 27: [ExprStmt] ExprStmt
+# 54| 0: [AssignExpr] ... = ...
+# 54| Type = [ArithmeticType] _Imaginary float
+# 54| ValueCategory = prvalue
+# 54| 0: [VariableAccess] jf3
+# 54| Type = [ArithmeticType] _Imaginary float
+# 54| ValueCategory = lvalue
+# 54| 1: [MulExpr] ... * ...
+# 54| Type = [ArithmeticType] _Imaginary float
+# 54| ValueCategory = prvalue
+# 54| 0: [VariableAccess] f1
+# 54| Type = [FloatType] float
+# 54| ValueCategory = prvalue(load)
+# 54| 1: [VariableAccess] jf2
+# 54| Type = [ArithmeticType] _Imaginary float
+# 54| ValueCategory = prvalue(load)
+# 55| 28: [ExprStmt] ExprStmt
+# 55| 0: [AssignExpr] ... = ...
+# 55| Type = [ArithmeticType] _Imaginary float
+# 55| ValueCategory = prvalue
+# 55| 0: [VariableAccess] jf3
+# 55| Type = [ArithmeticType] _Imaginary float
+# 55| ValueCategory = lvalue
+# 55| 1: [ImaginaryDivExpr] ... / ...
+# 55| Type = [ArithmeticType] _Imaginary float
+# 55| ValueCategory = prvalue
+# 55| 0: [VariableAccess] f1
+# 55| Type = [FloatType] float
+# 55| ValueCategory = prvalue(load)
+# 55| 1: [VariableAccess] jf2
+# 55| Type = [ArithmeticType] _Imaginary float
+# 55| ValueCategory = prvalue(load)
+# 56| 29: [ReturnStmt] return ...
+# 58| [TopLevelFunction] void complex_conversions()
+# 58| params:
+# 58| body: [Block] { ... }
+# 59| 0: [DeclStmt] declaration
+# 59| 0: [VariableDeclarationEntry] definition of f
+# 59| Type = [FloatType] float
+# 59| init: [Initializer] initializer for f
+# 59| expr: [CStyleCast] (float)...
+# 59| Conversion = [FloatingPointConversion] floating point conversion
+# 59| Type = [FloatType] float
+# 59| Value = [CStyleCast] 2.0
+# 59| ValueCategory = prvalue
+# 59| expr: [Literal] 2.0
+# 59| Type = [DoubleType] double
+# 59| Value = [Literal] 2.0
+# 59| ValueCategory = prvalue
+# 60| 1: [DeclStmt] declaration
+# 60| 0: [VariableDeclarationEntry] definition of d
+# 60| Type = [DoubleType] double
+# 60| init: [Initializer] initializer for d
+# 60| expr: [Literal] 3.0
+# 60| Type = [DoubleType] double
+# 60| Value = [Literal] 3.0
+# 60| ValueCategory = prvalue
+# 61| 2: [DeclStmt] declaration
+# 61| 0: [VariableDeclarationEntry] definition of ld
+# 61| Type = [LongDoubleType] long double
+# 61| init: [Initializer] initializer for ld
+# 61| expr: [CStyleCast] (long double)...
+# 61| Conversion = [FloatingPointConversion] floating point conversion
+# 61| Type = [LongDoubleType] long double
+# 61| Value = [CStyleCast] 5.0
+# 61| ValueCategory = prvalue
+# 61| expr: [Literal] 5.0
+# 61| Type = [DoubleType] double
+# 61| Value = [Literal] 5.0
+# 61| ValueCategory = prvalue
+# 62| 3: [DeclStmt] declaration
+# 62| 0: [VariableDeclarationEntry] definition of cf
+# 62| Type = [ArithmeticType] _Complex float
+# 62| init: [Initializer] initializer for cf
+# 62| expr: [CStyleCast] (_Complex float)...
+# 62| Conversion = [FloatingPointConversion] floating point conversion
+# 62| Type = [ArithmeticType] _Complex float
+# 62| ValueCategory = prvalue
+# 62| expr: [Literal] 7.0
+# 62| Type = [DoubleType] double
+# 62| Value = [Literal] 7.0
+# 62| ValueCategory = prvalue
+# 63| 4: [DeclStmt] declaration
+# 63| 0: [VariableDeclarationEntry] definition of cd
+# 63| Type = [ArithmeticType] _Complex double
+# 63| init: [Initializer] initializer for cd
+# 63| expr: [CStyleCast] (_Complex double)...
+# 63| Conversion = [FloatingPointConversion] floating point conversion
+# 63| Type = [ArithmeticType] _Complex double
+# 63| ValueCategory = prvalue
+# 63| expr: [Literal] 11.0
+# 63| Type = [DoubleType] double
+# 63| Value = [Literal] 11.0
+# 63| ValueCategory = prvalue
+# 64| 5: [DeclStmt] declaration
+# 64| 0: [VariableDeclarationEntry] definition of cld
+# 64| Type = [ArithmeticType] _Complex long double
+# 64| init: [Initializer] initializer for cld
+# 64| expr: [CStyleCast] (_Complex long double)...
+# 64| Conversion = [FloatingPointConversion] floating point conversion
+# 64| Type = [ArithmeticType] _Complex long double
+# 64| ValueCategory = prvalue
+# 64| expr: [Literal] 13.0
+# 64| Type = [DoubleType] double
+# 64| Value = [Literal] 13.0
+# 64| ValueCategory = prvalue
+# 65| 6: [DeclStmt] declaration
+# 65| 0: [VariableDeclarationEntry] definition of jf
+# 65| Type = [ArithmeticType] _Imaginary float
+# 65| init: [Initializer] initializer for jf
+# 65| expr: [Literal] 1.0i
+# 65| Type = [ArithmeticType] _Imaginary float
+# 65| Value = [Literal] 1.0i
+# 65| ValueCategory = prvalue
+# 66| 7: [DeclStmt] declaration
+# 66| 0: [VariableDeclarationEntry] definition of jd
+# 66| Type = [ArithmeticType] _Imaginary double
+# 66| init: [Initializer] initializer for jd
+# 66| expr: [CStyleCast] (_Imaginary double)...
+# 66| Conversion = [FloatingPointConversion] floating point conversion
+# 66| Type = [ArithmeticType] _Imaginary double
+# 66| ValueCategory = prvalue
+# 66| expr: [Literal] 1.0i
+# 66| Type = [ArithmeticType] _Imaginary float
+# 66| Value = [Literal] 1.0i
+# 66| ValueCategory = prvalue
+# 67| 8: [DeclStmt] declaration
+# 67| 0: [VariableDeclarationEntry] definition of jld
+# 67| Type = [ArithmeticType] _Imaginary long double
+# 67| init: [Initializer] initializer for jld
+# 67| expr: [CStyleCast] (_Imaginary long double)...
+# 67| Conversion = [FloatingPointConversion] floating point conversion
+# 67| Type = [ArithmeticType] _Imaginary long double
+# 67| ValueCategory = prvalue
+# 67| expr: [Literal] 1.0i
+# 67| Type = [ArithmeticType] _Imaginary float
+# 67| Value = [Literal] 1.0i
+# 67| ValueCategory = prvalue
+# 70| 9: [ExprStmt] ExprStmt
+# 70| 0: [AssignExpr] ... = ...
+# 70| Type = [ArithmeticType] _Complex float
+# 70| ValueCategory = prvalue
+# 70| 0: [VariableAccess] cf
+# 70| Type = [ArithmeticType] _Complex float
+# 70| ValueCategory = lvalue
+# 70| 1: [VariableAccess] cf
+# 70| Type = [ArithmeticType] _Complex float
+# 70| ValueCategory = prvalue(load)
+# 71| 10: [ExprStmt] ExprStmt
+# 71| 0: [AssignExpr] ... = ...
+# 71| Type = [ArithmeticType] _Complex float
+# 71| ValueCategory = prvalue
+# 71| 0: [VariableAccess] cf
+# 71| Type = [ArithmeticType] _Complex float
+# 71| ValueCategory = lvalue
+# 71| 1: [CStyleCast] (_Complex float)...
+# 71| Conversion = [FloatingPointConversion] floating point conversion
+# 71| Type = [ArithmeticType] _Complex float
+# 71| ValueCategory = prvalue
+# 71| expr: [VariableAccess] cd
+# 71| Type = [ArithmeticType] _Complex double
+# 71| ValueCategory = prvalue(load)
+# 72| 11: [ExprStmt] ExprStmt
+# 72| 0: [AssignExpr] ... = ...
+# 72| Type = [ArithmeticType] _Complex float
+# 72| ValueCategory = prvalue
+# 72| 0: [VariableAccess] cf
+# 72| Type = [ArithmeticType] _Complex float
+# 72| ValueCategory = lvalue
+# 72| 1: [CStyleCast] (_Complex float)...
+# 72| Conversion = [FloatingPointConversion] floating point conversion
+# 72| Type = [ArithmeticType] _Complex float
+# 72| ValueCategory = prvalue
+# 72| expr: [VariableAccess] cld
+# 72| Type = [ArithmeticType] _Complex long double
+# 72| ValueCategory = prvalue(load)
+# 73| 12: [ExprStmt] ExprStmt
+# 73| 0: [AssignExpr] ... = ...
+# 73| Type = [ArithmeticType] _Complex double
+# 73| ValueCategory = prvalue
+# 73| 0: [VariableAccess] cd
+# 73| Type = [ArithmeticType] _Complex double
+# 73| ValueCategory = lvalue
+# 73| 1: [CStyleCast] (_Complex double)...
+# 73| Conversion = [FloatingPointConversion] floating point conversion
+# 73| Type = [ArithmeticType] _Complex double
+# 73| ValueCategory = prvalue
+# 73| expr: [VariableAccess] cf
+# 73| Type = [ArithmeticType] _Complex float
+# 73| ValueCategory = prvalue(load)
+# 74| 13: [ExprStmt] ExprStmt
+# 74| 0: [AssignExpr] ... = ...
+# 74| Type = [ArithmeticType] _Complex double
+# 74| ValueCategory = prvalue
+# 74| 0: [VariableAccess] cd
+# 74| Type = [ArithmeticType] _Complex double
+# 74| ValueCategory = lvalue
+# 74| 1: [VariableAccess] cd
+# 74| Type = [ArithmeticType] _Complex double
+# 74| ValueCategory = prvalue(load)
+# 75| 14: [ExprStmt] ExprStmt
+# 75| 0: [AssignExpr] ... = ...
+# 75| Type = [ArithmeticType] _Complex double
+# 75| ValueCategory = prvalue
+# 75| 0: [VariableAccess] cd
+# 75| Type = [ArithmeticType] _Complex double
+# 75| ValueCategory = lvalue
+# 75| 1: [CStyleCast] (_Complex double)...
+# 75| Conversion = [FloatingPointConversion] floating point conversion
+# 75| Type = [ArithmeticType] _Complex double
+# 75| ValueCategory = prvalue
+# 75| expr: [VariableAccess] cld
+# 75| Type = [ArithmeticType] _Complex long double
+# 75| ValueCategory = prvalue(load)
+# 76| 15: [ExprStmt] ExprStmt
+# 76| 0: [AssignExpr] ... = ...
+# 76| Type = [ArithmeticType] _Complex long double
+# 76| ValueCategory = prvalue
+# 76| 0: [VariableAccess] cld
+# 76| Type = [ArithmeticType] _Complex long double
+# 76| ValueCategory = lvalue
+# 76| 1: [CStyleCast] (_Complex long double)...
+# 76| Conversion = [FloatingPointConversion] floating point conversion
+# 76| Type = [ArithmeticType] _Complex long double
+# 76| ValueCategory = prvalue
+# 76| expr: [VariableAccess] cf
+# 76| Type = [ArithmeticType] _Complex float
+# 76| ValueCategory = prvalue(load)
+# 77| 16: [ExprStmt] ExprStmt
+# 77| 0: [AssignExpr] ... = ...
+# 77| Type = [ArithmeticType] _Complex long double
+# 77| ValueCategory = prvalue
+# 77| 0: [VariableAccess] cld
+# 77| Type = [ArithmeticType] _Complex long double
+# 77| ValueCategory = lvalue
+# 77| 1: [CStyleCast] (_Complex long double)...
+# 77| Conversion = [FloatingPointConversion] floating point conversion
+# 77| Type = [ArithmeticType] _Complex long double
+# 77| ValueCategory = prvalue
+# 77| expr: [VariableAccess] cd
+# 77| Type = [ArithmeticType] _Complex double
+# 77| ValueCategory = prvalue(load)
+# 78| 17: [ExprStmt] ExprStmt
+# 78| 0: [AssignExpr] ... = ...
+# 78| Type = [ArithmeticType] _Complex long double
+# 78| ValueCategory = prvalue
+# 78| 0: [VariableAccess] cld
+# 78| Type = [ArithmeticType] _Complex long double
+# 78| ValueCategory = lvalue
+# 78| 1: [VariableAccess] cld
+# 78| Type = [ArithmeticType] _Complex long double
+# 78| ValueCategory = prvalue(load)
+# 81| 18: [ExprStmt] ExprStmt
+# 81| 0: [AssignExpr] ... = ...
+# 81| Type = [ArithmeticType] _Complex float
+# 81| ValueCategory = prvalue
+# 81| 0: [VariableAccess] cf
+# 81| Type = [ArithmeticType] _Complex float
+# 81| ValueCategory = lvalue
+# 81| 1: [CStyleCast] (_Complex float)...
+# 81| Conversion = [FloatingPointConversion] floating point conversion
+# 81| Type = [ArithmeticType] _Complex float
+# 81| ValueCategory = prvalue
+# 81| expr: [VariableAccess] f
+# 81| Type = [FloatType] float
+# 81| ValueCategory = prvalue(load)
+# 82| 19: [ExprStmt] ExprStmt
+# 82| 0: [AssignExpr] ... = ...
+# 82| Type = [ArithmeticType] _Complex float
+# 82| ValueCategory = prvalue
+# 82| 0: [VariableAccess] cf
+# 82| Type = [ArithmeticType] _Complex float
+# 82| ValueCategory = lvalue
+# 82| 1: [CStyleCast] (_Complex float)...
+# 82| Conversion = [FloatingPointConversion] floating point conversion
+# 82| Type = [ArithmeticType] _Complex float
+# 82| ValueCategory = prvalue
+# 82| expr: [VariableAccess] d
+# 82| Type = [DoubleType] double
+# 82| ValueCategory = prvalue(load)
+# 83| 20: [ExprStmt] ExprStmt
+# 83| 0: [AssignExpr] ... = ...
+# 83| Type = [ArithmeticType] _Complex float
+# 83| ValueCategory = prvalue
+# 83| 0: [VariableAccess] cf
+# 83| Type = [ArithmeticType] _Complex float
+# 83| ValueCategory = lvalue
+# 83| 1: [CStyleCast] (_Complex float)...
+# 83| Conversion = [FloatingPointConversion] floating point conversion
+# 83| Type = [ArithmeticType] _Complex float
+# 83| ValueCategory = prvalue
+# 83| expr: [VariableAccess] ld
+# 83| Type = [LongDoubleType] long double
+# 83| ValueCategory = prvalue(load)
+# 84| 21: [ExprStmt] ExprStmt
+# 84| 0: [AssignExpr] ... = ...
+# 84| Type = [ArithmeticType] _Complex double
+# 84| ValueCategory = prvalue
+# 84| 0: [VariableAccess] cd
+# 84| Type = [ArithmeticType] _Complex double
+# 84| ValueCategory = lvalue
+# 84| 1: [CStyleCast] (_Complex double)...
+# 84| Conversion = [FloatingPointConversion] floating point conversion
+# 84| Type = [ArithmeticType] _Complex double
+# 84| ValueCategory = prvalue
+# 84| expr: [VariableAccess] f
+# 84| Type = [FloatType] float
+# 84| ValueCategory = prvalue(load)
+# 85| 22: [ExprStmt] ExprStmt
+# 85| 0: [AssignExpr] ... = ...
+# 85| Type = [ArithmeticType] _Complex double
+# 85| ValueCategory = prvalue
+# 85| 0: [VariableAccess] cd
+# 85| Type = [ArithmeticType] _Complex double
+# 85| ValueCategory = lvalue
+# 85| 1: [CStyleCast] (_Complex double)...
+# 85| Conversion = [FloatingPointConversion] floating point conversion
+# 85| Type = [ArithmeticType] _Complex double
+# 85| ValueCategory = prvalue
+# 85| expr: [VariableAccess] d
+# 85| Type = [DoubleType] double
+# 85| ValueCategory = prvalue(load)
+# 86| 23: [ExprStmt] ExprStmt
+# 86| 0: [AssignExpr] ... = ...
+# 86| Type = [ArithmeticType] _Complex double
+# 86| ValueCategory = prvalue
+# 86| 0: [VariableAccess] cd
+# 86| Type = [ArithmeticType] _Complex double
+# 86| ValueCategory = lvalue
+# 86| 1: [CStyleCast] (_Complex double)...
+# 86| Conversion = [FloatingPointConversion] floating point conversion
+# 86| Type = [ArithmeticType] _Complex double
+# 86| ValueCategory = prvalue
+# 86| expr: [VariableAccess] ld
+# 86| Type = [LongDoubleType] long double
+# 86| ValueCategory = prvalue(load)
+# 87| 24: [ExprStmt] ExprStmt
+# 87| 0: [AssignExpr] ... = ...
+# 87| Type = [ArithmeticType] _Complex long double
+# 87| ValueCategory = prvalue
+# 87| 0: [VariableAccess] cld
+# 87| Type = [ArithmeticType] _Complex long double
+# 87| ValueCategory = lvalue
+# 87| 1: [CStyleCast] (_Complex long double)...
+# 87| Conversion = [FloatingPointConversion] floating point conversion
+# 87| Type = [ArithmeticType] _Complex long double
+# 87| ValueCategory = prvalue
+# 87| expr: [VariableAccess] f
+# 87| Type = [FloatType] float
+# 87| ValueCategory = prvalue(load)
+# 88| 25: [ExprStmt] ExprStmt
+# 88| 0: [AssignExpr] ... = ...
+# 88| Type = [ArithmeticType] _Complex long double
+# 88| ValueCategory = prvalue
+# 88| 0: [VariableAccess] cld
+# 88| Type = [ArithmeticType] _Complex long double
+# 88| ValueCategory = lvalue
+# 88| 1: [CStyleCast] (_Complex long double)...
+# 88| Conversion = [FloatingPointConversion] floating point conversion
+# 88| Type = [ArithmeticType] _Complex long double
+# 88| ValueCategory = prvalue
+# 88| expr: [VariableAccess] d
+# 88| Type = [DoubleType] double
+# 88| ValueCategory = prvalue(load)
+# 89| 26: [ExprStmt] ExprStmt
+# 89| 0: [AssignExpr] ... = ...
+# 89| Type = [ArithmeticType] _Complex long double
+# 89| ValueCategory = prvalue
+# 89| 0: [VariableAccess] cld
+# 89| Type = [ArithmeticType] _Complex long double
+# 89| ValueCategory = lvalue
+# 89| 1: [CStyleCast] (_Complex long double)...
+# 89| Conversion = [FloatingPointConversion] floating point conversion
+# 89| Type = [ArithmeticType] _Complex long double
+# 89| ValueCategory = prvalue
+# 89| expr: [VariableAccess] ld
+# 89| Type = [LongDoubleType] long double
+# 89| ValueCategory = prvalue(load)
+# 92| 27: [ExprStmt] ExprStmt
+# 92| 0: [AssignExpr] ... = ...
+# 92| Type = [FloatType] float
+# 92| ValueCategory = prvalue
+# 92| 0: [VariableAccess] f
+# 92| Type = [FloatType] float
+# 92| ValueCategory = lvalue
+# 92| 1: [CStyleCast] (float)...
+# 92| Conversion = [FloatingPointConversion] floating point conversion
+# 92| Type = [FloatType] float
+# 92| ValueCategory = prvalue
+# 92| expr: [VariableAccess] cf
+# 92| Type = [ArithmeticType] _Complex float
+# 92| ValueCategory = prvalue(load)
+# 93| 28: [ExprStmt] ExprStmt
+# 93| 0: [AssignExpr] ... = ...
+# 93| Type = [FloatType] float
+# 93| ValueCategory = prvalue
+# 93| 0: [VariableAccess] f
+# 93| Type = [FloatType] float
+# 93| ValueCategory = lvalue
+# 93| 1: [CStyleCast] (float)...
+# 93| Conversion = [FloatingPointConversion] floating point conversion
+# 93| Type = [FloatType] float
+# 93| ValueCategory = prvalue
+# 93| expr: [VariableAccess] cd
+# 93| Type = [ArithmeticType] _Complex double
+# 93| ValueCategory = prvalue(load)
+# 94| 29: [ExprStmt] ExprStmt
+# 94| 0: [AssignExpr] ... = ...
+# 94| Type = [FloatType] float
+# 94| ValueCategory = prvalue
+# 94| 0: [VariableAccess] f
+# 94| Type = [FloatType] float
+# 94| ValueCategory = lvalue
+# 94| 1: [CStyleCast] (float)...
+# 94| Conversion = [FloatingPointConversion] floating point conversion
+# 94| Type = [FloatType] float
+# 94| ValueCategory = prvalue
+# 94| expr: [VariableAccess] cld
+# 94| Type = [ArithmeticType] _Complex long double
+# 94| ValueCategory = prvalue(load)
+# 95| 30: [ExprStmt] ExprStmt
+# 95| 0: [AssignExpr] ... = ...
+# 95| Type = [DoubleType] double
+# 95| ValueCategory = prvalue
+# 95| 0: [VariableAccess] d
+# 95| Type = [DoubleType] double
+# 95| ValueCategory = lvalue
+# 95| 1: [CStyleCast] (double)...
+# 95| Conversion = [FloatingPointConversion] floating point conversion
+# 95| Type = [DoubleType] double
+# 95| ValueCategory = prvalue
+# 95| expr: [VariableAccess] cf
+# 95| Type = [ArithmeticType] _Complex float
+# 95| ValueCategory = prvalue(load)
+# 96| 31: [ExprStmt] ExprStmt
+# 96| 0: [AssignExpr] ... = ...
+# 96| Type = [DoubleType] double
+# 96| ValueCategory = prvalue
+# 96| 0: [VariableAccess] d
+# 96| Type = [DoubleType] double
+# 96| ValueCategory = lvalue
+# 96| 1: [CStyleCast] (double)...
+# 96| Conversion = [FloatingPointConversion] floating point conversion
+# 96| Type = [DoubleType] double
+# 96| ValueCategory = prvalue
+# 96| expr: [VariableAccess] cd
+# 96| Type = [ArithmeticType] _Complex double
+# 96| ValueCategory = prvalue(load)
+# 97| 32: [ExprStmt] ExprStmt
+# 97| 0: [AssignExpr] ... = ...
+# 97| Type = [DoubleType] double
+# 97| ValueCategory = prvalue
+# 97| 0: [VariableAccess] d
+# 97| Type = [DoubleType] double
+# 97| ValueCategory = lvalue
+# 97| 1: [CStyleCast] (double)...
+# 97| Conversion = [FloatingPointConversion] floating point conversion
+# 97| Type = [DoubleType] double
+# 97| ValueCategory = prvalue
+# 97| expr: [VariableAccess] cld
+# 97| Type = [ArithmeticType] _Complex long double
+# 97| ValueCategory = prvalue(load)
+# 98| 33: [ExprStmt] ExprStmt
+# 98| 0: [AssignExpr] ... = ...
+# 98| Type = [LongDoubleType] long double
+# 98| ValueCategory = prvalue
+# 98| 0: [VariableAccess] ld
+# 98| Type = [LongDoubleType] long double
+# 98| ValueCategory = lvalue
+# 98| 1: [CStyleCast] (long double)...
+# 98| Conversion = [FloatingPointConversion] floating point conversion
+# 98| Type = [LongDoubleType] long double
+# 98| ValueCategory = prvalue
+# 98| expr: [VariableAccess] cf
+# 98| Type = [ArithmeticType] _Complex float
+# 98| ValueCategory = prvalue(load)
+# 99| 34: [ExprStmt] ExprStmt
+# 99| 0: [AssignExpr] ... = ...
+# 99| Type = [LongDoubleType] long double
+# 99| ValueCategory = prvalue
+# 99| 0: [VariableAccess] ld
+# 99| Type = [LongDoubleType] long double
+# 99| ValueCategory = lvalue
+# 99| 1: [CStyleCast] (long double)...
+# 99| Conversion = [FloatingPointConversion] floating point conversion
+# 99| Type = [LongDoubleType] long double
+# 99| ValueCategory = prvalue
+# 99| expr: [VariableAccess] cd
+# 99| Type = [ArithmeticType] _Complex double
+# 99| ValueCategory = prvalue(load)
+# 100| 35: [ExprStmt] ExprStmt
+# 100| 0: [AssignExpr] ... = ...
+# 100| Type = [LongDoubleType] long double
+# 100| ValueCategory = prvalue
+# 100| 0: [VariableAccess] ld
+# 100| Type = [LongDoubleType] long double
+# 100| ValueCategory = lvalue
+# 100| 1: [CStyleCast] (long double)...
+# 100| Conversion = [FloatingPointConversion] floating point conversion
+# 100| Type = [LongDoubleType] long double
+# 100| ValueCategory = prvalue
+# 100| expr: [VariableAccess] cld
+# 100| Type = [ArithmeticType] _Complex long double
+# 100| ValueCategory = prvalue(load)
+# 103| 36: [ExprStmt] ExprStmt
+# 103| 0: [AssignExpr] ... = ...
+# 103| Type = [ArithmeticType] _Complex float
+# 103| ValueCategory = prvalue
+# 103| 0: [VariableAccess] cf
+# 103| Type = [ArithmeticType] _Complex float
+# 103| ValueCategory = lvalue
+# 103| 1: [CStyleCast] (_Complex float)...
+# 103| Conversion = [FloatingPointConversion] floating point conversion
+# 103| Type = [ArithmeticType] _Complex float
+# 103| ValueCategory = prvalue
+# 103| expr: [VariableAccess] jf
+# 103| Type = [ArithmeticType] _Imaginary float
+# 103| ValueCategory = prvalue(load)
+# 104| 37: [ExprStmt] ExprStmt
+# 104| 0: [AssignExpr] ... = ...
+# 104| Type = [ArithmeticType] _Complex float
+# 104| ValueCategory = prvalue
+# 104| 0: [VariableAccess] cf
+# 104| Type = [ArithmeticType] _Complex float
+# 104| ValueCategory = lvalue
+# 104| 1: [CStyleCast] (_Complex float)...
+# 104| Conversion = [FloatingPointConversion] floating point conversion
+# 104| Type = [ArithmeticType] _Complex float
+# 104| ValueCategory = prvalue
+# 104| expr: [VariableAccess] jd
+# 104| Type = [ArithmeticType] _Imaginary double
+# 104| ValueCategory = prvalue(load)
+# 105| 38: [ExprStmt] ExprStmt
+# 105| 0: [AssignExpr] ... = ...
+# 105| Type = [ArithmeticType] _Complex float
+# 105| ValueCategory = prvalue
+# 105| 0: [VariableAccess] cf
+# 105| Type = [ArithmeticType] _Complex float
+# 105| ValueCategory = lvalue
+# 105| 1: [CStyleCast] (_Complex float)...
+# 105| Conversion = [FloatingPointConversion] floating point conversion
+# 105| Type = [ArithmeticType] _Complex float
+# 105| ValueCategory = prvalue
+# 105| expr: [VariableAccess] jld
+# 105| Type = [ArithmeticType] _Imaginary long double
+# 105| ValueCategory = prvalue(load)
+# 106| 39: [ExprStmt] ExprStmt
+# 106| 0: [AssignExpr] ... = ...
+# 106| Type = [ArithmeticType] _Complex double
+# 106| ValueCategory = prvalue
+# 106| 0: [VariableAccess] cd
+# 106| Type = [ArithmeticType] _Complex double
+# 106| ValueCategory = lvalue
+# 106| 1: [CStyleCast] (_Complex double)...
+# 106| Conversion = [FloatingPointConversion] floating point conversion
+# 106| Type = [ArithmeticType] _Complex double
+# 106| ValueCategory = prvalue
+# 106| expr: [VariableAccess] jf
+# 106| Type = [ArithmeticType] _Imaginary float
+# 106| ValueCategory = prvalue(load)
+# 107| 40: [ExprStmt] ExprStmt
+# 107| 0: [AssignExpr] ... = ...
+# 107| Type = [ArithmeticType] _Complex double
+# 107| ValueCategory = prvalue
+# 107| 0: [VariableAccess] cd
+# 107| Type = [ArithmeticType] _Complex double
+# 107| ValueCategory = lvalue
+# 107| 1: [CStyleCast] (_Complex double)...
+# 107| Conversion = [FloatingPointConversion] floating point conversion
+# 107| Type = [ArithmeticType] _Complex double
+# 107| ValueCategory = prvalue
+# 107| expr: [VariableAccess] jd
+# 107| Type = [ArithmeticType] _Imaginary double
+# 107| ValueCategory = prvalue(load)
+# 108| 41: [ExprStmt] ExprStmt
+# 108| 0: [AssignExpr] ... = ...
+# 108| Type = [ArithmeticType] _Complex double
+# 108| ValueCategory = prvalue
+# 108| 0: [VariableAccess] cd
+# 108| Type = [ArithmeticType] _Complex double
+# 108| ValueCategory = lvalue
+# 108| 1: [CStyleCast] (_Complex double)...
+# 108| Conversion = [FloatingPointConversion] floating point conversion
+# 108| Type = [ArithmeticType] _Complex double
+# 108| ValueCategory = prvalue
+# 108| expr: [VariableAccess] jld
+# 108| Type = [ArithmeticType] _Imaginary long double
+# 108| ValueCategory = prvalue(load)
+# 109| 42: [ExprStmt] ExprStmt
+# 109| 0: [AssignExpr] ... = ...
+# 109| Type = [ArithmeticType] _Complex long double
+# 109| ValueCategory = prvalue
+# 109| 0: [VariableAccess] cld
+# 109| Type = [ArithmeticType] _Complex long double
+# 109| ValueCategory = lvalue
+# 109| 1: [CStyleCast] (_Complex long double)...
+# 109| Conversion = [FloatingPointConversion] floating point conversion
+# 109| Type = [ArithmeticType] _Complex long double
+# 109| ValueCategory = prvalue
+# 109| expr: [VariableAccess] jf
+# 109| Type = [ArithmeticType] _Imaginary float
+# 109| ValueCategory = prvalue(load)
+# 110| 43: [ExprStmt] ExprStmt
+# 110| 0: [AssignExpr] ... = ...
+# 110| Type = [ArithmeticType] _Complex long double
+# 110| ValueCategory = prvalue
+# 110| 0: [VariableAccess] cld
+# 110| Type = [ArithmeticType] _Complex long double
+# 110| ValueCategory = lvalue
+# 110| 1: [CStyleCast] (_Complex long double)...
+# 110| Conversion = [FloatingPointConversion] floating point conversion
+# 110| Type = [ArithmeticType] _Complex long double
+# 110| ValueCategory = prvalue
+# 110| expr: [VariableAccess] jd
+# 110| Type = [ArithmeticType] _Imaginary double
+# 110| ValueCategory = prvalue(load)
+# 111| 44: [ExprStmt] ExprStmt
+# 111| 0: [AssignExpr] ... = ...
+# 111| Type = [ArithmeticType] _Complex long double
+# 111| ValueCategory = prvalue
+# 111| 0: [VariableAccess] cld
+# 111| Type = [ArithmeticType] _Complex long double
+# 111| ValueCategory = lvalue
+# 111| 1: [CStyleCast] (_Complex long double)...
+# 111| Conversion = [FloatingPointConversion] floating point conversion
+# 111| Type = [ArithmeticType] _Complex long double
+# 111| ValueCategory = prvalue
+# 111| expr: [VariableAccess] jld
+# 111| Type = [ArithmeticType] _Imaginary long double
+# 111| ValueCategory = prvalue(load)
+# 114| 45: [ExprStmt] ExprStmt
+# 114| 0: [AssignExpr] ... = ...
+# 114| Type = [ArithmeticType] _Imaginary float
+# 114| ValueCategory = prvalue
+# 114| 0: [VariableAccess] jf
+# 114| Type = [ArithmeticType] _Imaginary float
+# 114| ValueCategory = lvalue
+# 114| 1: [CStyleCast] (_Imaginary float)...
+# 114| Conversion = [FloatingPointConversion] floating point conversion
+# 114| Type = [ArithmeticType] _Imaginary float
+# 114| ValueCategory = prvalue
+# 114| expr: [VariableAccess] cf
+# 114| Type = [ArithmeticType] _Complex float
+# 114| ValueCategory = prvalue(load)
+# 115| 46: [ExprStmt] ExprStmt
+# 115| 0: [AssignExpr] ... = ...
+# 115| Type = [ArithmeticType] _Imaginary float
+# 115| ValueCategory = prvalue
+# 115| 0: [VariableAccess] jf
+# 115| Type = [ArithmeticType] _Imaginary float
+# 115| ValueCategory = lvalue
+# 115| 1: [CStyleCast] (_Imaginary float)...
+# 115| Conversion = [FloatingPointConversion] floating point conversion
+# 115| Type = [ArithmeticType] _Imaginary float
+# 115| ValueCategory = prvalue
+# 115| expr: [VariableAccess] cd
+# 115| Type = [ArithmeticType] _Complex double
+# 115| ValueCategory = prvalue(load)
+# 116| 47: [ExprStmt] ExprStmt
+# 116| 0: [AssignExpr] ... = ...
+# 116| Type = [ArithmeticType] _Imaginary float
+# 116| ValueCategory = prvalue
+# 116| 0: [VariableAccess] jf
+# 116| Type = [ArithmeticType] _Imaginary float
+# 116| ValueCategory = lvalue
+# 116| 1: [CStyleCast] (_Imaginary float)...
+# 116| Conversion = [FloatingPointConversion] floating point conversion
+# 116| Type = [ArithmeticType] _Imaginary float
+# 116| ValueCategory = prvalue
+# 116| expr: [VariableAccess] cld
+# 116| Type = [ArithmeticType] _Complex long double
+# 116| ValueCategory = prvalue(load)
+# 117| 48: [ExprStmt] ExprStmt
+# 117| 0: [AssignExpr] ... = ...
+# 117| Type = [ArithmeticType] _Imaginary double
+# 117| ValueCategory = prvalue
+# 117| 0: [VariableAccess] jd
+# 117| Type = [ArithmeticType] _Imaginary double
+# 117| ValueCategory = lvalue
+# 117| 1: [CStyleCast] (_Imaginary double)...
+# 117| Conversion = [FloatingPointConversion] floating point conversion
+# 117| Type = [ArithmeticType] _Imaginary double
+# 117| ValueCategory = prvalue
+# 117| expr: [VariableAccess] cf
+# 117| Type = [ArithmeticType] _Complex float
+# 117| ValueCategory = prvalue(load)
+# 118| 49: [ExprStmt] ExprStmt
+# 118| 0: [AssignExpr] ... = ...
+# 118| Type = [ArithmeticType] _Imaginary double
+# 118| ValueCategory = prvalue
+# 118| 0: [VariableAccess] jd
+# 118| Type = [ArithmeticType] _Imaginary double
+# 118| ValueCategory = lvalue
+# 118| 1: [CStyleCast] (_Imaginary double)...
+# 118| Conversion = [FloatingPointConversion] floating point conversion
+# 118| Type = [ArithmeticType] _Imaginary double
+# 118| ValueCategory = prvalue
+# 118| expr: [VariableAccess] cd
+# 118| Type = [ArithmeticType] _Complex double
+# 118| ValueCategory = prvalue(load)
+# 119| 50: [ExprStmt] ExprStmt
+# 119| 0: [AssignExpr] ... = ...
+# 119| Type = [ArithmeticType] _Imaginary double
+# 119| ValueCategory = prvalue
+# 119| 0: [VariableAccess] jd
+# 119| Type = [ArithmeticType] _Imaginary double
+# 119| ValueCategory = lvalue
+# 119| 1: [CStyleCast] (_Imaginary double)...
+# 119| Conversion = [FloatingPointConversion] floating point conversion
+# 119| Type = [ArithmeticType] _Imaginary double
+# 119| ValueCategory = prvalue
+# 119| expr: [VariableAccess] cld
+# 119| Type = [ArithmeticType] _Complex long double
+# 119| ValueCategory = prvalue(load)
+# 120| 51: [ExprStmt] ExprStmt
+# 120| 0: [AssignExpr] ... = ...
+# 120| Type = [ArithmeticType] _Imaginary long double
+# 120| ValueCategory = prvalue
+# 120| 0: [VariableAccess] jld
+# 120| Type = [ArithmeticType] _Imaginary long double
+# 120| ValueCategory = lvalue
+# 120| 1: [CStyleCast] (_Imaginary long double)...
+# 120| Conversion = [FloatingPointConversion] floating point conversion
+# 120| Type = [ArithmeticType] _Imaginary long double
+# 120| ValueCategory = prvalue
+# 120| expr: [VariableAccess] cf
+# 120| Type = [ArithmeticType] _Complex float
+# 120| ValueCategory = prvalue(load)
+# 121| 52: [ExprStmt] ExprStmt
+# 121| 0: [AssignExpr] ... = ...
+# 121| Type = [ArithmeticType] _Imaginary long double
+# 121| ValueCategory = prvalue
+# 121| 0: [VariableAccess] jld
+# 121| Type = [ArithmeticType] _Imaginary long double
+# 121| ValueCategory = lvalue
+# 121| 1: [CStyleCast] (_Imaginary long double)...
+# 121| Conversion = [FloatingPointConversion] floating point conversion
+# 121| Type = [ArithmeticType] _Imaginary long double
+# 121| ValueCategory = prvalue
+# 121| expr: [VariableAccess] cd
+# 121| Type = [ArithmeticType] _Complex double
+# 121| ValueCategory = prvalue(load)
+# 122| 53: [ExprStmt] ExprStmt
+# 122| 0: [AssignExpr] ... = ...
+# 122| Type = [ArithmeticType] _Imaginary long double
+# 122| ValueCategory = prvalue
+# 122| 0: [VariableAccess] jld
+# 122| Type = [ArithmeticType] _Imaginary long double
+# 122| ValueCategory = lvalue
+# 122| 1: [CStyleCast] (_Imaginary long double)...
+# 122| Conversion = [FloatingPointConversion] floating point conversion
+# 122| Type = [ArithmeticType] _Imaginary long double
+# 122| ValueCategory = prvalue
+# 122| expr: [VariableAccess] cld
+# 122| Type = [ArithmeticType] _Complex long double
+# 122| ValueCategory = prvalue(load)
+# 125| 54: [ExprStmt] ExprStmt
+# 125| 0: [AssignExpr] ... = ...
+# 125| Type = [ArithmeticType] _Imaginary float
+# 125| ValueCategory = prvalue
+# 125| 0: [VariableAccess] jf
+# 125| Type = [ArithmeticType] _Imaginary float
+# 125| ValueCategory = lvalue
+# 125| 1: [CStyleCast] (_Imaginary float)...
+# 125| Conversion = [FloatingPointConversion] floating point conversion
+# 125| Type = [ArithmeticType] _Imaginary float
+# 125| ValueCategory = prvalue
+# 125| expr: [VariableAccess] f
+# 125| Type = [FloatType] float
+# 125| ValueCategory = prvalue(load)
+# 126| 55: [ExprStmt] ExprStmt
+# 126| 0: [AssignExpr] ... = ...
+# 126| Type = [ArithmeticType] _Imaginary float
+# 126| ValueCategory = prvalue
+# 126| 0: [VariableAccess] jf
+# 126| Type = [ArithmeticType] _Imaginary float
+# 126| ValueCategory = lvalue
+# 126| 1: [CStyleCast] (_Imaginary float)...
+# 126| Conversion = [FloatingPointConversion] floating point conversion
+# 126| Type = [ArithmeticType] _Imaginary float
+# 126| ValueCategory = prvalue
+# 126| expr: [VariableAccess] d
+# 126| Type = [DoubleType] double
+# 126| ValueCategory = prvalue(load)
+# 127| 56: [ExprStmt] ExprStmt
+# 127| 0: [AssignExpr] ... = ...
+# 127| Type = [ArithmeticType] _Imaginary float
+# 127| ValueCategory = prvalue
+# 127| 0: [VariableAccess] jf
+# 127| Type = [ArithmeticType] _Imaginary float
+# 127| ValueCategory = lvalue
+# 127| 1: [CStyleCast] (_Imaginary float)...
+# 127| Conversion = [FloatingPointConversion] floating point conversion
+# 127| Type = [ArithmeticType] _Imaginary float
+# 127| ValueCategory = prvalue
+# 127| expr: [VariableAccess] ld
+# 127| Type = [LongDoubleType] long double
+# 127| ValueCategory = prvalue(load)
+# 128| 57: [ExprStmt] ExprStmt
+# 128| 0: [AssignExpr] ... = ...
+# 128| Type = [ArithmeticType] _Imaginary double
+# 128| ValueCategory = prvalue
+# 128| 0: [VariableAccess] jd
+# 128| Type = [ArithmeticType] _Imaginary double
+# 128| ValueCategory = lvalue
+# 128| 1: [CStyleCast] (_Imaginary double)...
+# 128| Conversion = [FloatingPointConversion] floating point conversion
+# 128| Type = [ArithmeticType] _Imaginary double
+# 128| ValueCategory = prvalue
+# 128| expr: [VariableAccess] f
+# 128| Type = [FloatType] float
+# 128| ValueCategory = prvalue(load)
+# 129| 58: [ExprStmt] ExprStmt
+# 129| 0: [AssignExpr] ... = ...
+# 129| Type = [ArithmeticType] _Imaginary double
+# 129| ValueCategory = prvalue
+# 129| 0: [VariableAccess] jd
+# 129| Type = [ArithmeticType] _Imaginary double
+# 129| ValueCategory = lvalue
+# 129| 1: [CStyleCast] (_Imaginary double)...
+# 129| Conversion = [FloatingPointConversion] floating point conversion
+# 129| Type = [ArithmeticType] _Imaginary double
+# 129| ValueCategory = prvalue
+# 129| expr: [VariableAccess] d
+# 129| Type = [DoubleType] double
+# 129| ValueCategory = prvalue(load)
+# 130| 59: [ExprStmt] ExprStmt
+# 130| 0: [AssignExpr] ... = ...
+# 130| Type = [ArithmeticType] _Imaginary double
+# 130| ValueCategory = prvalue
+# 130| 0: [VariableAccess] jd
+# 130| Type = [ArithmeticType] _Imaginary double
+# 130| ValueCategory = lvalue
+# 130| 1: [CStyleCast] (_Imaginary double)...
+# 130| Conversion = [FloatingPointConversion] floating point conversion
+# 130| Type = [ArithmeticType] _Imaginary double
+# 130| ValueCategory = prvalue
+# 130| expr: [VariableAccess] ld
+# 130| Type = [LongDoubleType] long double
+# 130| ValueCategory = prvalue(load)
+# 131| 60: [ExprStmt] ExprStmt
+# 131| 0: [AssignExpr] ... = ...
+# 131| Type = [ArithmeticType] _Imaginary long double
+# 131| ValueCategory = prvalue
+# 131| 0: [VariableAccess] jld
+# 131| Type = [ArithmeticType] _Imaginary long double
+# 131| ValueCategory = lvalue
+# 131| 1: [CStyleCast] (_Imaginary long double)...
+# 131| Conversion = [FloatingPointConversion] floating point conversion
+# 131| Type = [ArithmeticType] _Imaginary long double
+# 131| ValueCategory = prvalue
+# 131| expr: [VariableAccess] f
+# 131| Type = [FloatType] float
+# 131| ValueCategory = prvalue(load)
+# 132| 61: [ExprStmt] ExprStmt
+# 132| 0: [AssignExpr] ... = ...
+# 132| Type = [ArithmeticType] _Imaginary long double
+# 132| ValueCategory = prvalue
+# 132| 0: [VariableAccess] jld
+# 132| Type = [ArithmeticType] _Imaginary long double
+# 132| ValueCategory = lvalue
+# 132| 1: [CStyleCast] (_Imaginary long double)...
+# 132| Conversion = [FloatingPointConversion] floating point conversion
+# 132| Type = [ArithmeticType] _Imaginary long double
+# 132| ValueCategory = prvalue
+# 132| expr: [VariableAccess] d
+# 132| Type = [DoubleType] double
+# 132| ValueCategory = prvalue(load)
+# 133| 62: [ExprStmt] ExprStmt
+# 133| 0: [AssignExpr] ... = ...
+# 133| Type = [ArithmeticType] _Imaginary long double
+# 133| ValueCategory = prvalue
+# 133| 0: [VariableAccess] jld
+# 133| Type = [ArithmeticType] _Imaginary long double
+# 133| ValueCategory = lvalue
+# 133| 1: [CStyleCast] (_Imaginary long double)...
+# 133| Conversion = [FloatingPointConversion] floating point conversion
+# 133| Type = [ArithmeticType] _Imaginary long double
+# 133| ValueCategory = prvalue
+# 133| expr: [VariableAccess] ld
+# 133| Type = [LongDoubleType] long double
+# 133| ValueCategory = prvalue(load)
+# 136| 63: [ExprStmt] ExprStmt
+# 136| 0: [AssignExpr] ... = ...
+# 136| Type = [FloatType] float
+# 136| ValueCategory = prvalue
+# 136| 0: [VariableAccess] f
+# 136| Type = [FloatType] float
+# 136| ValueCategory = lvalue
+# 136| 1: [CStyleCast] (float)...
+# 136| Conversion = [FloatingPointConversion] floating point conversion
+# 136| Type = [FloatType] float
+# 136| ValueCategory = prvalue
+# 136| expr: [VariableAccess] jf
+# 136| Type = [ArithmeticType] _Imaginary float
+# 136| ValueCategory = prvalue(load)
+# 137| 64: [ExprStmt] ExprStmt
+# 137| 0: [AssignExpr] ... = ...
+# 137| Type = [FloatType] float
+# 137| ValueCategory = prvalue
+# 137| 0: [VariableAccess] f
+# 137| Type = [FloatType] float
+# 137| ValueCategory = lvalue
+# 137| 1: [CStyleCast] (float)...
+# 137| Conversion = [FloatingPointConversion] floating point conversion
+# 137| Type = [FloatType] float
+# 137| ValueCategory = prvalue
+# 137| expr: [VariableAccess] jd
+# 137| Type = [ArithmeticType] _Imaginary double
+# 137| ValueCategory = prvalue(load)
+# 138| 65: [ExprStmt] ExprStmt
+# 138| 0: [AssignExpr] ... = ...
+# 138| Type = [FloatType] float
+# 138| ValueCategory = prvalue
+# 138| 0: [VariableAccess] f
+# 138| Type = [FloatType] float
+# 138| ValueCategory = lvalue
+# 138| 1: [CStyleCast] (float)...
+# 138| Conversion = [FloatingPointConversion] floating point conversion
+# 138| Type = [FloatType] float
+# 138| ValueCategory = prvalue
+# 138| expr: [VariableAccess] jld
+# 138| Type = [ArithmeticType] _Imaginary long double
+# 138| ValueCategory = prvalue(load)
+# 139| 66: [ExprStmt] ExprStmt
+# 139| 0: [AssignExpr] ... = ...
+# 139| Type = [DoubleType] double
+# 139| ValueCategory = prvalue
+# 139| 0: [VariableAccess] d
+# 139| Type = [DoubleType] double
+# 139| ValueCategory = lvalue
+# 139| 1: [CStyleCast] (double)...
+# 139| Conversion = [FloatingPointConversion] floating point conversion
+# 139| Type = [DoubleType] double
+# 139| ValueCategory = prvalue
+# 139| expr: [VariableAccess] jf
+# 139| Type = [ArithmeticType] _Imaginary float
+# 139| ValueCategory = prvalue(load)
+# 140| 67: [ExprStmt] ExprStmt
+# 140| 0: [AssignExpr] ... = ...
+# 140| Type = [DoubleType] double
+# 140| ValueCategory = prvalue
+# 140| 0: [VariableAccess] d
+# 140| Type = [DoubleType] double
+# 140| ValueCategory = lvalue
+# 140| 1: [CStyleCast] (double)...
+# 140| Conversion = [FloatingPointConversion] floating point conversion
+# 140| Type = [DoubleType] double
+# 140| ValueCategory = prvalue
+# 140| expr: [VariableAccess] jd
+# 140| Type = [ArithmeticType] _Imaginary double
+# 140| ValueCategory = prvalue(load)
+# 141| 68: [ExprStmt] ExprStmt
+# 141| 0: [AssignExpr] ... = ...
+# 141| Type = [DoubleType] double
+# 141| ValueCategory = prvalue
+# 141| 0: [VariableAccess] d
+# 141| Type = [DoubleType] double
+# 141| ValueCategory = lvalue
+# 141| 1: [CStyleCast] (double)...
+# 141| Conversion = [FloatingPointConversion] floating point conversion
+# 141| Type = [DoubleType] double
+# 141| ValueCategory = prvalue
+# 141| expr: [VariableAccess] jld
+# 141| Type = [ArithmeticType] _Imaginary long double
+# 141| ValueCategory = prvalue(load)
+# 142| 69: [ExprStmt] ExprStmt
+# 142| 0: [AssignExpr] ... = ...
+# 142| Type = [LongDoubleType] long double
+# 142| ValueCategory = prvalue
+# 142| 0: [VariableAccess] ld
+# 142| Type = [LongDoubleType] long double
+# 142| ValueCategory = lvalue
+# 142| 1: [CStyleCast] (long double)...
+# 142| Conversion = [FloatingPointConversion] floating point conversion
+# 142| Type = [LongDoubleType] long double
+# 142| ValueCategory = prvalue
+# 142| expr: [VariableAccess] jf
+# 142| Type = [ArithmeticType] _Imaginary float
+# 142| ValueCategory = prvalue(load)
+# 143| 70: [ExprStmt] ExprStmt
+# 143| 0: [AssignExpr] ... = ...
+# 143| Type = [LongDoubleType] long double
+# 143| ValueCategory = prvalue
+# 143| 0: [VariableAccess] ld
+# 143| Type = [LongDoubleType] long double
+# 143| ValueCategory = lvalue
+# 143| 1: [CStyleCast] (long double)...
+# 143| Conversion = [FloatingPointConversion] floating point conversion
+# 143| Type = [LongDoubleType] long double
+# 143| ValueCategory = prvalue
+# 143| expr: [VariableAccess] jd
+# 143| Type = [ArithmeticType] _Imaginary double
+# 143| ValueCategory = prvalue(load)
+# 144| 71: [ExprStmt] ExprStmt
+# 144| 0: [AssignExpr] ... = ...
+# 144| Type = [LongDoubleType] long double
+# 144| ValueCategory = prvalue
+# 144| 0: [VariableAccess] ld
+# 144| Type = [LongDoubleType] long double
+# 144| ValueCategory = lvalue
+# 144| 1: [CStyleCast] (long double)...
+# 144| Conversion = [FloatingPointConversion] floating point conversion
+# 144| Type = [LongDoubleType] long double
+# 144| ValueCategory = prvalue
+# 144| expr: [VariableAccess] jld
+# 144| Type = [ArithmeticType] _Imaginary long double
+# 144| ValueCategory = prvalue(load)
+# 145| 72: [ReturnStmt] return ...
ir.cpp:
# 1| [TopLevelFunction] void Constants()
# 1| params:
@@ -4048,6 +5529,27 @@ ir.cpp:
#-----| Type = [RValueReferenceType] C &&
# 628| [Destructor] void C::~C()
# 628| params:
+#-----| body: [Block] { ... }
+#-----| 0: [ReturnStmt] return ...
+# 628| destructions:
+# 628| 0: [DestructorFieldDestruction] destructor field destruction of m_f
+# 628| Type = [Struct] String
+# 628| ValueCategory = prvalue
+# 628| 0: [DestructorCall] call to ~String
+# 628| Type = [VoidType] void
+# 628| ValueCategory = prvalue
+# 628| -1: [ImplicitThisFieldAccess] m_f
+# 628| Type = [Struct] String
+# 628| ValueCategory = lvalue
+# 628| 1: [DestructorFieldDestruction] destructor field destruction of m_b
+# 628| Type = [Struct] String
+# 628| ValueCategory = prvalue
+# 628| 0: [DestructorCall] call to ~String
+# 628| Type = [VoidType] void
+# 628| ValueCategory = prvalue
+# 628| -1: [ImplicitThisFieldAccess] m_b
+# 628| Type = [Struct] String
+# 628| ValueCategory = lvalue
# 630| [MemberFunction] int C::StaticMemberFunction(int)
# 630| params:
# 630| 0: [Parameter] x
@@ -8545,6 +10047,442 @@ ir.cpp:
# 1255| Type = [CharPointerType] char *
# 1255| ValueCategory = prvalue(load)
# 1256| 3: [ReturnStmt] return ...
+# 1258| [CopyAssignmentOperator] A& A::operator=(A const&)
+# 1258| params:
+#-----| 0: [Parameter] p#0
+#-----| Type = [LValueReferenceType] const A &
+# 1258| [MoveAssignmentOperator] A& A::operator=(A&&)
+# 1258| params:
+#-----| 0: [Parameter] p#0
+#-----| Type = [RValueReferenceType] A &&
+# 1261| [MemberFunction] void A::static_member(A*, int)
+# 1261| params:
+# 1261| 0: [Parameter] a
+# 1261| Type = [PointerType] A *
+# 1261| 1: [Parameter] x
+# 1261| Type = [IntType] int
+# 1261| body: [Block] { ... }
+# 1262| 0: [ExprStmt] ExprStmt
+# 1262| 0: [AssignExpr] ... = ...
+# 1262| Type = [IntType] int
+# 1262| ValueCategory = lvalue
+# 1262| 0: [PointerFieldAccess] member
+# 1262| Type = [IntType] int
+# 1262| ValueCategory = lvalue
+# 1262| -1: [VariableAccess] a
+# 1262| Type = [PointerType] A *
+# 1262| ValueCategory = prvalue(load)
+# 1262| 1: [VariableAccess] x
+# 1262| Type = [IntType] int
+# 1262| ValueCategory = prvalue(load)
+# 1263| 1: [ReturnStmt] return ...
+# 1265| [MemberFunction] void A::static_member_without_def()
+# 1265| params:
+# 1268| [TopLevelFunction] A* getAnInstanceOfA()
+# 1268| params:
+# 1270| [TopLevelFunction] void test_static_member_functions(int, A*)
+# 1270| params:
+# 1270| 0: [Parameter] int_arg
+# 1270| Type = [IntType] int
+# 1270| 1: [Parameter] a_arg
+# 1270| Type = [PointerType] A *
+# 1270| body: [Block] { ... }
+# 1271| 0: [DeclStmt] declaration
+# 1271| 0: [VariableDeclarationEntry] definition of c
+# 1271| Type = [Class] C
+# 1271| init: [Initializer] initializer for c
+# 1271| expr: [ConstructorCall] call to C
+# 1271| Type = [VoidType] void
+# 1271| ValueCategory = prvalue
+# 1272| 1: [ExprStmt] ExprStmt
+# 1272| 0: [FunctionCall] call to StaticMemberFunction
+# 1272| Type = [IntType] int
+# 1272| ValueCategory = prvalue
+# 1272| -1: [VariableAccess] c
+# 1272| Type = [Class] C
+# 1272| ValueCategory = lvalue
+# 1272| 0: [Literal] 10
+# 1272| Type = [IntType] int
+# 1272| Value = [Literal] 10
+# 1272| ValueCategory = prvalue
+# 1273| 2: [ExprStmt] ExprStmt
+# 1273| 0: [FunctionCall] call to StaticMemberFunction
+# 1273| Type = [IntType] int
+# 1273| ValueCategory = prvalue
+# 1273| 0: [Literal] 10
+# 1273| Type = [IntType] int
+# 1273| Value = [Literal] 10
+# 1273| ValueCategory = prvalue
+# 1275| 3: [DeclStmt] declaration
+# 1275| 0: [VariableDeclarationEntry] definition of a
+# 1275| Type = [Struct] A
+# 1276| 4: [ExprStmt] ExprStmt
+# 1276| 0: [FunctionCall] call to static_member
+# 1276| Type = [VoidType] void
+# 1276| ValueCategory = prvalue
+# 1276| -1: [VariableAccess] a
+# 1276| Type = [Struct] A
+# 1276| ValueCategory = lvalue
+# 1276| 0: [AddressOfExpr] & ...
+# 1276| Type = [PointerType] A *
+# 1276| ValueCategory = prvalue
+# 1276| 0: [VariableAccess] a
+# 1276| Type = [Struct] A
+# 1276| ValueCategory = lvalue
+# 1276| 1: [VariableAccess] int_arg
+# 1276| Type = [IntType] int
+# 1276| ValueCategory = prvalue(load)
+# 1277| 5: [ExprStmt] ExprStmt
+# 1277| 0: [FunctionCall] call to static_member
+# 1277| Type = [VoidType] void
+# 1277| ValueCategory = prvalue
+# 1277| 0: [AddressOfExpr] & ...
+# 1277| Type = [PointerType] A *
+# 1277| ValueCategory = prvalue
+# 1277| 0: [VariableAccess] a
+# 1277| Type = [Struct] A
+# 1277| ValueCategory = lvalue
+# 1277| 1: [VariableAccess] int_arg
+# 1277| Type = [IntType] int
+# 1277| ValueCategory = prvalue(load)
+# 1279| 6: [ExprStmt] ExprStmt
+# 1279| 0: [FunctionCall] call to static_member
+# 1279| Type = [VoidType] void
+# 1279| ValueCategory = prvalue
+# 1279| -1: [ParenthesisExpr] (...)
+# 1279| Type = [PointerType] A *
+# 1279| ValueCategory = prvalue
+# 1279| expr: [AddressOfExpr] & ...
+# 1279| Type = [PointerType] A *
+# 1279| ValueCategory = prvalue
+# 1279| 0: [VariableAccess] a
+# 1279| Type = [Struct] A
+# 1279| ValueCategory = lvalue
+# 1279| 0: [VariableAccess] a_arg
+# 1279| Type = [PointerType] A *
+# 1279| ValueCategory = prvalue(load)
+# 1279| 1: [AddExpr] ... + ...
+# 1279| Type = [IntType] int
+# 1279| ValueCategory = prvalue
+# 1279| 0: [VariableAccess] int_arg
+# 1279| Type = [IntType] int
+# 1279| ValueCategory = prvalue(load)
+# 1279| 1: [Literal] 2
+# 1279| Type = [IntType] int
+# 1279| Value = [Literal] 2
+# 1279| ValueCategory = prvalue
+# 1280| 7: [ExprStmt] ExprStmt
+# 1280| 0: [FunctionCall] call to static_member
+# 1280| Type = [VoidType] void
+# 1280| ValueCategory = prvalue
+# 1280| -1: [ParenthesisExpr] (...)
+# 1280| Type = [Struct] A
+# 1280| ValueCategory = lvalue
+# 1280| expr: [PointerDereferenceExpr] * ...
+# 1280| Type = [Struct] A
+# 1280| ValueCategory = lvalue
+# 1280| 0: [VariableAccess] a_arg
+# 1280| Type = [PointerType] A *
+# 1280| ValueCategory = prvalue(load)
+# 1280| 0: [AddressOfExpr] & ...
+# 1280| Type = [PointerType] A *
+# 1280| ValueCategory = prvalue
+# 1280| 0: [VariableAccess] a
+# 1280| Type = [Struct] A
+# 1280| ValueCategory = lvalue
+# 1280| 1: [Literal] 99
+# 1280| Type = [IntType] int
+# 1280| Value = [Literal] 99
+# 1280| ValueCategory = prvalue
+# 1281| 8: [ExprStmt] ExprStmt
+# 1281| 0: [FunctionCall] call to static_member
+# 1281| Type = [VoidType] void
+# 1281| ValueCategory = prvalue
+# 1281| -1: [VariableAccess] a_arg
+# 1281| Type = [PointerType] A *
+# 1281| ValueCategory = prvalue(load)
+# 1281| 0: [VariableAccess] a_arg
+# 1281| Type = [PointerType] A *
+# 1281| ValueCategory = prvalue(load)
+# 1281| 1: [UnaryMinusExpr] - ...
+# 1281| Type = [IntType] int
+# 1281| Value = [UnaryMinusExpr] -1
+# 1281| ValueCategory = prvalue
+# 1281| 0: [Literal] 1
+# 1281| Type = [IntType] int
+# 1281| Value = [Literal] 1
+# 1281| ValueCategory = prvalue
+# 1283| 9: [ExprStmt] ExprStmt
+# 1283| 0: [FunctionCall] call to static_member_without_def
+# 1283| Type = [VoidType] void
+# 1283| ValueCategory = prvalue
+# 1283| -1: [VariableAccess] a
+# 1283| Type = [Struct] A
+# 1283| ValueCategory = lvalue
+# 1284| 10: [ExprStmt] ExprStmt
+# 1284| 0: [FunctionCall] call to static_member_without_def
+# 1284| Type = [VoidType] void
+# 1284| ValueCategory = prvalue
+# 1286| 11: [ExprStmt] ExprStmt
+# 1286| 0: [FunctionCall] call to static_member_without_def
+# 1286| Type = [VoidType] void
+# 1286| ValueCategory = prvalue
+# 1286| -1: [FunctionCall] call to getAnInstanceOfA
+# 1286| Type = [PointerType] A *
+# 1286| ValueCategory = prvalue
+# 1287| 12: [ReturnStmt] return ...
+# 1289| [TopLevelFunction] int missingReturnValue(bool, int)
+# 1289| params:
+# 1289| 0: [Parameter] b
+# 1289| Type = [BoolType] bool
+# 1289| 1: [Parameter] x
+# 1289| Type = [IntType] int
+# 1289| body: [Block] { ... }
+# 1290| 0: [IfStmt] if (...) ...
+# 1290| 0: [VariableAccess] b
+# 1290| Type = [BoolType] bool
+# 1290| ValueCategory = prvalue(load)
+# 1290| 1: [Block] { ... }
+# 1291| 0: [ReturnStmt] return ...
+# 1291| 0: [VariableAccess] x
+# 1291| Type = [IntType] int
+# 1291| ValueCategory = prvalue(load)
+# 1293| 1: [ReturnStmt] return ...
+# 1295| [TopLevelFunction] void returnVoid(int, int)
+# 1295| params:
+# 1295| 0: [Parameter] x
+# 1295| Type = [IntType] int
+# 1295| 1: [Parameter] y
+# 1295| Type = [IntType] int
+# 1295| body: [Block] { ... }
+# 1296| 0: [ReturnStmt] return ...
+# 1296| 0: [FunctionCall] call to IntegerOps
+# 1296| Type = [VoidType] void
+# 1296| ValueCategory = prvalue
+# 1296| 0: [VariableAccess] x
+# 1296| Type = [IntType] int
+# 1296| ValueCategory = prvalue(load)
+# 1296| 1: [VariableAccess] y
+# 1296| Type = [IntType] int
+# 1296| ValueCategory = prvalue(load)
+# 1299| [TopLevelFunction] void gccBinaryConditional(bool, int, long)
+# 1299| params:
+# 1299| 0: [Parameter] b
+# 1299| Type = [BoolType] bool
+# 1299| 1: [Parameter] x
+# 1299| Type = [IntType] int
+# 1299| 2: [Parameter] y
+# 1299| Type = [LongType] long
+# 1299| body: [Block] { ... }
+# 1300| 0: [DeclStmt] declaration
+# 1300| 0: [VariableDeclarationEntry] definition of z
+# 1300| Type = [IntType] int
+# 1300| init: [Initializer] initializer for z
+# 1300| expr: [VariableAccess] x
+# 1300| Type = [IntType] int
+# 1300| ValueCategory = prvalue(load)
+# 1301| 1: [ExprStmt] ExprStmt
+# 1301| 0: [AssignExpr] ... = ...
+# 1301| Type = [IntType] int
+# 1301| ValueCategory = lvalue
+# 1301| 0: [VariableAccess] z
+# 1301| Type = [IntType] int
+# 1301| ValueCategory = lvalue
+# 1301| 1: [ConditionalExpr] ... ? ... : ...
+# 1301| Type = [IntType] int
+# 1301| ValueCategory = prvalue
+# 1301| 0: [VariableAccess] b
+# 1301| Type = [BoolType] bool
+# 1301| ValueCategory = prvalue(load)
+# 1301| 1: [VariableAccess] x
+# 1301| Type = [IntType] int
+# 1301| ValueCategory = prvalue(load)
+# 1302| 2: [ExprStmt] ExprStmt
+# 1302| 0: [AssignExpr] ... = ...
+# 1302| Type = [IntType] int
+# 1302| ValueCategory = lvalue
+# 1302| 0: [VariableAccess] z
+# 1302| Type = [IntType] int
+# 1302| ValueCategory = lvalue
+# 1302| 1: [CStyleCast] (int)...
+# 1302| Conversion = [IntegralConversion] integral conversion
+# 1302| Type = [IntType] int
+# 1302| ValueCategory = prvalue
+# 1302| expr: [ConditionalExpr] ... ? ... : ...
+# 1302| Type = [LongType] long
+# 1302| ValueCategory = prvalue
+# 1302| 0: [VariableAccess] b
+# 1302| Type = [BoolType] bool
+# 1302| ValueCategory = prvalue(load)
+# 1302| 1: [VariableAccess] y
+# 1302| Type = [LongType] long
+# 1302| ValueCategory = prvalue(load)
+# 1303| 3: [ExprStmt] ExprStmt
+# 1303| 0: [AssignExpr] ... = ...
+# 1303| Type = [IntType] int
+# 1303| ValueCategory = lvalue
+# 1303| 0: [VariableAccess] z
+# 1303| Type = [IntType] int
+# 1303| ValueCategory = lvalue
+# 1303| 1: [ConditionalExpr] ... ? ... : ...
+# 1303| Type = [IntType] int
+# 1303| ValueCategory = prvalue
+# 1303| 0: [CStyleCast] (bool)...
+# 1303| Conversion = [BoolConversion] conversion to bool
+# 1303| Type = [BoolType] bool
+# 1303| ValueCategory = prvalue
+# 1303| expr: [VariableAccess] x
+# 1303| Type = [IntType] int
+# 1303| ValueCategory = prvalue(load)
+# 1303| 1: [VariableAccess] x
+# 1303| Type = [IntType] int
+# 1303| ValueCategory = prvalue(load)
+# 1304| 4: [ExprStmt] ExprStmt
+# 1304| 0: [AssignExpr] ... = ...
+# 1304| Type = [IntType] int
+# 1304| ValueCategory = lvalue
+# 1304| 0: [VariableAccess] z
+# 1304| Type = [IntType] int
+# 1304| ValueCategory = lvalue
+# 1304| 1: [CStyleCast] (int)...
+# 1304| Conversion = [IntegralConversion] integral conversion
+# 1304| Type = [IntType] int
+# 1304| ValueCategory = prvalue
+# 1304| expr: [ConditionalExpr] ... ? ... : ...
+# 1304| Type = [LongType] long
+# 1304| ValueCategory = prvalue
+# 1304| 0: [CStyleCast] (bool)...
+# 1304| Conversion = [BoolConversion] conversion to bool
+# 1304| Type = [BoolType] bool
+# 1304| ValueCategory = prvalue
+# 1304| expr: [VariableAccess] x
+# 1304| Type = [IntType] int
+# 1304| ValueCategory = prvalue(load)
+# 1304| 1: [VariableAccess] y
+# 1304| Type = [LongType] long
+# 1304| ValueCategory = prvalue(load)
+# 1305| 5: [ExprStmt] ExprStmt
+# 1305| 0: [AssignExpr] ... = ...
+# 1305| Type = [IntType] int
+# 1305| ValueCategory = lvalue
+# 1305| 0: [VariableAccess] z
+# 1305| Type = [IntType] int
+# 1305| ValueCategory = lvalue
+# 1305| 1: [CStyleCast] (int)...
+# 1305| Conversion = [IntegralConversion] integral conversion
+# 1305| Type = [IntType] int
+# 1305| ValueCategory = prvalue
+# 1305| expr: [ConditionalExpr] ... ? ... : ...
+# 1305| Type = [LongType] long
+# 1305| ValueCategory = prvalue
+# 1305| 0: [CStyleCast] (bool)...
+# 1305| Conversion = [BoolConversion] conversion to bool
+# 1305| Type = [BoolType] bool
+# 1305| ValueCategory = prvalue
+# 1305| expr: [VariableAccess] y
+# 1305| Type = [LongType] long
+# 1305| ValueCategory = prvalue(load)
+# 1305| 1: [CStyleCast] (long)...
+# 1305| Conversion = [IntegralConversion] integral conversion
+# 1305| Type = [LongType] long
+# 1305| ValueCategory = prvalue
+# 1305| expr: [VariableAccess] x
+# 1305| Type = [IntType] int
+# 1305| ValueCategory = prvalue(load)
+# 1306| 6: [ExprStmt] ExprStmt
+# 1306| 0: [AssignExpr] ... = ...
+# 1306| Type = [IntType] int
+# 1306| ValueCategory = lvalue
+# 1306| 0: [VariableAccess] z
+# 1306| Type = [IntType] int
+# 1306| ValueCategory = lvalue
+# 1306| 1: [CStyleCast] (int)...
+# 1306| Conversion = [IntegralConversion] integral conversion
+# 1306| Type = [IntType] int
+# 1306| ValueCategory = prvalue
+# 1306| expr: [ConditionalExpr] ... ? ... : ...
+# 1306| Type = [LongType] long
+# 1306| ValueCategory = prvalue
+# 1306| 0: [CStyleCast] (bool)...
+# 1306| Conversion = [BoolConversion] conversion to bool
+# 1306| Type = [BoolType] bool
+# 1306| ValueCategory = prvalue
+# 1306| expr: [VariableAccess] y
+# 1306| Type = [LongType] long
+# 1306| ValueCategory = prvalue(load)
+# 1306| 1: [VariableAccess] y
+# 1306| Type = [LongType] long
+# 1306| ValueCategory = prvalue(load)
+# 1308| 7: [ExprStmt] ExprStmt
+# 1308| 0: [AssignExpr] ... = ...
+# 1308| Type = [IntType] int
+# 1308| ValueCategory = lvalue
+# 1308| 0: [VariableAccess] z
+# 1308| Type = [IntType] int
+# 1308| ValueCategory = lvalue
+# 1308| 1: [ConditionalExpr] ... ? ... : ...
+# 1308| Type = [IntType] int
+# 1308| ValueCategory = prvalue
+# 1308| 0: [ParenthesisExpr] (...)
+# 1308| Type = [BoolType] bool
+# 1308| ValueCategory = prvalue
+# 1308| expr: [LogicalOrExpr] ... || ...
+# 1308| Type = [BoolType] bool
+# 1308| ValueCategory = prvalue
+# 1308| 0: [LogicalAndExpr] ... && ...
+# 1308| Type = [BoolType] bool
+# 1308| ValueCategory = prvalue
+# 1308| 0: [CStyleCast] (bool)...
+# 1308| Conversion = [BoolConversion] conversion to bool
+# 1308| Type = [BoolType] bool
+# 1308| ValueCategory = prvalue
+# 1308| expr: [VariableAccess] x
+# 1308| Type = [IntType] int
+# 1308| ValueCategory = prvalue(load)
+# 1308| 1: [VariableAccess] b
+# 1308| Type = [BoolType] bool
+# 1308| ValueCategory = prvalue(load)
+# 1308| 1: [CStyleCast] (bool)...
+# 1308| Conversion = [BoolConversion] conversion to bool
+# 1308| Type = [BoolType] bool
+# 1308| ValueCategory = prvalue
+# 1308| expr: [VariableAccess] y
+# 1308| Type = [LongType] long
+# 1308| ValueCategory = prvalue(load)
+# 1308| 1: [VariableAccess] x
+# 1308| Type = [IntType] int
+# 1308| ValueCategory = prvalue(load)
+# 1309| 8: [ReturnStmt] return ...
+# 1311| [TopLevelFunction] bool predicateA()
+# 1311| params:
+# 1312| [TopLevelFunction] bool predicateB()
+# 1312| params:
+# 1314| [TopLevelFunction] int shortCircuitConditional(int, int)
+# 1314| params:
+# 1314| 0: [Parameter] x
+# 1314| Type = [IntType] int
+# 1314| 1: [Parameter] y
+# 1314| Type = [IntType] int
+# 1314| body: [Block] { ... }
+# 1315| 0: [ReturnStmt] return ...
+# 1315| 0: [ConditionalExpr] ... ? ... : ...
+# 1315| Type = [IntType] int
+# 1315| ValueCategory = prvalue
+# 1315| 0: [LogicalAndExpr] ... && ...
+# 1315| Type = [BoolType] bool
+# 1315| ValueCategory = prvalue
+# 1315| 0: [FunctionCall] call to predicateA
+# 1315| Type = [BoolType] bool
+# 1315| ValueCategory = prvalue
+# 1315| 1: [FunctionCall] call to predicateB
+# 1315| Type = [BoolType] bool
+# 1315| ValueCategory = prvalue
+# 1315| 1: [VariableAccess] x
+# 1315| Type = [IntType] int
+# 1315| ValueCategory = prvalue(load)
+# 1315| 2: [VariableAccess] y
+# 1315| Type = [IntType] int
+# 1315| ValueCategory = prvalue(load)
perf-regression.cpp:
# 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&)
# 4| params:
diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity.expected b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity.expected
index 735555b5a4b..3a1a30265b2 100644
--- a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity.expected
+++ b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity.expected
@@ -13,6 +13,7 @@ instructionWithoutSuccessor
ambiguousSuccessors
unexplainedLoop
unnecessaryPhiInstruction
+memoryOperandDefinitionIsUnmodeled
operandAcrossFunctions
instructionWithoutUniqueBlock
containsLoopOfForwardEdges
diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity_unsound.expected b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity_unsound.expected
index 735555b5a4b..3a1a30265b2 100644
--- a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity_unsound.expected
+++ b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity_unsound.expected
@@ -13,6 +13,7 @@ instructionWithoutSuccessor
ambiguousSuccessors
unexplainedLoop
unnecessaryPhiInstruction
+memoryOperandDefinitionIsUnmodeled
operandAcrossFunctions
instructionWithoutUniqueBlock
containsLoopOfForwardEdges
diff --git a/cpp/ql/test/library-tests/ir/ir/complex.c b/cpp/ql/test/library-tests/ir/ir/complex.c
new file mode 100644
index 00000000000..d2009778ae5
--- /dev/null
+++ b/cpp/ql/test/library-tests/ir/ir/complex.c
@@ -0,0 +1,147 @@
+void complex_literals(void) {
+ _Complex float cf = 2.0;
+ cf = __I__;
+ _Complex double cd = 3.0;
+ cd = __I__;
+ _Complex long double cld = 5.0;
+ cld = __I__;
+
+ _Imaginary float jf = __I__;
+ _Imaginary double jd = __I__;
+ _Imaginary long double jld = __I__;
+}
+
+void complex_arithmetic(void) {
+ float f1 = 5.0;
+ float f2 = 7.0;
+ float f3;
+ _Complex float cf1 = 2.0;
+ _Complex float cf2 = __I__;
+ _Complex float cf3;
+ _Imaginary float jf1 = __I__;
+ _Imaginary float jf2 = __I__;
+ _Imaginary float jf3;
+
+ // unaryop _Complex
+ cf3 = +cf1;
+ cf3 = -cf1;
+
+ // _Complex binaryop _Complex
+ cf3 = cf1 + cf2;
+ cf3 = cf1 - cf2;
+ cf3 = cf1 * cf2;
+ cf3 = cf1 / cf2;
+
+ // unaryop _Imaginary
+ jf3 = +jf1;
+ jf3 = -jf1;
+
+ // _Imaginary binaryop _Imaginary
+ jf3 = jf1 + jf2;
+ jf3 = jf1 - jf2;
+ f3 = jf1 * jf2; // Result is _Real
+ f3 = jf1 / jf2; // Result is _Real
+
+ // _Imaginary binaryop _Real
+ cf3 = jf1 + f2;
+ cf3 = jf1 - f2;
+ jf3 = jf1 * f2; // Result is _Imaginary
+ jf3 = jf1 / f2; // Result is _Imaginary
+
+ // _Real binaryop _Imaginary
+ cf3 = f1 + jf2;
+ cf3 = f1 - jf2;
+ jf3 = f1 * jf2; // Result is _Imaginary
+ jf3 = f1 / jf2; // Result is _Imaginary
+}
+
+void complex_conversions(void) {
+ float f = 2.0;
+ double d = 3.0;
+ long double ld = 5.0;
+ _Complex float cf = 7.0;
+ _Complex double cd = 11.0;
+ _Complex long double cld = 13.0;
+ _Imaginary float jf = __I__;
+ _Imaginary double jd = __I__;
+ _Imaginary long double jld = __I__;
+
+ // _Complex to _Complex
+ cf = cf;
+ cf = cd;
+ cf = cld;
+ cd = cf;
+ cd = cd;
+ cd = cld;
+ cld = cf;
+ cld = cd;
+ cld = cld;
+
+ // _Real to _Complex
+ cf = f;
+ cf = d;
+ cf = ld;
+ cd = f;
+ cd = d;
+ cd = ld;
+ cld = f;
+ cld = d;
+ cld = ld;
+
+ // _Complex to _Real
+ f = cf;
+ f = cd;
+ f = cld;
+ d = cf;
+ d = cd;
+ d = cld;
+ ld = cf;
+ ld = cd;
+ ld = cld;
+
+ // _Imaginary to _Complex
+ cf = jf;
+ cf = jd;
+ cf = jld;
+ cd = jf;
+ cd = jd;
+ cd = jld;
+ cld = jf;
+ cld = jd;
+ cld = jld;
+
+ // _Complex to _Imaginary
+ jf = cf;
+ jf = cd;
+ jf = cld;
+ jd = cf;
+ jd = cd;
+ jd = cld;
+ jld = cf;
+ jld = cd;
+ jld = cld;
+
+ // _Real to _Imaginary
+ jf = f;
+ jf = d;
+ jf = ld;
+ jd = f;
+ jd = d;
+ jd = ld;
+ jld = f;
+ jld = d;
+ jld = ld;
+
+ // _Imaginary to _Real
+ f = jf;
+ f = jd;
+ f = jld;
+ d = jf;
+ d = jd;
+ d = jld;
+ ld = jf;
+ ld = jd;
+ ld = jld;
+}
+
+// semmle-extractor-options: --microsoft --edg --c99
diff --git a/cpp/ql/test/library-tests/ir/ir/ir.cpp b/cpp/ql/test/library-tests/ir/ir/ir.cpp
index 6f6ab081347..5be14bf986c 100644
--- a/cpp/ql/test/library-tests/ir/ir/ir.cpp
+++ b/cpp/ql/test/library-tests/ir/ir/ir.cpp
@@ -1249,10 +1249,70 @@ char *strcpy(char *destination, const char *source);
char *strcat(char *destination, const char *source);
void test_strings(char *s1, char *s2) {
- char buffer[1024] = {0};
+ char buffer[1024] = {0};
- strcpy(buffer, s1);
- strcat(buffer, s2);
+ strcpy(buffer, s1);
+ strcat(buffer, s2);
+}
+
+struct A {
+ int member;
+
+ static void static_member(A* a, int x) {
+ a->member = x;
+ }
+
+ static void static_member_without_def();
+};
+
+A* getAnInstanceOfA();
+
+void test_static_member_functions(int int_arg, A* a_arg) {
+ C c;
+ c.StaticMemberFunction(10);
+ C::StaticMemberFunction(10);
+
+ A a;
+ a.static_member(&a, int_arg);
+ A::static_member(&a, int_arg);
+
+ (&a)->static_member(a_arg, int_arg + 2);
+ (*a_arg).static_member(&a, 99);
+ a_arg->static_member(a_arg, -1);
+
+ a.static_member_without_def();
+ A::static_member_without_def();
+
+ getAnInstanceOfA()->static_member_without_def();
+}
+
+int missingReturnValue(bool b, int x) {
+ if (b) {
+ return x;
+ }
+}
+
+void returnVoid(int x, int y) {
+ return IntegerOps(x, y);
+}
+
+void gccBinaryConditional(bool b, int x, long y) {
+ int z = x;
+ z = b ?: x;
+ z = b ?: y;
+ z = x ?: x;
+ z = x ?: y;
+ z = y ?: x;
+ z = y ?: y;
+
+ z = (x && b || y) ?: x;
+}
+
+bool predicateA();
+bool predicateB();
+
+int shortCircuitConditional(int x, int y) {
+ return predicateA() && predicateB() ? x : y;
}
// semmle-extractor-options: -std=c++17 --clang
diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected
index 28e6de0e1f1..e83fea6badc 100644
--- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected
+++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected
@@ -69,7 +69,7 @@ bad_asts.cpp:
# 26| mu26_4(unknown) = UnmodeledDefinition :
# 26| r26_5(glval) = VariableAddress[a] :
# 26| mu26_6(Point &) = InitializeParameter[a] : &:r26_5
-# 26| r26_7(Point &) = Load : &:r26_5, ~mu26_6
+# 26| r26_7(Point &) = Load : &:r26_5, ~mu26_4
# 26| mu26_8(unknown) = InitializeIndirection[a] : &:r26_7
# 27| r27_1(glval) = VariableAddress[b] :
# 27| r27_2(glval) = VariableAddress[a] :
@@ -79,7 +79,7 @@ bad_asts.cpp:
# 27| r27_6(Point) = Load : &:r27_5, ~mu26_4
# 27| mu27_7(Point) = Store : &:r27_1, r27_6
# 28| v28_1(void) = NoOp :
-# 26| v26_9(void) = ReturnIndirection : &:r26_7, ~mu26_4
+# 26| v26_9(void) = ReturnIndirection[a] : &:r26_7, ~mu26_4
# 26| v26_10(void) = ReturnVoid :
# 26| v26_11(void) = UnmodeledUse : mu*
# 26| v26_12(void) = AliasedUse : ~mu26_4
@@ -124,6 +124,580 @@ clang.cpp:
# 5| v5_8(void) = AliasedUse : ~mu5_4
# 5| v5_9(void) = ExitFunction :
+complex.c:
+# 1| void complex_literals()
+# 1| Block 0
+# 1| v1_1(void) = EnterFunction :
+# 1| mu1_2(unknown) = AliasedDefinition :
+# 1| mu1_3(unknown) = InitializeNonLocal :
+# 1| mu1_4(unknown) = UnmodeledDefinition :
+# 2| r2_1(glval<_Complex float>) = VariableAddress[cf] :
+# 2| r2_2(double) = Constant[2.0] :
+# 2| r2_3(_Complex float) = Convert : r2_2
+# 2| mu2_4(_Complex float) = Store : &:r2_1, r2_3
+# 3| r3_1(_Imaginary float) = Constant[1.0i] :
+# 3| r3_2(_Complex float) = Convert : r3_1
+# 3| r3_3(glval<_Complex float>) = VariableAddress[cf] :
+# 3| mu3_4(_Complex float) = Store : &:r3_3, r3_2
+# 4| r4_1(glval<_Complex double>) = VariableAddress[cd] :
+# 4| r4_2(double) = Constant[3.0] :
+# 4| r4_3(_Complex double) = Convert : r4_2
+# 4| mu4_4(_Complex double) = Store : &:r4_1, r4_3
+# 5| r5_1(_Imaginary float) = Constant[1.0i] :
+# 5| r5_2(_Complex double) = Convert : r5_1
+# 5| r5_3(glval<_Complex double>) = VariableAddress[cd] :
+# 5| mu5_4(_Complex double) = Store : &:r5_3, r5_2
+# 6| r6_1(glval<_Complex long double>) = VariableAddress[cld] :
+# 6| r6_2(double) = Constant[5.0] :
+# 6| r6_3(_Complex long double) = Convert : r6_2
+# 6| mu6_4(_Complex long double) = Store : &:r6_1, r6_3
+# 7| r7_1(_Imaginary float) = Constant[1.0i] :
+# 7| r7_2(_Complex long double) = Convert : r7_1
+# 7| r7_3(glval<_Complex long double>) = VariableAddress[cld] :
+# 7| mu7_4(_Complex long double) = Store : &:r7_3, r7_2
+# 9| r9_1(glval<_Imaginary float>) = VariableAddress[jf] :
+# 9| r9_2(_Imaginary float) = Constant[1.0i] :
+# 9| mu9_3(_Imaginary float) = Store : &:r9_1, r9_2
+# 10| r10_1(glval<_Imaginary double>) = VariableAddress[jd] :
+# 10| r10_2(_Imaginary float) = Constant[1.0i] :
+# 10| r10_3(_Imaginary double) = Convert : r10_2
+# 10| mu10_4(_Imaginary double) = Store : &:r10_1, r10_3
+# 11| r11_1(glval<_Imaginary long double>) = VariableAddress[jld] :
+# 11| r11_2(_Imaginary float) = Constant[1.0i] :
+# 11| r11_3(_Imaginary long double) = Convert : r11_2
+# 11| mu11_4(_Imaginary long double) = Store : &:r11_1, r11_3
+# 12| v12_1(void) = NoOp :
+# 1| v1_5(void) = ReturnVoid :
+# 1| v1_6(void) = UnmodeledUse : mu*
+# 1| v1_7(void) = AliasedUse : ~mu1_4
+# 1| v1_8(void) = ExitFunction :
+
+# 14| void complex_arithmetic()
+# 14| Block 0
+# 14| v14_1(void) = EnterFunction :
+# 14| mu14_2(unknown) = AliasedDefinition :
+# 14| mu14_3(unknown) = InitializeNonLocal :
+# 14| mu14_4(unknown) = UnmodeledDefinition :
+# 15| r15_1(glval) = VariableAddress[f1] :
+# 15| r15_2(float) = Constant[5.0] :
+# 15| mu15_3(float) = Store : &:r15_1, r15_2
+# 16| r16_1(glval) = VariableAddress[f2] :
+# 16| r16_2(float) = Constant[7.0] :
+# 16| mu16_3(float) = Store : &:r16_1, r16_2
+# 17| r17_1(glval) = VariableAddress[f3] :
+# 17| mu17_2(float) = Uninitialized[f3] : &:r17_1
+# 18| r18_1(glval<_Complex float>) = VariableAddress[cf1] :
+# 18| r18_2(double) = Constant[2.0] :
+# 18| r18_3(_Complex float) = Convert : r18_2
+# 18| mu18_4(_Complex float) = Store : &:r18_1, r18_3
+# 19| r19_1(glval<_Complex float>) = VariableAddress[cf2] :
+# 19| r19_2(_Imaginary float) = Constant[1.0i] :
+# 19| r19_3(_Complex float) = Convert : r19_2
+# 19| mu19_4(_Complex float) = Store : &:r19_1, r19_3
+# 20| r20_1(glval<_Complex float>) = VariableAddress[cf3] :
+# 20| mu20_2(_Complex float) = Uninitialized[cf3] : &:r20_1
+# 21| r21_1(glval<_Imaginary float>) = VariableAddress[jf1] :
+# 21| r21_2(_Imaginary float) = Constant[1.0i] :
+# 21| mu21_3(_Imaginary float) = Store : &:r21_1, r21_2
+# 22| r22_1(glval<_Imaginary float>) = VariableAddress[jf2] :
+# 22| r22_2(_Imaginary float) = Constant[1.0i] :
+# 22| mu22_3(_Imaginary float) = Store : &:r22_1, r22_2
+# 23| r23_1(glval<_Imaginary float>) = VariableAddress[jf3] :
+# 23| mu23_2(_Imaginary float) = Uninitialized[jf3] : &:r23_1
+# 26| r26_1(glval<_Complex float>) = VariableAddress[cf1] :
+# 26| r26_2(_Complex float) = Load : &:r26_1, ~mu14_4
+# 26| r26_3(_Complex float) = CopyValue : r26_2
+# 26| r26_4(glval<_Complex float>) = VariableAddress[cf3] :
+# 26| mu26_5(_Complex float) = Store : &:r26_4, r26_3
+# 27| r27_1(glval<_Complex float>) = VariableAddress[cf1] :
+# 27| r27_2(_Complex float) = Load : &:r27_1, ~mu14_4
+# 27| r27_3(_Complex float) = Negate : r27_2
+# 27| r27_4(glval<_Complex float>) = VariableAddress[cf3] :
+# 27| mu27_5(_Complex float) = Store : &:r27_4, r27_3
+# 30| r30_1(glval<_Complex float>) = VariableAddress[cf1] :
+# 30| r30_2(_Complex float) = Load : &:r30_1, ~mu14_4
+# 30| r30_3(glval<_Complex float>) = VariableAddress[cf2] :
+# 30| r30_4(_Complex float) = Load : &:r30_3, ~mu14_4
+# 30| r30_5(_Complex float) = Add : r30_2, r30_4
+# 30| r30_6(glval<_Complex float>) = VariableAddress[cf3] :
+# 30| mu30_7(_Complex float) = Store : &:r30_6, r30_5
+# 31| r31_1(glval<_Complex float>) = VariableAddress[cf1] :
+# 31| r31_2(_Complex float) = Load : &:r31_1, ~mu14_4
+# 31| r31_3(glval<_Complex float>) = VariableAddress[cf2] :
+# 31| r31_4(_Complex float) = Load : &:r31_3, ~mu14_4
+# 31| r31_5(_Complex float) = Sub : r31_2, r31_4
+# 31| r31_6(glval<_Complex float>) = VariableAddress[cf3] :
+# 31| mu31_7(_Complex float) = Store : &:r31_6, r31_5
+# 32| r32_1(glval<_Complex float>) = VariableAddress[cf1] :
+# 32| r32_2(_Complex float) = Load : &:r32_1, ~mu14_4
+# 32| r32_3(glval<_Complex float>) = VariableAddress[cf2] :
+# 32| r32_4(_Complex float) = Load : &:r32_3, ~mu14_4
+# 32| r32_5(_Complex float) = Mul : r32_2, r32_4
+# 32| r32_6(glval<_Complex float>) = VariableAddress[cf3] :
+# 32| mu32_7(_Complex float) = Store : &:r32_6, r32_5
+# 33| r33_1(glval<_Complex float>) = VariableAddress[cf1] :
+# 33| r33_2(_Complex float) = Load : &:r33_1, ~mu14_4
+# 33| r33_3(glval<_Complex float>) = VariableAddress[cf2] :
+# 33| r33_4(_Complex float) = Load : &:r33_3, ~mu14_4
+# 33| r33_5(_Complex float) = Div : r33_2, r33_4
+# 33| r33_6(glval<_Complex float>) = VariableAddress[cf3] :
+# 33| mu33_7(_Complex float) = Store : &:r33_6, r33_5
+# 36| r36_1(glval<_Imaginary float>) = VariableAddress[jf1] :
+# 36| r36_2(_Imaginary float) = Load : &:r36_1, ~mu14_4
+# 36| r36_3(_Imaginary float) = CopyValue : r36_2
+# 36| r36_4(glval<_Imaginary float>) = VariableAddress[jf3] :
+# 36| mu36_5(_Imaginary float) = Store : &:r36_4, r36_3
+# 37| r37_1(glval<_Imaginary float>) = VariableAddress[jf1] :
+# 37| r37_2(_Imaginary float) = Load : &:r37_1, ~mu14_4
+# 37| r37_3(_Imaginary float) = Negate : r37_2
+# 37| r37_4(glval<_Imaginary float>) = VariableAddress[jf3] :
+# 37| mu37_5(_Imaginary float) = Store : &:r37_4, r37_3
+# 40| r40_1(glval<_Imaginary float>) = VariableAddress[jf1] :
+# 40| r40_2(_Imaginary float) = Load : &:r40_1, ~mu14_4
+# 40| r40_3(glval<_Imaginary float>) = VariableAddress[jf2] :
+# 40| r40_4(_Imaginary float) = Load : &:r40_3, ~mu14_4
+# 40| r40_5(_Imaginary float) = Add : r40_2, r40_4
+# 40| r40_6(glval<_Imaginary float>) = VariableAddress[jf3] :
+# 40| mu40_7(_Imaginary float) = Store : &:r40_6, r40_5
+# 41| r41_1(glval<_Imaginary float>) = VariableAddress[jf1] :
+# 41| r41_2(_Imaginary float) = Load : &:r41_1, ~mu14_4
+# 41| r41_3(glval<_Imaginary float>) = VariableAddress[jf2] :
+# 41| r41_4(_Imaginary float) = Load : &:r41_3, ~mu14_4
+# 41| r41_5(_Imaginary float) = Sub : r41_2, r41_4
+# 41| r41_6(glval<_Imaginary float>) = VariableAddress[jf3] :
+# 41| mu41_7(_Imaginary float) = Store : &:r41_6, r41_5
+# 42| r42_1(glval<_Imaginary float>) = VariableAddress[jf1] :
+# 42| r42_2(_Imaginary float) = Load : &:r42_1, ~mu14_4
+# 42| r42_3(glval<_Imaginary float>) = VariableAddress[jf2] :
+# 42| r42_4(_Imaginary float) = Load : &:r42_3, ~mu14_4
+# 42| r42_5(float) = Mul : r42_2, r42_4
+# 42| r42_6(glval) = VariableAddress[f3] :
+# 42| mu42_7(float) = Store : &:r42_6, r42_5
+# 43| r43_1(glval<_Imaginary float>) = VariableAddress[jf1] :
+# 43| r43_2(_Imaginary float) = Load : &:r43_1, ~mu14_4
+# 43| r43_3(glval<_Imaginary float>) = VariableAddress[jf2] :
+# 43| r43_4(_Imaginary float) = Load : &:r43_3, ~mu14_4
+# 43| r43_5(float) = Div : r43_2, r43_4
+# 43| r43_6(glval) = VariableAddress[f3] :
+# 43| mu43_7(float) = Store : &:r43_6, r43_5
+# 46| r46_1(glval<_Imaginary float>) = VariableAddress[jf1] :
+# 46| r46_2(_Imaginary float) = Load : &:r46_1, ~mu14_4
+# 46| r46_3(glval) = VariableAddress[f2] :
+# 46| r46_4(float) = Load : &:r46_3, ~mu14_4
+# 46| r46_5(_Complex float) = Add : r46_2, r46_4
+# 46| r46_6(glval<_Complex float>) = VariableAddress[cf3] :
+# 46| mu46_7(_Complex float) = Store : &:r46_6, r46_5
+# 47| r47_1(glval<_Imaginary float>) = VariableAddress[jf1] :
+# 47| r47_2(_Imaginary float) = Load : &:r47_1, ~mu14_4
+# 47| r47_3(glval) = VariableAddress[f2] :
+# 47| r47_4(float) = Load : &:r47_3, ~mu14_4
+# 47| r47_5(_Complex float) = Sub : r47_2, r47_4
+# 47| r47_6(glval<_Complex float>) = VariableAddress[cf3] :
+# 47| mu47_7(_Complex float) = Store : &:r47_6, r47_5
+# 48| r48_1(glval<_Imaginary float>) = VariableAddress[jf1] :
+# 48| r48_2(_Imaginary float) = Load : &:r48_1, ~mu14_4
+# 48| r48_3(glval) = VariableAddress[f2] :
+# 48| r48_4(float) = Load : &:r48_3, ~mu14_4
+# 48| r48_5(_Imaginary float) = Mul : r48_2, r48_4
+# 48| r48_6(glval<_Imaginary float>) = VariableAddress[jf3] :
+# 48| mu48_7(_Imaginary float) = Store : &:r48_6, r48_5
+# 49| r49_1(glval<_Imaginary float>) = VariableAddress[jf1] :
+# 49| r49_2(_Imaginary float) = Load : &:r49_1, ~mu14_4
+# 49| r49_3(glval) = VariableAddress[f2] :
+# 49| r49_4(float) = Load : &:r49_3, ~mu14_4
+# 49| r49_5(_Imaginary float) = Div : r49_2, r49_4
+# 49| r49_6(glval<_Imaginary float>) = VariableAddress[jf3] :
+# 49| mu49_7(_Imaginary float) = Store : &:r49_6, r49_5
+# 52| r52_1(glval) = VariableAddress[f1] :
+# 52| r52_2(float) = Load : &:r52_1, ~mu14_4
+# 52| r52_3(glval<_Imaginary float>) = VariableAddress[jf2] :
+# 52| r52_4(_Imaginary float) = Load : &:r52_3, ~mu14_4
+# 52| r52_5(_Complex float) = Add : r52_2, r52_4
+# 52| r52_6(glval<_Complex float>) = VariableAddress[cf3] :
+# 52| mu52_7(_Complex float) = Store : &:r52_6, r52_5
+# 53| r53_1(glval) = VariableAddress[f1] :
+# 53| r53_2(float) = Load : &:r53_1, ~mu14_4
+# 53| r53_3(glval<_Imaginary float>) = VariableAddress[jf2] :
+# 53| r53_4(_Imaginary float) = Load : &:r53_3, ~mu14_4
+# 53| r53_5(_Complex float) = Sub : r53_2, r53_4
+# 53| r53_6(glval<_Complex float>) = VariableAddress[cf3] :
+# 53| mu53_7(_Complex float) = Store : &:r53_6, r53_5
+# 54| r54_1(glval) = VariableAddress[f1] :
+# 54| r54_2(float) = Load : &:r54_1, ~mu14_4
+# 54| r54_3(glval<_Imaginary float>) = VariableAddress[jf2] :
+# 54| r54_4(_Imaginary float) = Load : &:r54_3, ~mu14_4
+# 54| r54_5(_Imaginary float) = Mul : r54_2, r54_4
+# 54| r54_6(glval<_Imaginary float>) = VariableAddress[jf3] :
+# 54| mu54_7(_Imaginary float) = Store : &:r54_6, r54_5
+# 55| r55_1(glval) = VariableAddress[f1] :
+# 55| r55_2(float) = Load : &:r55_1, ~mu14_4
+# 55| r55_3(glval<_Imaginary float>) = VariableAddress[jf2] :
+# 55| r55_4(_Imaginary float) = Load : &:r55_3, ~mu14_4
+# 55| r55_5(_Imaginary float) = Div : r55_2, r55_4
+# 55| r55_6(glval<_Imaginary float>) = VariableAddress[jf3] :
+# 55| mu55_7(_Imaginary float) = Store : &:r55_6, r55_5
+# 56| v56_1(void) = NoOp :
+# 14| v14_5(void) = ReturnVoid :
+# 14| v14_6(void) = UnmodeledUse : mu*
+# 14| v14_7(void) = AliasedUse : ~mu14_4
+# 14| v14_8(void) = ExitFunction :
+
+# 58| void complex_conversions()
+# 58| Block 0
+# 58| v58_1(void) = EnterFunction :
+# 58| mu58_2(unknown) = AliasedDefinition :
+# 58| mu58_3(unknown) = InitializeNonLocal :
+# 58| mu58_4(unknown) = UnmodeledDefinition :
+# 59| r59_1(glval) = VariableAddress[f] :
+# 59| r59_2(float) = Constant[2.0] :
+# 59| mu59_3(float) = Store : &:r59_1, r59_2
+# 60| r60_1(glval) = VariableAddress[d] :
+# 60| r60_2(double) = Constant[3.0] :
+# 60| mu60_3(double) = Store : &:r60_1, r60_2
+# 61| r61_1(glval) = VariableAddress[ld] :
+# 61| r61_2(long double) = Constant[5.0] :
+# 61| mu61_3(long double) = Store : &:r61_1, r61_2
+# 62| r62_1(glval<_Complex float>) = VariableAddress[cf] :
+# 62| r62_2(double) = Constant[7.0] :
+# 62| r62_3(_Complex float) = Convert : r62_2
+# 62| mu62_4(_Complex float) = Store : &:r62_1, r62_3
+# 63| r63_1(glval<_Complex double>) = VariableAddress[cd] :
+# 63| r63_2(double) = Constant[11.0] :
+# 63| r63_3(_Complex double) = Convert : r63_2
+# 63| mu63_4(_Complex double) = Store : &:r63_1, r63_3
+# 64| r64_1(glval<_Complex long double>) = VariableAddress[cld] :
+# 64| r64_2(double) = Constant[13.0] :
+# 64| r64_3(_Complex long double) = Convert : r64_2
+# 64| mu64_4(_Complex long double) = Store : &:r64_1, r64_3
+# 65| r65_1(glval<_Imaginary float>) = VariableAddress[jf] :
+# 65| r65_2(_Imaginary float) = Constant[1.0i] :
+# 65| mu65_3(_Imaginary float) = Store : &:r65_1, r65_2
+# 66| r66_1(glval<_Imaginary double>) = VariableAddress[jd] :
+# 66| r66_2(_Imaginary float) = Constant[1.0i] :
+# 66| r66_3(_Imaginary double) = Convert : r66_2
+# 66| mu66_4(_Imaginary double) = Store : &:r66_1, r66_3
+# 67| r67_1(glval<_Imaginary long double>) = VariableAddress[jld] :
+# 67| r67_2(_Imaginary float) = Constant[1.0i] :
+# 67| r67_3(_Imaginary long double) = Convert : r67_2
+# 67| mu67_4(_Imaginary long double) = Store : &:r67_1, r67_3
+# 70| r70_1(glval<_Complex float>) = VariableAddress[cf] :
+# 70| r70_2(_Complex float) = Load : &:r70_1, ~mu58_4
+# 70| r70_3(glval<_Complex float>) = VariableAddress[cf] :
+# 70| mu70_4(_Complex float) = Store : &:r70_3, r70_2
+# 71| r71_1(glval<_Complex double>) = VariableAddress[cd] :
+# 71| r71_2(_Complex double) = Load : &:r71_1, ~mu58_4
+# 71| r71_3(_Complex float) = Convert : r71_2
+# 71| r71_4(glval<_Complex float>) = VariableAddress[cf] :
+# 71| mu71_5(_Complex float) = Store : &:r71_4, r71_3
+# 72| r72_1(glval<_Complex long double>) = VariableAddress[cld] :
+# 72| r72_2(_Complex long double) = Load : &:r72_1, ~mu58_4
+# 72| r72_3(_Complex float) = Convert : r72_2
+# 72| r72_4(glval<_Complex float>) = VariableAddress[cf] :
+# 72| mu72_5(_Complex float) = Store : &:r72_4, r72_3
+# 73| r73_1(glval<_Complex float>) = VariableAddress[cf] :
+# 73| r73_2(_Complex float) = Load : &:r73_1, ~mu58_4
+# 73| r73_3(_Complex double) = Convert : r73_2
+# 73| r73_4(glval<_Complex double>) = VariableAddress[cd] :
+# 73| mu73_5(_Complex double) = Store : &:r73_4, r73_3
+# 74| r74_1(glval<_Complex double>) = VariableAddress[cd] :
+# 74| r74_2(_Complex double) = Load : &:r74_1, ~mu58_4
+# 74| r74_3(glval<_Complex double>) = VariableAddress[cd] :
+# 74| mu74_4(_Complex double) = Store : &:r74_3, r74_2
+# 75| r75_1(glval<_Complex long double>) = VariableAddress[cld] :
+# 75| r75_2(_Complex long double) = Load : &:r75_1, ~mu58_4
+# 75| r75_3(_Complex double) = Convert : r75_2
+# 75| r75_4(glval<_Complex double>) = VariableAddress[cd] :
+# 75| mu75_5(_Complex double) = Store : &:r75_4, r75_3
+# 76| r76_1(glval<_Complex float>) = VariableAddress[cf] :
+# 76| r76_2(_Complex float) = Load : &:r76_1, ~mu58_4
+# 76| r76_3(_Complex long double) = Convert : r76_2
+# 76| r76_4(glval<_Complex long double>) = VariableAddress[cld] :
+# 76| mu76_5(_Complex long double) = Store : &:r76_4, r76_3
+# 77| r77_1(glval<_Complex double>) = VariableAddress[cd] :
+# 77| r77_2(_Complex double) = Load : &:r77_1, ~mu58_4
+# 77| r77_3(_Complex long double) = Convert : r77_2
+# 77| r77_4(glval<_Complex long double>) = VariableAddress[cld] :
+# 77| mu77_5(_Complex long double) = Store : &:r77_4, r77_3
+# 78| r78_1(glval<_Complex long double>) = VariableAddress[cld] :
+# 78| r78_2(_Complex long double) = Load : &:r78_1, ~mu58_4
+# 78| r78_3(glval<_Complex long double>) = VariableAddress[cld] :
+# 78| mu78_4(_Complex long double) = Store : &:r78_3, r78_2
+# 81| r81_1(glval) = VariableAddress[f] :
+# 81| r81_2(float) = Load : &:r81_1, ~mu58_4
+# 81| r81_3(_Complex float) = Convert : r81_2
+# 81| r81_4(glval<_Complex float>) = VariableAddress[cf] :
+# 81| mu81_5(_Complex float) = Store : &:r81_4, r81_3
+# 82| r82_1(glval) = VariableAddress[d] :
+# 82| r82_2(double) = Load : &:r82_1, ~mu58_4
+# 82| r82_3(_Complex float) = Convert : r82_2
+# 82| r82_4(glval<_Complex float>) = VariableAddress[cf] :
+# 82| mu82_5(_Complex float) = Store : &:r82_4, r82_3
+# 83| r83_1(glval) = VariableAddress[ld] :
+# 83| r83_2(long double) = Load : &:r83_1, ~mu58_4
+# 83| r83_3(_Complex float) = Convert : r83_2
+# 83| r83_4(glval<_Complex float>) = VariableAddress[cf] :
+# 83| mu83_5(_Complex float) = Store : &:r83_4, r83_3
+# 84| r84_1(glval) = VariableAddress[f] :
+# 84| r84_2(float) = Load : &:r84_1, ~mu58_4
+# 84| r84_3(_Complex double) = Convert : r84_2
+# 84| r84_4(glval<_Complex double>) = VariableAddress[cd] :
+# 84| mu84_5(_Complex double) = Store : &:r84_4, r84_3
+# 85| r85_1(glval) = VariableAddress[d] :
+# 85| r85_2(double) = Load : &:r85_1, ~mu58_4
+# 85| r85_3(_Complex double) = Convert : r85_2
+# 85| r85_4(glval<_Complex double>) = VariableAddress[cd] :
+# 85| mu85_5(_Complex double) = Store : &:r85_4, r85_3
+# 86| r86_1(glval) = VariableAddress[ld] :
+# 86| r86_2(long double) = Load : &:r86_1, ~mu58_4
+# 86| r86_3(_Complex double) = Convert : r86_2
+# 86| r86_4(glval<_Complex double>) = VariableAddress[cd] :
+# 86| mu86_5(_Complex double) = Store : &:r86_4, r86_3
+# 87| r87_1(glval) = VariableAddress[f] :
+# 87| r87_2(float) = Load : &:r87_1, ~mu58_4
+# 87| r87_3(_Complex long double) = Convert : r87_2
+# 87| r87_4(glval<_Complex long double>) = VariableAddress[cld] :
+# 87| mu87_5(_Complex long double) = Store : &:r87_4, r87_3
+# 88| r88_1(glval) = VariableAddress[d] :
+# 88| r88_2(double) = Load : &:r88_1, ~mu58_4
+# 88| r88_3(_Complex long double) = Convert : r88_2
+# 88| r88_4(glval<_Complex long double>) = VariableAddress[cld] :
+# 88| mu88_5(_Complex long double) = Store : &:r88_4, r88_3
+# 89| r89_1(glval) = VariableAddress[ld] :
+# 89| r89_2(long double) = Load : &:r89_1, ~mu58_4
+# 89| r89_3(_Complex long double) = Convert : r89_2
+# 89| r89_4(glval<_Complex long double>) = VariableAddress[cld] :
+# 89| mu89_5(_Complex long double) = Store : &:r89_4, r89_3
+# 92| r92_1(glval<_Complex float>) = VariableAddress[cf] :
+# 92| r92_2(_Complex float) = Load : &:r92_1, ~mu58_4
+# 92| r92_3(float) = Convert : r92_2
+# 92| r92_4(glval) = VariableAddress[f] :
+# 92| mu92_5(float) = Store : &:r92_4, r92_3
+# 93| r93_1(glval<_Complex double>) = VariableAddress[cd] :
+# 93| r93_2(_Complex double) = Load : &:r93_1, ~mu58_4
+# 93| r93_3(float) = Convert : r93_2
+# 93| r93_4(glval) = VariableAddress[f] :
+# 93| mu93_5(float) = Store : &:r93_4, r93_3
+# 94| r94_1(glval<_Complex long double>) = VariableAddress[cld] :
+# 94| r94_2(_Complex long double) = Load : &:r94_1, ~mu58_4
+# 94| r94_3(float) = Convert : r94_2
+# 94| r94_4(glval) = VariableAddress[f] :
+# 94| mu94_5(float) = Store : &:r94_4, r94_3
+# 95| r95_1(glval<_Complex float>) = VariableAddress[cf] :
+# 95| r95_2(_Complex float) = Load : &:r95_1, ~mu58_4
+# 95| r95_3(double) = Convert : r95_2
+# 95| r95_4(glval) = VariableAddress[d] :
+# 95| mu95_5(double) = Store : &:r95_4, r95_3
+# 96| r96_1(glval<_Complex double>) = VariableAddress[cd] :
+# 96| r96_2(_Complex double) = Load : &:r96_1, ~mu58_4
+# 96| r96_3(double) = Convert : r96_2
+# 96| r96_4(glval) = VariableAddress[d] :
+# 96| mu96_5(double) = Store : &:r96_4, r96_3
+# 97| r97_1(glval<_Complex long double>) = VariableAddress[cld] :
+# 97| r97_2(_Complex long double) = Load : &:r97_1, ~mu58_4
+# 97| r97_3(double) = Convert : r97_2
+# 97| r97_4(glval) = VariableAddress[d] :
+# 97| mu97_5(double) = Store : &:r97_4, r97_3
+# 98| r98_1(glval<_Complex float>) = VariableAddress[cf] :
+# 98| r98_2(_Complex float) = Load : &:r98_1, ~mu58_4
+# 98| r98_3(long double) = Convert : r98_2
+# 98| r98_4(glval) = VariableAddress[ld] :
+# 98| mu98_5(long double) = Store : &:r98_4, r98_3
+# 99| r99_1(glval<_Complex double>) = VariableAddress[cd] :
+# 99| r99_2(_Complex double) = Load : &:r99_1, ~mu58_4
+# 99| r99_3(long double) = Convert : r99_2
+# 99| r99_4(glval) = VariableAddress[ld] :
+# 99| mu99_5(long double) = Store : &:r99_4, r99_3
+# 100| r100_1(glval<_Complex long double>) = VariableAddress[cld] :
+# 100| r100_2(_Complex long double) = Load : &:r100_1, ~mu58_4
+# 100| r100_3(long double) = Convert : r100_2
+# 100| r100_4(glval) = VariableAddress[ld] :
+# 100| mu100_5(long double) = Store : &:r100_4, r100_3
+# 103| r103_1(glval<_Imaginary float>) = VariableAddress[jf] :
+# 103| r103_2(_Imaginary float) = Load : &:r103_1, ~mu58_4
+# 103| r103_3(_Complex float) = Convert : r103_2
+# 103| r103_4(glval<_Complex float>) = VariableAddress[cf] :
+# 103| mu103_5(_Complex float) = Store : &:r103_4, r103_3
+# 104| r104_1(glval<_Imaginary double>) = VariableAddress[jd] :
+# 104| r104_2(_Imaginary double) = Load : &:r104_1, ~mu58_4
+# 104| r104_3(_Complex float) = Convert : r104_2
+# 104| r104_4(glval<_Complex float>) = VariableAddress[cf] :
+# 104| mu104_5(_Complex float) = Store : &:r104_4, r104_3
+# 105| r105_1(glval<_Imaginary long double>) = VariableAddress[jld] :
+# 105| r105_2(_Imaginary long double) = Load : &:r105_1, ~mu58_4
+# 105| r105_3(_Complex float) = Convert : r105_2
+# 105| r105_4(glval<_Complex float>) = VariableAddress[cf] :
+# 105| mu105_5(_Complex float) = Store : &:r105_4, r105_3
+# 106| r106_1(glval<_Imaginary float>) = VariableAddress[jf] :
+# 106| r106_2(_Imaginary float) = Load : &:r106_1, ~mu58_4
+# 106| r106_3(_Complex double) = Convert : r106_2
+# 106| r106_4(glval<_Complex double>) = VariableAddress[cd] :
+# 106| mu106_5(_Complex double) = Store : &:r106_4, r106_3
+# 107| r107_1(glval<_Imaginary double>) = VariableAddress[jd] :
+# 107| r107_2(_Imaginary double) = Load : &:r107_1, ~mu58_4
+# 107| r107_3(_Complex double) = Convert : r107_2
+# 107| r107_4(glval<_Complex double>) = VariableAddress[cd] :
+# 107| mu107_5(_Complex double) = Store : &:r107_4, r107_3
+# 108| r108_1(glval<_Imaginary long double>) = VariableAddress[jld] :
+# 108| r108_2(_Imaginary long double) = Load : &:r108_1, ~mu58_4
+# 108| r108_3(_Complex double) = Convert : r108_2
+# 108| r108_4(glval<_Complex double>) = VariableAddress[cd] :
+# 108| mu108_5(_Complex double) = Store : &:r108_4, r108_3
+# 109| r109_1(glval<_Imaginary float>) = VariableAddress[jf] :
+# 109| r109_2(_Imaginary float) = Load : &:r109_1, ~mu58_4
+# 109| r109_3(_Complex long double) = Convert : r109_2
+# 109| r109_4(glval<_Complex long double>) = VariableAddress[cld] :
+# 109| mu109_5(_Complex long double) = Store : &:r109_4, r109_3
+# 110| r110_1(glval<_Imaginary double>) = VariableAddress[jd] :
+# 110| r110_2(_Imaginary double) = Load : &:r110_1, ~mu58_4
+# 110| r110_3(_Complex long double) = Convert : r110_2
+# 110| r110_4(glval<_Complex long double>) = VariableAddress[cld] :
+# 110| mu110_5(_Complex long double) = Store : &:r110_4, r110_3
+# 111| r111_1(glval<_Imaginary long double>) = VariableAddress[jld] :
+# 111| r111_2(_Imaginary long double) = Load : &:r111_1, ~mu58_4
+# 111| r111_3(_Complex long double) = Convert : r111_2
+# 111| r111_4(glval<_Complex long double>) = VariableAddress[cld] :
+# 111| mu111_5(_Complex long double) = Store : &:r111_4, r111_3
+# 114| r114_1(glval<_Complex float>) = VariableAddress[cf] :
+# 114| r114_2(_Complex float) = Load : &:r114_1, ~mu58_4
+# 114| r114_3(_Imaginary float) = Convert : r114_2
+# 114| r114_4(glval<_Imaginary float>) = VariableAddress[jf] :
+# 114| mu114_5(_Imaginary float) = Store : &:r114_4, r114_3
+# 115| r115_1(glval<_Complex double>) = VariableAddress[cd] :
+# 115| r115_2(_Complex double) = Load : &:r115_1, ~mu58_4
+# 115| r115_3(_Imaginary float) = Convert : r115_2
+# 115| r115_4(glval<_Imaginary float>) = VariableAddress[jf] :
+# 115| mu115_5(_Imaginary float) = Store : &:r115_4, r115_3
+# 116| r116_1(glval<_Complex long double>) = VariableAddress[cld] :
+# 116| r116_2(_Complex long double) = Load : &:r116_1, ~mu58_4
+# 116| r116_3(_Imaginary float) = Convert : r116_2
+# 116| r116_4(glval<_Imaginary float>) = VariableAddress[jf] :
+# 116| mu116_5(_Imaginary float) = Store : &:r116_4, r116_3
+# 117| r117_1(glval<_Complex float>) = VariableAddress[cf] :
+# 117| r117_2(_Complex float) = Load : &:r117_1, ~mu58_4
+# 117| r117_3(_Imaginary double) = Convert : r117_2
+# 117| r117_4(glval<_Imaginary double>) = VariableAddress[jd] :
+# 117| mu117_5(_Imaginary double) = Store : &:r117_4, r117_3
+# 118| r118_1(glval<_Complex double>) = VariableAddress[cd] :
+# 118| r118_2(_Complex double) = Load : &:r118_1, ~mu58_4
+# 118| r118_3(_Imaginary double) = Convert : r118_2
+# 118| r118_4(glval<_Imaginary double>) = VariableAddress[jd] :
+# 118| mu118_5(_Imaginary double) = Store : &:r118_4, r118_3
+# 119| r119_1(glval<_Complex long double>) = VariableAddress[cld] :
+# 119| r119_2(_Complex long double) = Load : &:r119_1, ~mu58_4
+# 119| r119_3(_Imaginary double) = Convert : r119_2
+# 119| r119_4(glval<_Imaginary double>) = VariableAddress[jd] :
+# 119| mu119_5(_Imaginary double) = Store : &:r119_4, r119_3
+# 120| r120_1(glval<_Complex float>) = VariableAddress[cf] :
+# 120| r120_2(_Complex float) = Load : &:r120_1, ~mu58_4
+# 120| r120_3(_Imaginary long double) = Convert : r120_2
+# 120| r120_4(glval<_Imaginary long double>) = VariableAddress[jld] :
+# 120| mu120_5(_Imaginary long double) = Store : &:r120_4, r120_3
+# 121| r121_1(glval<_Complex double>) = VariableAddress[cd] :
+# 121| r121_2(_Complex double) = Load : &:r121_1, ~mu58_4
+# 121| r121_3(_Imaginary long double) = Convert : r121_2
+# 121| r121_4(glval<_Imaginary long double>) = VariableAddress[jld] :
+# 121| mu121_5(_Imaginary long double) = Store : &:r121_4, r121_3
+# 122| r122_1(glval<_Complex long double>) = VariableAddress[cld] :
+# 122| r122_2(_Complex long double) = Load : &:r122_1, ~mu58_4
+# 122| r122_3(_Imaginary long double) = Convert : r122_2
+# 122| r122_4(glval<_Imaginary long double>) = VariableAddress[jld] :
+# 122| mu122_5(_Imaginary long double) = Store : &:r122_4, r122_3
+# 125| r125_1(glval) = VariableAddress[f] :
+# 125| r125_2(float) = Load : &:r125_1, ~mu58_4
+# 125| r125_3(_Imaginary float) = Convert : r125_2
+# 125| r125_4(glval<_Imaginary float>) = VariableAddress[jf] :
+# 125| mu125_5(_Imaginary float) = Store : &:r125_4, r125_3
+# 126| r126_1(glval) = VariableAddress[d] :
+# 126| r126_2(double) = Load : &:r126_1, ~mu58_4
+# 126| r126_3(_Imaginary float) = Convert : r126_2
+# 126| r126_4(glval<_Imaginary float>) = VariableAddress[jf] :
+# 126| mu126_5(_Imaginary float) = Store : &:r126_4, r126_3
+# 127| r127_1(glval) = VariableAddress[ld] :
+# 127| r127_2(long double) = Load : &:r127_1, ~mu58_4
+# 127| r127_3(_Imaginary float) = Convert : r127_2
+# 127| r127_4(glval<_Imaginary float>) = VariableAddress[jf] :
+# 127| mu127_5(_Imaginary float) = Store : &:r127_4, r127_3
+# 128| r128_1(glval) = VariableAddress[f] :
+# 128| r128_2(float) = Load : &:r128_1, ~mu58_4
+# 128| r128_3(_Imaginary double) = Convert : r128_2
+# 128| r128_4(glval<_Imaginary double>) = VariableAddress[jd] :
+# 128| mu128_5(_Imaginary double) = Store : &:r128_4, r128_3
+# 129| r129_1(glval) = VariableAddress[d] :
+# 129| r129_2(double) = Load : &:r129_1, ~mu58_4
+# 129| r129_3(_Imaginary double) = Convert : r129_2
+# 129| r129_4(glval<_Imaginary double>) = VariableAddress[jd] :
+# 129| mu129_5(_Imaginary double) = Store : &:r129_4, r129_3
+# 130| r130_1(glval) = VariableAddress[ld] :
+# 130| r130_2(long double) = Load : &:r130_1, ~mu58_4
+# 130| r130_3(_Imaginary double) = Convert : r130_2
+# 130| r130_4(glval<_Imaginary double>) = VariableAddress[jd] :
+# 130| mu130_5(_Imaginary double) = Store : &:r130_4, r130_3
+# 131| r131_1(glval) = VariableAddress[f] :
+# 131| r131_2(float) = Load : &:r131_1, ~mu58_4
+# 131| r131_3(_Imaginary long double) = Convert : r131_2
+# 131| r131_4(glval<_Imaginary long double>) = VariableAddress[jld] :
+# 131| mu131_5(_Imaginary long double) = Store : &:r131_4, r131_3
+# 132| r132_1(glval) = VariableAddress[d] :
+# 132| r132_2(double) = Load : &:r132_1, ~mu58_4
+# 132| r132_3(_Imaginary long double) = Convert : r132_2
+# 132| r132_4(glval<_Imaginary long double>) = VariableAddress[jld] :
+# 132| mu132_5(_Imaginary long double) = Store : &:r132_4, r132_3
+# 133| r133_1(glval) = VariableAddress[ld] :
+# 133| r133_2(long double) = Load : &:r133_1, ~mu58_4
+# 133| r133_3(_Imaginary long double) = Convert : r133_2
+# 133| r133_4(glval<_Imaginary long double>) = VariableAddress[jld] :
+# 133| mu133_5(_Imaginary long double) = Store : &:r133_4, r133_3
+# 136| r136_1(glval<_Imaginary float>) = VariableAddress[jf] :
+# 136| r136_2(_Imaginary float) = Load : &:r136_1, ~mu58_4
+# 136| r136_3(float) = Convert : r136_2
+# 136| r136_4(glval) = VariableAddress[f] :
+# 136| mu136_5(float) = Store : &:r136_4, r136_3
+# 137| r137_1(glval<_Imaginary double>) = VariableAddress[jd] :
+# 137| r137_2(_Imaginary double) = Load : &:r137_1, ~mu58_4
+# 137| r137_3(float) = Convert : r137_2
+# 137| r137_4(glval) = VariableAddress[f] :
+# 137| mu137_5(float) = Store : &:r137_4, r137_3
+# 138| r138_1(glval<_Imaginary long double>) = VariableAddress[jld] :
+# 138| r138_2(_Imaginary long double) = Load : &:r138_1, ~mu58_4
+# 138| r138_3(float) = Convert : r138_2
+# 138| r138_4(glval) = VariableAddress[f] :
+# 138| mu138_5(float) = Store : &:r138_4, r138_3
+# 139| r139_1(glval<_Imaginary float>) = VariableAddress[jf] :
+# 139| r139_2(_Imaginary float) = Load : &:r139_1, ~mu58_4
+# 139| r139_3(double) = Convert : r139_2
+# 139| r139_4(glval) = VariableAddress[d] :
+# 139| mu139_5(double) = Store : &:r139_4, r139_3
+# 140| r140_1(glval<_Imaginary double>) = VariableAddress[jd] :
+# 140| r140_2(_Imaginary double) = Load : &:r140_1, ~mu58_4
+# 140| r140_3(double) = Convert : r140_2
+# 140| r140_4(glval) = VariableAddress[d] :
+# 140| mu140_5(double) = Store : &:r140_4, r140_3
+# 141| r141_1(glval<_Imaginary long double>) = VariableAddress[jld] :
+# 141| r141_2(_Imaginary long double) = Load : &:r141_1, ~mu58_4
+# 141| r141_3(double) = Convert : r141_2
+# 141| r141_4(glval) = VariableAddress[d] :
+# 141| mu141_5(double) = Store : &:r141_4, r141_3
+# 142| r142_1(glval<_Imaginary float>) = VariableAddress[jf] :
+# 142| r142_2(_Imaginary float) = Load : &:r142_1, ~mu58_4
+# 142| r142_3(long double) = Convert : r142_2
+# 142| r142_4(glval) = VariableAddress[ld] :
+# 142| mu142_5(long double) = Store : &:r142_4, r142_3
+# 143| r143_1(glval<_Imaginary double>) = VariableAddress[jd] :
+# 143| r143_2(_Imaginary double) = Load : &:r143_1, ~mu58_4
+# 143| r143_3(long double) = Convert : r143_2
+# 143| r143_4(glval) = VariableAddress[ld] :
+# 143| mu143_5(long double) = Store : &:r143_4, r143_3
+# 144| r144_1(glval<_Imaginary long double>) = VariableAddress[jld] :
+# 144| r144_2(_Imaginary long double) = Load : &:r144_1, ~mu58_4
+# 144| r144_3(long double) = Convert : r144_2
+# 144| r144_4(glval) = VariableAddress[ld] :
+# 144| mu144_5(long double) = Store : &:r144_4, r144_3
+# 145| v145_1(void) = NoOp :
+# 58| v58_5(void) = ReturnVoid :
+# 58| v58_6(void) = UnmodeledUse : mu*
+# 58| v58_7(void) = AliasedUse : ~mu58_4
+# 58| v58_8(void) = ExitFunction :
+
ir.cpp:
# 1| void Constants()
# 1| Block 0
@@ -769,7 +1343,7 @@ ir.cpp:
# 153| mu153_4(unknown) = UnmodeledDefinition :
# 153| r153_5(glval) = VariableAddress[p] :
# 153| mu153_6(int *) = InitializeParameter[p] : &:r153_5
-# 153| r153_7(int *) = Load : &:r153_5, ~mu153_6
+# 153| r153_7(int *) = Load : &:r153_5, ~mu153_4
# 153| mu153_8(unknown) = InitializeIndirection[p] : &:r153_7
# 153| r153_9(glval) = VariableAddress[i] :
# 153| mu153_10(int) = InitializeParameter[i] : &:r153_9
@@ -836,7 +1410,7 @@ ir.cpp:
# 168| r168_6(glval) = VariableAddress[b] :
# 168| mu168_7(bool) = Store : &:r168_6, r168_5
# 169| v169_1(void) = NoOp :
-# 153| v153_11(void) = ReturnIndirection : &:r153_7, ~mu153_4
+# 153| v153_11(void) = ReturnIndirection[p] : &:r153_7, ~mu153_4
# 153| v153_12(void) = ReturnVoid :
# 153| v153_13(void) = UnmodeledUse : mu*
# 153| v153_14(void) = AliasedUse : ~mu153_4
@@ -850,7 +1424,7 @@ ir.cpp:
# 171| mu171_4(unknown) = UnmodeledDefinition :
# 171| r171_5(glval) = VariableAddress[p] :
# 171| mu171_6(int *) = InitializeParameter[p] : &:r171_5
-# 171| r171_7(int *) = Load : &:r171_5, ~mu171_6
+# 171| r171_7(int *) = Load : &:r171_5, ~mu171_4
# 171| mu171_8(unknown) = InitializeIndirection[p] : &:r171_7
# 171| r171_9(glval) = VariableAddress[i] :
# 171| mu171_10(int) = InitializeParameter[i] : &:r171_9
@@ -923,7 +1497,7 @@ ir.cpp:
# 184| r184_7(glval) = PointerAdd[4] : r184_4, r184_6
# 184| mu184_8(int) = Store : &:r184_7, r184_2
# 185| v185_1(void) = NoOp :
-# 171| v171_11(void) = ReturnIndirection : &:r171_7, ~mu171_4
+# 171| v171_11(void) = ReturnIndirection[p] : &:r171_7, ~mu171_4
# 171| v171_12(void) = ReturnVoid :
# 171| v171_13(void) = UnmodeledUse : mu*
# 171| v171_14(void) = AliasedUse : ~mu171_4
@@ -972,11 +1546,11 @@ ir.cpp:
# 193| mu193_4(unknown) = UnmodeledDefinition :
# 193| r193_5(glval) = VariableAddress[p] :
# 193| mu193_6(int *) = InitializeParameter[p] : &:r193_5
-# 193| r193_7(int *) = Load : &:r193_5, ~mu193_6
+# 193| r193_7(int *) = Load : &:r193_5, ~mu193_4
# 193| mu193_8(unknown) = InitializeIndirection[p] : &:r193_7
# 193| r193_9(glval) = VariableAddress[q] :
# 193| mu193_10(int *) = InitializeParameter[q] : &:r193_9
-# 193| r193_11(int *) = Load : &:r193_9, ~mu193_10
+# 193| r193_11(int *) = Load : &:r193_9, ~mu193_4
# 193| mu193_12(unknown) = InitializeIndirection[q] : &:r193_11
# 194| r194_1(glval) = VariableAddress[b] :
# 194| mu194_2(bool) = Uninitialized[b] : &:r194_1
@@ -1023,8 +1597,8 @@ ir.cpp:
# 201| r201_6(glval) = VariableAddress[b] :
# 201| mu201_7(bool) = Store : &:r201_6, r201_5
# 202| v202_1(void) = NoOp :
-# 193| v193_13(void) = ReturnIndirection : &:r193_7, ~mu193_4
-# 193| v193_14(void) = ReturnIndirection : &:r193_11, ~mu193_4
+# 193| v193_13(void) = ReturnIndirection[p] : &:r193_7, ~mu193_4
+# 193| v193_14(void) = ReturnIndirection[q] : &:r193_11, ~mu193_4
# 193| v193_15(void) = ReturnVoid :
# 193| v193_16(void) = UnmodeledUse : mu*
# 193| v193_17(void) = AliasedUse : ~mu193_4
@@ -1038,7 +1612,7 @@ ir.cpp:
# 204| mu204_4(unknown) = UnmodeledDefinition :
# 204| r204_5(glval) = VariableAddress[p] :
# 204| mu204_6(int *) = InitializeParameter[p] : &:r204_5
-# 204| r204_7(int *) = Load : &:r204_5, ~mu204_6
+# 204| r204_7(int *) = Load : &:r204_5, ~mu204_4
# 204| mu204_8(unknown) = InitializeIndirection[p] : &:r204_7
# 205| r205_1(glval) = VariableAddress[q] :
# 205| mu205_2(int *) = Uninitialized[q] : &:r205_1
@@ -1073,7 +1647,7 @@ ir.cpp:
# 210| r210_7(glval) = VariableAddress[q] :
# 210| mu210_8(int *) = Store : &:r210_7, r210_6
# 211| v211_1(void) = NoOp :
-# 204| v204_9(void) = ReturnIndirection : &:r204_7, ~mu204_4
+# 204| v204_9(void) = ReturnIndirection[p] : &:r204_7, ~mu204_4
# 204| v204_10(void) = ReturnVoid :
# 204| v204_11(void) = UnmodeledUse : mu*
# 204| v204_12(void) = AliasedUse : ~mu204_4
@@ -1672,7 +2246,7 @@ ir.cpp:
# 341| mu341_4(unknown) = UnmodeledDefinition :
# 341| r341_5(glval) = VariableAddress[p] :
# 341| mu341_6(int *) = InitializeParameter[p] : &:r341_5
-# 341| r341_7(int *) = Load : &:r341_5, ~mu341_6
+# 341| r341_7(int *) = Load : &:r341_5, ~mu341_4
# 341| mu341_8(unknown) = InitializeIndirection[p] : &:r341_7
# 342| r342_1(int) = Constant[1] :
# 342| r342_2(glval) = VariableAddress[p] :
@@ -1684,7 +2258,7 @@ ir.cpp:
# 343| r343_3(int *) = Load : &:r343_2, ~mu341_4
# 343| r343_4(int) = Load : &:r343_3, ~mu341_4
# 343| mu343_5(int) = Store : &:r343_1, r343_4
-# 341| v341_9(void) = ReturnIndirection : &:r341_7, ~mu341_4
+# 341| v341_9(void) = ReturnIndirection[p] : &:r341_7, ~mu341_4
# 341| r341_10(glval) = VariableAddress[#return] :
# 341| v341_11(void) = ReturnValue : &:r341_10, ~mu341_4
# 341| v341_12(void) = UnmodeledUse : mu*
@@ -2951,11 +3525,11 @@ ir.cpp:
# 622| mu622_4(unknown) = UnmodeledDefinition :
# 622| r622_5(glval) = VariableAddress[r] :
# 622| mu622_6(String &) = InitializeParameter[r] : &:r622_5
-# 622| r622_7(String &) = Load : &:r622_5, ~mu622_6
+# 622| r622_7(String &) = Load : &:r622_5, ~mu622_4
# 622| mu622_8(unknown) = InitializeIndirection[r] : &:r622_7
# 622| r622_9(glval) = VariableAddress[p] :
# 622| mu622_10(String *) = InitializeParameter[p] : &:r622_9
-# 622| r622_11(String *) = Load : &:r622_9, ~mu622_10
+# 622| r622_11(String *) = Load : &:r622_9, ~mu622_4
# 622| mu622_12(unknown) = InitializeIndirection[p] : &:r622_11
# 622| r622_13(glval) = VariableAddress[s] :
# 622| mu622_14(String) = InitializeParameter[s] : &:r622_13
@@ -2984,13 +3558,34 @@ ir.cpp:
# 625| v625_6(void) = ^BufferReadSideEffect[-1] : &:r625_2, ~mu622_4
# 625| mu625_7(String) = ^IndirectMayWriteSideEffect[-1] : &:r625_2
# 626| v626_1(void) = NoOp :
-# 622| v622_15(void) = ReturnIndirection : &:r622_7, ~mu622_4
-# 622| v622_16(void) = ReturnIndirection : &:r622_11, ~mu622_4
+# 622| v622_15(void) = ReturnIndirection[r] : &:r622_7, ~mu622_4
+# 622| v622_16(void) = ReturnIndirection[p] : &:r622_11, ~mu622_4
# 622| v622_17(void) = ReturnVoid :
# 622| v622_18(void) = UnmodeledUse : mu*
# 622| v622_19(void) = AliasedUse : ~mu622_4
# 622| v622_20(void) = ExitFunction :
+# 628| void C::~C()
+# 628| Block 0
+# 628| v628_1(void) = EnterFunction :
+# 628| mu628_2(unknown) = AliasedDefinition :
+# 628| mu628_3(unknown) = InitializeNonLocal :
+# 628| mu628_4(unknown) = UnmodeledDefinition :
+# 628| r628_5(glval) = InitializeThis :
+#-----| v0_1(void) = NoOp :
+# 628| r628_6(glval) = FieldAddress[m_f] : r628_5
+# 628| r628_7(glval) = FunctionAddress[~String] :
+# 628| v628_8(void) = Call : func:r628_7, this:r628_6
+# 628| mu628_9(unknown) = ^CallSideEffect : ~mu628_4
+# 628| r628_10(glval) = FieldAddress[m_b] : r628_5
+# 628| r628_11(glval) = FunctionAddress[~String] :
+# 628| v628_12(void) = Call : func:r628_11, this:r628_10
+# 628| mu628_13(unknown) = ^CallSideEffect : ~mu628_4
+# 628| v628_14(void) = ReturnVoid :
+# 628| v628_15(void) = UnmodeledUse : mu*
+# 628| v628_16(void) = AliasedUse : ~mu628_4
+# 628| v628_17(void) = ExitFunction :
+
# 630| int C::StaticMemberFunction(int)
# 630| Block 0
# 630| v630_1(void) = EnterFunction :
@@ -3170,14 +3765,14 @@ ir.cpp:
# 675| mu675_4(unknown) = UnmodeledDefinition :
# 675| r675_5(glval) = VariableAddress[r] :
# 675| mu675_6(int &) = InitializeParameter[r] : &:r675_5
-# 675| r675_7(int &) = Load : &:r675_5, ~mu675_6
+# 675| r675_7(int &) = Load : &:r675_5, ~mu675_4
# 675| mu675_8(unknown) = InitializeIndirection[r] : &:r675_7
# 676| r676_1(glval) = VariableAddress[#return] :
# 676| r676_2(glval) = VariableAddress[r] :
# 676| r676_3(int &) = Load : &:r676_2, ~mu675_4
# 676| r676_4(int) = Load : &:r676_3, ~mu675_4
# 676| mu676_5(int) = Store : &:r676_1, r676_4
-# 675| v675_9(void) = ReturnIndirection : &:r675_7, ~mu675_4
+# 675| v675_9(void) = ReturnIndirection[r] : &:r675_7, ~mu675_4
# 675| r675_10(glval) = VariableAddress[#return] :
# 675| v675_11(void) = ReturnValue : &:r675_10, ~mu675_4
# 675| v675_12(void) = UnmodeledUse : mu*
@@ -3363,14 +3958,14 @@ ir.cpp:
# 715| mu715_4(unknown) = UnmodeledDefinition :
# 715| r715_5(glval) = VariableAddress[x] :
# 715| mu715_6(void *) = InitializeParameter[x] : &:r715_5
-# 715| r715_7(void *) = Load : &:r715_5, ~mu715_6
+# 715| r715_7(void *) = Load : &:r715_5, ~mu715_4
# 715| mu715_8(unknown) = InitializeIndirection[x] : &:r715_7
# 715| r715_9(glval) = VariableAddress[y] :
# 715| mu715_10(char) = InitializeParameter[y] : &:r715_9
# 716| r716_1(glval) = VariableAddress[#return] :
# 716| r716_2(long) = Constant[0] :
# 716| mu716_3(long) = Store : &:r716_1, r716_2
-# 715| v715_11(void) = ReturnIndirection : &:r715_7, ~mu715_4
+# 715| v715_11(void) = ReturnIndirection[x] : &:r715_7, ~mu715_4
# 715| r715_12(glval) = VariableAddress[#return] :
# 715| v715_13(void) = ReturnValue : &:r715_12, ~mu715_4
# 715| v715_14(void) = UnmodeledUse : mu*
@@ -3487,7 +4082,7 @@ ir.cpp:
# 735| Block 10
# 735| r735_2(glval) = VariableAddress[s] :
# 735| mu735_3(char *) = InitializeParameter[s] : &:r735_2
-# 735| r735_4(char *) = Load : &:r735_2, ~mu735_3
+# 735| r735_4(char *) = Load : &:r735_2, ~mu724_4
# 735| mu735_5(unknown) = InitializeIndirection[s] : &:r735_4
# 736| r736_1(glval) = VariableAddress[#throw736:5] :
# 736| mu736_2(String) = Uninitialized[#throw736:5] : &:r736_1
@@ -3510,7 +4105,7 @@ ir.cpp:
# 738| Block 12
# 738| r738_2(glval) = VariableAddress[e] :
# 738| mu738_3(String &) = InitializeParameter[e] : &:r738_2
-# 738| r738_4(String &) = Load : &:r738_2, ~mu738_3
+# 738| r738_4(String &) = Load : &:r738_2, ~mu724_4
# 738| mu738_5(unknown) = InitializeIndirection[e] : &:r738_4
# 738| v738_6(void) = NoOp :
#-----| Goto -> Block 14
@@ -3534,7 +4129,7 @@ ir.cpp:
# 745| r745_5(glval) = InitializeThis :
#-----| r0_1(glval) = VariableAddress[p#0] :
#-----| mu0_2(Base &) = InitializeParameter[p#0] : &:r0_1
-#-----| r0_3(Base &) = Load : &:r0_1, ~mu0_2
+#-----| r0_3(Base &) = Load : &:r0_1, ~mu745_4
#-----| mu0_4(unknown) = InitializeIndirection[p#0] : &:r0_3
#-----| r0_5(Base *) = CopyValue : r745_5
#-----| r0_6(glval) = FieldAddress[base_s] : r0_5
@@ -3557,7 +4152,7 @@ ir.cpp:
#-----| r0_20(glval) = CopyValue : r0_19
#-----| r0_21(Base &) = CopyValue : r0_20
#-----| mu0_22(Base &) = Store : &:r0_18, r0_21
-#-----| v0_23(void) = ReturnIndirection : &:r0_3, ~mu745_4
+#-----| v0_23(void) = ReturnIndirection[p#0] : &:r0_3, ~mu745_4
# 745| r745_9(glval) = VariableAddress[#return] :
# 745| v745_10(void) = ReturnValue : &:r745_9, ~mu745_4
# 745| v745_11(void) = UnmodeledUse : mu*
@@ -3573,7 +4168,7 @@ ir.cpp:
# 745| r745_5(glval) = InitializeThis :
#-----| r0_1(glval) = VariableAddress[p#0] :
#-----| mu0_2(Base &) = InitializeParameter[p#0] : &:r0_1
-#-----| r0_3(Base &) = Load : &:r0_1, ~mu0_2
+#-----| r0_3(Base &) = Load : &:r0_1, ~mu745_4
#-----| mu0_4(unknown) = InitializeIndirection[p#0] : &:r0_3
# 745| r745_6(glval) = FieldAddress[base_s] : r745_5
# 745| r745_7(glval) = FunctionAddress[String] :
@@ -3581,7 +4176,7 @@ ir.cpp:
# 745| mu745_9(unknown) = ^CallSideEffect : ~mu745_4
# 745| mu745_10(String) = ^IndirectMayWriteSideEffect[-1] : &:r745_6
# 745| v745_11(void) = NoOp :
-#-----| v0_5(void) = ReturnIndirection : &:r0_3, ~mu745_4
+#-----| v0_5(void) = ReturnIndirection[p#0] : &:r0_3, ~mu745_4
# 745| v745_12(void) = ReturnVoid :
# 745| v745_13(void) = UnmodeledUse : mu*
# 745| v745_14(void) = AliasedUse : ~mu745_4
@@ -3631,7 +4226,7 @@ ir.cpp:
# 754| r754_5(glval) = InitializeThis :
#-----| r0_1(glval) = VariableAddress[p#0] :
#-----| mu0_2(Middle &) = InitializeParameter[p#0] : &:r0_1
-#-----| r0_3(Middle &) = Load : &:r0_1, ~mu0_2
+#-----| r0_3(Middle &) = Load : &:r0_1, ~mu754_4
#-----| mu0_4(unknown) = InitializeIndirection[p#0] : &:r0_3
#-----| r0_5(Middle *) = CopyValue : r754_5
#-----| r0_6(Base *) = ConvertToNonVirtualBase[Middle : Base] : r0_5
@@ -3671,7 +4266,7 @@ ir.cpp:
#-----| r0_34(glval) = CopyValue : r0_33
#-----| r0_35(Middle &) = CopyValue : r0_34
#-----| mu0_36(Middle &) = Store : &:r0_32, r0_35
-#-----| v0_37(void) = ReturnIndirection : &:r0_3, ~mu754_4
+#-----| v0_37(void) = ReturnIndirection[p#0] : &:r0_3, ~mu754_4
# 754| r754_12(glval) = VariableAddress[#return] :
# 754| v754_13(void) = ReturnValue : &:r754_12, ~mu754_4
# 754| v754_14(void) = UnmodeledUse : mu*
@@ -3731,7 +4326,7 @@ ir.cpp:
# 763| r763_5(glval) = InitializeThis :
#-----| r0_1(glval) = VariableAddress[p#0] :
#-----| mu0_2(Derived &) = InitializeParameter[p#0] : &:r0_1
-#-----| r0_3(Derived &) = Load : &:r0_1, ~mu0_2
+#-----| r0_3(Derived &) = Load : &:r0_1, ~mu763_4
#-----| mu0_4(unknown) = InitializeIndirection[p#0] : &:r0_3
#-----| r0_5(Derived *) = CopyValue : r763_5
#-----| r0_6(Middle *) = ConvertToNonVirtualBase[Derived : Middle] : r0_5
@@ -3771,7 +4366,7 @@ ir.cpp:
#-----| r0_34(glval) = CopyValue : r0_33
#-----| r0_35(Derived &) = CopyValue : r0_34
#-----| mu0_36(Derived &) = Store : &:r0_32, r0_35
-#-----| v0_37(void) = ReturnIndirection : &:r0_3, ~mu763_4
+#-----| v0_37(void) = ReturnIndirection[p#0] : &:r0_3, ~mu763_4
# 763| r763_12(glval) = VariableAddress[#return] :
# 763| v763_13(void) = ReturnValue : &:r763_12, ~mu763_4
# 763| v763_14(void) = UnmodeledUse : mu*
@@ -4462,7 +5057,7 @@ ir.cpp:
# 883| mu883_6(..(*)(..)) = InitializeParameter[pfn] : &:r883_5
# 883| r883_7(glval) = VariableAddress[p] :
# 883| mu883_8(void *) = InitializeParameter[p] : &:r883_7
-# 883| r883_9(void *) = Load : &:r883_7, ~mu883_8
+# 883| r883_9(void *) = Load : &:r883_7, ~mu883_4
# 883| mu883_10(unknown) = InitializeIndirection[p] : &:r883_9
# 884| r884_1(glval<..(*)(..)>) = VariableAddress[pfn] :
# 884| r884_2(..(*)(..)) = Load : &:r884_1, ~mu883_4
@@ -4475,7 +5070,7 @@ ir.cpp:
# 885| r885_4(glval<..(*)(..)>) = VariableAddress[pfn] :
# 885| mu885_5(..(*)(..)) = Store : &:r885_4, r885_3
# 886| v886_1(void) = NoOp :
-# 883| v883_11(void) = ReturnIndirection : &:r883_9, ~mu883_4
+# 883| v883_11(void) = ReturnIndirection[p] : &:r883_9, ~mu883_4
# 883| v883_12(void) = ReturnVoid :
# 883| v883_13(void) = UnmodeledUse : mu*
# 883| v883_14(void) = AliasedUse : ~mu883_4
@@ -4491,7 +5086,7 @@ ir.cpp:
# 888| mu888_6(int) = InitializeParameter[x] : &:r888_5
# 888| r888_7(glval<__va_list_tag *>) = VariableAddress[args] :
# 888| mu888_8(__va_list_tag *) = InitializeParameter[args] : &:r888_7
-# 888| r888_9(__va_list_tag *) = Load : &:r888_7, ~mu888_8
+# 888| r888_9(__va_list_tag *) = Load : &:r888_7, ~mu888_4
# 888| mu888_10(unknown) = InitializeIndirection[args] : &:r888_9
# 889| r889_1(glval<__va_list_tag[1]>) = VariableAddress[args2] :
# 889| mu889_2(__va_list_tag[1]) = Uninitialized[args2] : &:r889_1
@@ -4524,7 +5119,7 @@ ir.cpp:
# 893| r893_2(__va_list_tag *) = Convert : r893_1
# 893| v893_3(void) = VarArgsEnd : r893_2
# 894| v894_1(void) = NoOp :
-# 888| v888_11(void) = ReturnIndirection : &:r888_9, ~mu888_4
+# 888| v888_11(void) = ReturnIndirection[args] : &:r888_9, ~mu888_4
# 888| v888_12(void) = ReturnVoid :
# 888| v888_13(void) = UnmodeledUse : mu*
# 888| v888_14(void) = AliasedUse : ~mu888_4
@@ -4540,7 +5135,7 @@ ir.cpp:
# 896| mu896_6(int) = InitializeParameter[x] : &:r896_5
# 896| r896_7(glval) = VariableAddress[#ellipsis] :
# 896| mu896_8(unknown[11]) = InitializeParameter[#ellipsis] : &:r896_7
-# 896| r896_9(unknown[11]) = Load : &:r896_7, ~mu896_8
+# 896| r896_9(unknown[11]) = Load : &:r896_7, ~mu896_4
# 896| mu896_10(unknown) = InitializeIndirection[#ellipsis] : &:r896_9
# 897| r897_1(glval<__va_list_tag[1]>) = VariableAddress[args] :
# 897| mu897_2(__va_list_tag[1]) = Uninitialized[args] : &:r897_1
@@ -5027,7 +5622,7 @@ ir.cpp:
# 996| mu996_4(unknown) = UnmodeledDefinition :
# 996| r996_5(glval) = VariableAddress[a] :
# 996| mu996_6(int *) = InitializeParameter[a] : &:r996_5
-# 996| r996_7(int *) = Load : &:r996_5, ~mu996_6
+# 996| r996_7(int *) = Load : &:r996_5, ~mu996_4
# 996| mu996_8(unknown) = InitializeIndirection[a] : &:r996_7
# 996| r996_9(glval<..(*)(..)>) = VariableAddress[fn] :
# 996| mu996_10(..(*)(..)) = InitializeParameter[fn] : &:r996_9
@@ -5044,7 +5639,7 @@ ir.cpp:
# 997| mu997_11(unknown) = ^CallSideEffect : ~mu996_4
# 997| r997_12(int) = Add : r997_6, r997_10
# 997| mu997_13(int) = Store : &:r997_1, r997_12
-# 996| v996_11(void) = ReturnIndirection : &:r996_7, ~mu996_4
+# 996| v996_11(void) = ReturnIndirection[a] : &:r996_7, ~mu996_4
# 996| r996_12(glval) = VariableAddress[#return] :
# 996| v996_13(void) = ReturnValue : &:r996_12, ~mu996_4
# 996| v996_14(void) = UnmodeledUse : mu*
@@ -5201,7 +5796,7 @@ ir.cpp:
# 1040| mu1040_6(int) = InitializeParameter[x] : &:r1040_5
# 1040| r1040_7(glval) = VariableAddress[s] :
# 1040| mu1040_8(String &) = InitializeParameter[s] : &:r1040_7
-# 1040| r1040_9(String &) = Load : &:r1040_7, ~mu1040_8
+# 1040| r1040_9(String &) = Load : &:r1040_7, ~mu1040_4
# 1040| mu1040_10(unknown) = InitializeIndirection[s] : &:r1040_9
# 1041| r1041_1(glval) = VariableAddress[lambda_empty] :
# 1041| r1041_2(glval) = VariableAddress[#temp1041:23] :
@@ -5354,7 +5949,7 @@ ir.cpp:
# 1055| v1055_7(void) = ^BufferReadSideEffect[-1] : &:r1055_2, ~mu1040_4
# 1055| mu1055_8(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r1055_2
# 1056| v1056_1(void) = NoOp :
-# 1040| v1040_11(void) = ReturnIndirection : &:r1040_9, ~mu1040_4
+# 1040| v1040_11(void) = ReturnIndirection[s] : &:r1040_9, ~mu1040_4
# 1040| v1040_12(void) = ReturnVoid :
# 1040| v1040_13(void) = UnmodeledUse : mu*
# 1040| v1040_14(void) = AliasedUse : ~mu1040_4
@@ -5624,7 +6219,7 @@ ir.cpp:
# 1077| mu1077_4(unknown) = UnmodeledDefinition :
# 1077| r1077_5(glval &>) = VariableAddress[v] :
# 1077| mu1077_6(vector &) = InitializeParameter[v] : &:r1077_5
-# 1077| r1077_7(vector &) = Load : &:r1077_5, ~mu1077_6
+# 1077| r1077_7(vector &) = Load : &:r1077_5, ~mu1077_4
# 1077| mu1077_8(unknown) = InitializeIndirection[v] : &:r1077_7
# 1078| r1078_1(glval &>) = VariableAddress[(__range)] :
# 1078| r1078_2(glval &>) = VariableAddress[v] :
@@ -5705,13 +6300,13 @@ ir.cpp:
#-----| Goto -> Block 5
# 1088| Block 5
-# 1088| v1088_1(void) = NoOp :
-# 1089| v1089_1(void) = NoOp :
-# 1077| v1077_9(void) = ReturnIndirection : &:r1077_7, ~mu1077_4
-# 1077| v1077_10(void) = ReturnVoid :
-# 1077| v1077_11(void) = UnmodeledUse : mu*
-# 1077| v1077_12(void) = AliasedUse : ~mu1077_4
-# 1077| v1077_13(void) = ExitFunction :
+# 1088| v1088_1(void) = NoOp :
+# 1089| v1089_1(void) = NoOp :
+# 1077| v1077_9(void) = ReturnIndirection[v] : &:r1077_7, ~mu1077_4
+# 1077| v1077_10(void) = ReturnVoid :
+# 1077| v1077_11(void) = UnmodeledUse : mu*
+# 1077| v1077_12(void) = AliasedUse : ~mu1077_4
+# 1077| v1077_13(void) = ExitFunction :
#-----| Block 6
#-----| r0_24(glval) = VariableAddress[(__begin)] :
@@ -5817,13 +6412,13 @@ ir.cpp:
# 1113| mu1113_4(unknown) = UnmodeledDefinition :
# 1113| r1113_5(glval) = VariableAddress[a] :
# 1113| mu1113_6(unsigned int &) = InitializeParameter[a] : &:r1113_5
-# 1113| r1113_7(unsigned int &) = Load : &:r1113_5, ~mu1113_6
+# 1113| r1113_7(unsigned int &) = Load : &:r1113_5, ~mu1113_4
# 1113| mu1113_8(unknown) = InitializeIndirection[a] : &:r1113_7
# 1113| r1113_9(glval) = VariableAddress[b] :
# 1113| mu1113_10(unsigned int) = InitializeParameter[b] : &:r1113_9
# 1113| r1113_11(glval) = VariableAddress[c] :
# 1113| mu1113_12(unsigned int &) = InitializeParameter[c] : &:r1113_11
-# 1113| r1113_13(unsigned int &) = Load : &:r1113_11, ~mu1113_12
+# 1113| r1113_13(unsigned int &) = Load : &:r1113_11, ~mu1113_4
# 1113| mu1113_14(unknown) = InitializeIndirection[c] : &:r1113_13
# 1113| r1113_15(glval) = VariableAddress[d] :
# 1113| mu1113_16(unsigned int) = InitializeParameter[d] : &:r1113_15
@@ -5838,8 +6433,8 @@ ir.cpp:
# 1118| r1118_9(unsigned int) = Load : &:r1118_8, ~mu1113_4
# 1115| mu1115_1(unknown) = InlineAsm : ~mu1113_4, 0:r1118_3, 1:r1118_4, 2:r1118_7, 3:r1118_9
# 1120| v1120_1(void) = NoOp :
-# 1113| v1113_17(void) = ReturnIndirection : &:r1113_7, ~mu1113_4
-# 1113| v1113_18(void) = ReturnIndirection : &:r1113_13, ~mu1113_4
+# 1113| v1113_17(void) = ReturnIndirection[a] : &:r1113_7, ~mu1113_4
+# 1113| v1113_18(void) = ReturnIndirection[c] : &:r1113_13, ~mu1113_4
# 1113| v1113_19(void) = ReturnVoid :
# 1113| v1113_20(void) = UnmodeledUse : mu*
# 1113| v1113_21(void) = AliasedUse : ~mu1113_4
@@ -5987,7 +6582,7 @@ ir.cpp:
# 1153| Block 10
# 1153| r1153_2(glval) = VariableAddress[s] :
# 1153| mu1153_3(char *) = InitializeParameter[s] : &:r1153_2
-# 1153| r1153_4(char *) = Load : &:r1153_2, ~mu1153_3
+# 1153| r1153_4(char *) = Load : &:r1153_2, ~mu1142_4
# 1153| mu1153_5(unknown) = InitializeIndirection[s] : &:r1153_4
# 1154| r1154_1(glval) = VariableAddress[#throw1154:5] :
# 1154| mu1154_2(String) = Uninitialized[#throw1154:5] : &:r1154_1
@@ -6010,7 +6605,7 @@ ir.cpp:
# 1156| Block 12
# 1156| r1156_2(glval) = VariableAddress[e] :
# 1156| mu1156_3(String &) = InitializeParameter[e] : &:r1156_2
-# 1156| r1156_4(String &) = Load : &:r1156_2, ~mu1156_3
+# 1156| r1156_4(String &) = Load : &:r1156_2, ~mu1142_4
# 1156| mu1156_5(unknown) = InitializeIndirection[e] : &:r1156_4
# 1156| v1156_6(void) = NoOp :
#-----| Goto -> Block 13
@@ -6364,7 +6959,7 @@ ir.cpp:
# 1240| mu1240_4(unknown) = UnmodeledDefinition :
# 1240| r1240_5(glval) = VariableAddress[dynamic] :
# 1240| mu1240_6(char *) = InitializeParameter[dynamic] : &:r1240_5
-# 1240| r1240_7(char *) = Load : &:r1240_5, ~mu1240_6
+# 1240| r1240_7(char *) = Load : &:r1240_5, ~mu1240_4
# 1240| mu1240_8(unknown) = InitializeIndirection[dynamic] : &:r1240_7
# 1241| r1241_1(glval) = VariableAddress[a#init] :
# 1241| r1241_2(bool) = Load : &:r1241_1, ~mu1240_4
@@ -6415,12 +7010,12 @@ ir.cpp:
#-----| Goto -> Block 5
# 1244| Block 5
-# 1244| v1244_1(void) = NoOp :
-# 1240| v1240_9(void) = ReturnIndirection : &:r1240_7, ~mu1240_4
-# 1240| v1240_10(void) = ReturnVoid :
-# 1240| v1240_11(void) = UnmodeledUse : mu*
-# 1240| v1240_12(void) = AliasedUse : ~mu1240_4
-# 1240| v1240_13(void) = ExitFunction :
+# 1244| v1244_1(void) = NoOp :
+# 1240| v1240_9(void) = ReturnIndirection[dynamic] : &:r1240_7, ~mu1240_4
+# 1240| v1240_10(void) = ReturnVoid :
+# 1240| v1240_11(void) = UnmodeledUse : mu*
+# 1240| v1240_12(void) = AliasedUse : ~mu1240_4
+# 1240| v1240_13(void) = ExitFunction :
# 1241| Block 6
# 1241| r1241_4(glval) = VariableAddress[a] :
@@ -6440,11 +7035,11 @@ ir.cpp:
# 1251| mu1251_4(unknown) = UnmodeledDefinition :
# 1251| r1251_5(glval) = VariableAddress[s1] :
# 1251| mu1251_6(char *) = InitializeParameter[s1] : &:r1251_5
-# 1251| r1251_7(char *) = Load : &:r1251_5, ~mu1251_6
+# 1251| r1251_7(char *) = Load : &:r1251_5, ~mu1251_4
# 1251| mu1251_8(unknown) = InitializeIndirection[s1] : &:r1251_7
# 1251| r1251_9(glval) = VariableAddress[s2] :
# 1251| mu1251_10(char *) = InitializeParameter[s2] : &:r1251_9
-# 1251| r1251_11(char *) = Load : &:r1251_9, ~mu1251_10
+# 1251| r1251_11(char *) = Load : &:r1251_9, ~mu1251_4
# 1251| mu1251_12(unknown) = InitializeIndirection[s2] : &:r1251_11
# 1252| r1252_1(glval) = VariableAddress[buffer] :
# 1252| mu1252_2(char[1024]) = Uninitialized[buffer] : &:r1252_1
@@ -6476,13 +7071,476 @@ ir.cpp:
# 1255| v1255_9(void) = ^BufferReadSideEffect[1] : &:r1255_6, ~mu1251_4
# 1255| mu1255_10(unknown) = ^BufferMayWriteSideEffect[0] : &:r1255_3
# 1256| v1256_1(void) = NoOp :
-# 1251| v1251_13(void) = ReturnIndirection : &:r1251_7, ~mu1251_4
-# 1251| v1251_14(void) = ReturnIndirection : &:r1251_11, ~mu1251_4
+# 1251| v1251_13(void) = ReturnIndirection[s1] : &:r1251_7, ~mu1251_4
+# 1251| v1251_14(void) = ReturnIndirection[s2] : &:r1251_11, ~mu1251_4
# 1251| v1251_15(void) = ReturnVoid :
# 1251| v1251_16(void) = UnmodeledUse : mu*
# 1251| v1251_17(void) = AliasedUse : ~mu1251_4
# 1251| v1251_18(void) = ExitFunction :
+# 1261| void A::static_member(A*, int)
+# 1261| Block 0
+# 1261| v1261_1(void) = EnterFunction :
+# 1261| mu1261_2(unknown) = AliasedDefinition :
+# 1261| mu1261_3(unknown) = InitializeNonLocal :
+# 1261| mu1261_4(unknown) = UnmodeledDefinition :
+# 1261| r1261_5(glval) = VariableAddress[a] :
+# 1261| mu1261_6(A *) = InitializeParameter[a] : &:r1261_5
+# 1261| r1261_7(A *) = Load : &:r1261_5, ~mu1261_4
+# 1261| mu1261_8(unknown) = InitializeIndirection[a] : &:r1261_7
+# 1261| r1261_9(glval) = VariableAddress[x] :
+# 1261| mu1261_10(int) = InitializeParameter[x] : &:r1261_9
+# 1262| r1262_1(glval) = VariableAddress[x] :
+# 1262| r1262_2(int) = Load : &:r1262_1, ~mu1261_4
+# 1262| r1262_3(glval) = VariableAddress[a] :
+# 1262| r1262_4(A *) = Load : &:r1262_3, ~mu1261_4
+# 1262| r1262_5(glval) = FieldAddress[member] : r1262_4
+# 1262| mu1262_6(int) = Store : &:r1262_5, r1262_2
+# 1263| v1263_1(void) = NoOp :
+# 1261| v1261_11(void) = ReturnIndirection[a] : &:r1261_7, ~mu1261_4
+# 1261| v1261_12(void) = ReturnVoid :
+# 1261| v1261_13(void) = UnmodeledUse : mu*
+# 1261| v1261_14(void) = AliasedUse : ~mu1261_4
+# 1261| v1261_15(void) = ExitFunction :
+
+# 1270| void test_static_member_functions(int, A*)
+# 1270| Block 0
+# 1270| v1270_1(void) = EnterFunction :
+# 1270| mu1270_2(unknown) = AliasedDefinition :
+# 1270| mu1270_3(unknown) = InitializeNonLocal :
+# 1270| mu1270_4(unknown) = UnmodeledDefinition :
+# 1270| r1270_5(glval) = VariableAddress[int_arg] :
+# 1270| mu1270_6(int) = InitializeParameter[int_arg] : &:r1270_5
+# 1270| r1270_7(glval) = VariableAddress[a_arg] :
+# 1270| mu1270_8(A *) = InitializeParameter[a_arg] : &:r1270_7
+# 1270| r1270_9(A *) = Load : &:r1270_7, ~mu1270_4
+# 1270| mu1270_10(unknown) = InitializeIndirection[a_arg] : &:r1270_9
+# 1271| r1271_1(glval) = VariableAddress[c] :
+# 1271| mu1271_2(C) = Uninitialized[c] : &:r1271_1
+# 1271| r1271_3(glval) = FunctionAddress[C] :
+# 1271| v1271_4(void) = Call : func:r1271_3, this:r1271_1
+# 1271| mu1271_5(unknown) = ^CallSideEffect : ~mu1270_4
+# 1271| mu1271_6(C) = ^IndirectMayWriteSideEffect[-1] : &:r1271_1
+# 1272| r1272_1(glval) = VariableAddress[c] :
+# 1272| r1272_2(glval) = FunctionAddress[StaticMemberFunction] :
+# 1272| r1272_3(int) = Constant[10] :
+# 1272| r1272_4(int) = Call : func:r1272_2, 0:r1272_3
+# 1272| mu1272_5(unknown) = ^CallSideEffect : ~mu1270_4
+# 1273| r1273_1(glval) = FunctionAddress[StaticMemberFunction] :
+# 1273| r1273_2(int) = Constant[10] :
+# 1273| r1273_3(int) = Call : func:r1273_1, 0:r1273_2
+# 1273| mu1273_4(unknown) = ^CallSideEffect : ~mu1270_4
+# 1275| r1275_1(glval) = VariableAddress[a] :
+# 1275| mu1275_2(A) = Uninitialized[a] : &:r1275_1
+# 1276| r1276_1(glval) = VariableAddress[a] :
+# 1276| r1276_2(glval) = FunctionAddress[static_member] :
+# 1276| r1276_3(glval) = VariableAddress[a] :
+# 1276| r1276_4(A *) = CopyValue : r1276_3
+# 1276| r1276_5(glval) = VariableAddress[int_arg] :
+# 1276| r1276_6(int) = Load : &:r1276_5, ~mu1270_4
+# 1276| v1276_7(void) = Call : func:r1276_2, 0:r1276_4, 1:r1276_6
+# 1276| mu1276_8(unknown) = ^CallSideEffect : ~mu1270_4
+# 1276| v1276_9(void) = ^BufferReadSideEffect[0] : &:r1276_4, ~mu1270_4
+# 1276| mu1276_10(unknown) = ^BufferMayWriteSideEffect[0] : &:r1276_4
+# 1277| r1277_1(glval) = FunctionAddress[static_member] :
+# 1277| r1277_2(glval) = VariableAddress[a] :
+# 1277| r1277_3(A *) = CopyValue : r1277_2
+# 1277| r1277_4(glval) = VariableAddress[int_arg] :
+# 1277| r1277_5(int) = Load : &:r1277_4, ~mu1270_4
+# 1277| v1277_6(void) = Call : func:r1277_1, 0:r1277_3, 1:r1277_5
+# 1277| mu1277_7(unknown) = ^CallSideEffect : ~mu1270_4
+# 1277| v1277_8(void) = ^BufferReadSideEffect[0] : &:r1277_3, ~mu1270_4
+# 1277| mu1277_9(unknown) = ^BufferMayWriteSideEffect[0] : &:r1277_3
+# 1279| r1279_1(glval) = VariableAddress[a] :
+# 1279| r1279_2(A *) = CopyValue : r1279_1
+# 1279| r1279_3(glval) = FunctionAddress[static_member] :
+# 1279| r1279_4(glval) = VariableAddress[a_arg] :
+# 1279| r1279_5(A *) = Load : &:r1279_4, ~mu1270_4
+# 1279| r1279_6(glval) = VariableAddress[int_arg] :
+# 1279| r1279_7(int) = Load : &:r1279_6, ~mu1270_4
+# 1279| r1279_8(int) = Constant[2] :
+# 1279| r1279_9(int) = Add : r1279_7, r1279_8
+# 1279| v1279_10(void) = Call : func:r1279_3, 0:r1279_5, 1:r1279_9
+# 1279| mu1279_11(unknown) = ^CallSideEffect : ~mu1270_4
+# 1279| v1279_12(void) = ^BufferReadSideEffect[0] : &:r1279_5, ~mu1270_4
+# 1279| mu1279_13(unknown) = ^BufferMayWriteSideEffect[0] : &:r1279_5
+# 1280| r1280_1(glval) = VariableAddress[a_arg] :
+# 1280| r1280_2(A *) = Load : &:r1280_1, ~mu1270_4
+# 1280| r1280_3(glval) = CopyValue : r1280_2
+# 1280| r1280_4(glval) = FunctionAddress[static_member] :
+# 1280| r1280_5(glval) = VariableAddress[a] :
+# 1280| r1280_6(A *) = CopyValue : r1280_5
+# 1280| r1280_7(int) = Constant[99] :
+# 1280| v1280_8(void) = Call : func:r1280_4, 0:r1280_6, 1:r1280_7
+# 1280| mu1280_9(unknown) = ^CallSideEffect : ~mu1270_4
+# 1280| v1280_10(void) = ^BufferReadSideEffect[0] : &:r1280_6, ~mu1270_4
+# 1280| mu1280_11(unknown) = ^BufferMayWriteSideEffect[0] : &:r1280_6
+# 1281| r1281_1(glval) = VariableAddress[a_arg] :
+# 1281| r1281_2(A *) = Load : &:r1281_1, ~mu1270_4
+# 1281| r1281_3(glval) = FunctionAddress[static_member] :
+# 1281| r1281_4(glval) = VariableAddress[a_arg] :
+# 1281| r1281_5(A *) = Load : &:r1281_4, ~mu1270_4
+# 1281| r1281_6(int) = Constant[-1] :
+# 1281| v1281_7(void) = Call : func:r1281_3, 0:r1281_5, 1:r1281_6
+# 1281| mu1281_8(unknown) = ^CallSideEffect : ~mu1270_4
+# 1281| v1281_9(void) = ^BufferReadSideEffect[0] : &:r1281_5, ~mu1270_4
+# 1281| mu1281_10(unknown) = ^BufferMayWriteSideEffect[0] : &:r1281_5
+# 1283| r1283_1(glval) = VariableAddress[a] :
+# 1283| r1283_2(glval) = FunctionAddress[static_member_without_def] :
+# 1283| v1283_3(void) = Call : func:r1283_2
+# 1283| mu1283_4(unknown) = ^CallSideEffect : ~mu1270_4
+# 1284| r1284_1(glval) = FunctionAddress[static_member_without_def] :
+# 1284| v1284_2(void) = Call : func:r1284_1
+# 1284| mu1284_3(unknown) = ^CallSideEffect : ~mu1270_4
+# 1286| r1286_1(glval) = FunctionAddress[getAnInstanceOfA] :
+# 1286| r1286_2(A *) = Call : func:r1286_1
+# 1286| mu1286_3(unknown) = ^CallSideEffect : ~mu1270_4
+# 1286| r1286_4(glval) = FunctionAddress[static_member_without_def] :
+# 1286| v1286_5(void) = Call : func:r1286_4
+# 1286| mu1286_6(unknown) = ^CallSideEffect : ~mu1270_4
+# 1287| v1287_1(void) = NoOp :
+# 1270| v1270_11(void) = ReturnIndirection[a_arg] : &:r1270_9, ~mu1270_4
+# 1270| v1270_12(void) = ReturnVoid :
+# 1270| v1270_13(void) = UnmodeledUse : mu*
+# 1270| v1270_14(void) = AliasedUse : ~mu1270_4
+# 1270| v1270_15(void) = ExitFunction :
+
+# 1289| int missingReturnValue(bool, int)
+# 1289| Block 0
+# 1289| v1289_1(void) = EnterFunction :
+# 1289| mu1289_2(unknown) = AliasedDefinition :
+# 1289| mu1289_3(unknown) = InitializeNonLocal :
+# 1289| mu1289_4(unknown) = UnmodeledDefinition :
+# 1289| r1289_5(glval) = VariableAddress[b] :
+# 1289| mu1289_6(bool) = InitializeParameter[b] : &:r1289_5
+# 1289| r1289_7(glval) = VariableAddress[x] :
+# 1289| mu1289_8(int) = InitializeParameter[x] : &:r1289_7
+# 1290| r1290_1(glval) = VariableAddress[b] :
+# 1290| r1290_2(bool) = Load : &:r1290_1, ~mu1289_4
+# 1290| v1290_3(void) = ConditionalBranch : r1290_2
+#-----| False -> Block 1
+#-----| True -> Block 2
+
+# 1293| Block 1
+# 1293| v1293_1(void) = Unreached :
+
+# 1291| Block 2
+# 1291| r1291_1(glval) = VariableAddress[#return] :
+# 1291| r1291_2(glval) = VariableAddress[x] :
+# 1291| r1291_3(int) = Load : &:r1291_2, ~mu1289_4
+# 1291| mu1291_4(int) = Store : &:r1291_1, r1291_3
+# 1289| r1289_9(glval) = VariableAddress[#return] :
+# 1289| v1289_10(void) = ReturnValue : &:r1289_9, ~mu1289_4
+# 1289| v1289_11(void) = UnmodeledUse : mu*
+# 1289| v1289_12(void) = AliasedUse : ~mu1289_4
+# 1289| v1289_13(void) = ExitFunction :
+
+# 1295| void returnVoid(int, int)
+# 1295| Block 0
+# 1295| v1295_1(void) = EnterFunction :
+# 1295| mu1295_2(unknown) = AliasedDefinition :
+# 1295| mu1295_3(unknown) = InitializeNonLocal :
+# 1295| mu1295_4(unknown) = UnmodeledDefinition :
+# 1295| r1295_5(glval) = VariableAddress[x] :
+# 1295| mu1295_6(int) = InitializeParameter[x] : &:r1295_5
+# 1295| r1295_7(glval) = VariableAddress[y] :
+# 1295| mu1295_8(int) = InitializeParameter[y] : &:r1295_7
+# 1296| r1296_1(glval) = FunctionAddress[IntegerOps] :
+# 1296| r1296_2(glval) = VariableAddress[x] :
+# 1296| r1296_3(int) = Load : &:r1296_2, ~mu1295_4
+# 1296| r1296_4(glval) = VariableAddress[y] :
+# 1296| r1296_5(int) = Load : &:r1296_4, ~mu1295_4
+# 1296| v1296_6(void) = Call : func:r1296_1, 0:r1296_3, 1:r1296_5
+# 1296| mu1296_7(unknown) = ^CallSideEffect : ~mu1295_4
+# 1296| v1296_8(void) = NoOp :
+# 1295| v1295_9(void) = ReturnVoid :
+# 1295| v1295_10(void) = UnmodeledUse : mu*
+# 1295| v1295_11(void) = AliasedUse : ~mu1295_4
+# 1295| v1295_12(void) = ExitFunction :
+
+# 1299| void gccBinaryConditional(bool, int, long)
+# 1299| Block 0
+# 1299| v1299_1(void) = EnterFunction :
+# 1299| mu1299_2(unknown) = AliasedDefinition :
+# 1299| mu1299_3(unknown) = InitializeNonLocal :
+# 1299| mu1299_4(unknown) = UnmodeledDefinition :
+# 1299| r1299_5(glval) = VariableAddress[b] :
+# 1299| mu1299_6(bool) = InitializeParameter[b] : &:r1299_5
+# 1299| r1299_7(glval) = VariableAddress[x] :
+# 1299| mu1299_8(int) = InitializeParameter[x] : &:r1299_7
+# 1299| r1299_9(glval) = VariableAddress[y] :
+# 1299| mu1299_10(long) = InitializeParameter[y] : &:r1299_9
+# 1300| r1300_1(glval) = VariableAddress[z] :
+# 1300| r1300_2(glval) = VariableAddress[x] :
+# 1300| r1300_3(int) = Load : &:r1300_2, ~mu1299_4
+# 1300| mu1300_4(int) = Store : &:r1300_1, r1300_3
+# 1301| r1301_1(glval) = VariableAddress[b] :
+# 1301| r1301_2(bool) = Load : &:r1301_1, ~mu1299_4
+# 1301| v1301_3(void) = ConditionalBranch : r1301_2
+#-----| False -> Block 3
+#-----| True -> Block 2
+
+# 1301| Block 1
+# 1301| r1301_4(glval) = VariableAddress[#temp1301:9] :
+# 1301| r1301_5(int) = Load : &:r1301_4, ~mu1299_4
+# 1301| r1301_6(glval) = VariableAddress[z] :
+# 1301| mu1301_7(int) = Store : &:r1301_6, r1301_5
+# 1302| r1302_1(glval) = VariableAddress[b] :
+# 1302| r1302_2(bool) = Load : &:r1302_1, ~mu1299_4
+# 1302| v1302_3(void) = ConditionalBranch : r1302_2
+#-----| False -> Block 6
+#-----| True -> Block 5
+
+# 1301| Block 2
+# 1301| r1301_8(glval) = VariableAddress[#temp1301:9] :
+# 1301| mu1301_9(int) = Store : &:r1301_8, r1301_2
+#-----| Goto -> Block 1
+
+# 1301| Block 3
+# 1301| r1301_10(glval) = VariableAddress[x] :
+# 1301| r1301_11(int) = Load : &:r1301_10, ~mu1299_4
+# 1301| r1301_12(glval) = VariableAddress[#temp1301:9] :
+# 1301| mu1301_13(int) = Store : &:r1301_12, r1301_11
+#-----| Goto -> Block 1
+
+# 1302| Block 4
+# 1302| r1302_4(glval) = VariableAddress[#temp1302:9] :
+# 1302| r1302_5(long) = Load : &:r1302_4, ~mu1299_4
+# 1302| r1302_6(int) = Convert : r1302_5
+# 1302| r1302_7(glval) = VariableAddress[z] :
+# 1302| mu1302_8(int) = Store : &:r1302_7, r1302_6
+# 1303| r1303_1(glval) = VariableAddress[x] :
+# 1303| r1303_2(int) = Load : &:r1303_1, ~mu1299_4
+# 1303| r1303_3(int) = Constant[0] :
+# 1303| r1303_4(bool) = CompareNE : r1303_2, r1303_3
+# 1303| v1303_5(void) = ConditionalBranch : r1303_4
+#-----| False -> Block 9
+#-----| True -> Block 8
+
+# 1302| Block 5
+# 1302| r1302_9(glval) = VariableAddress[#temp1302:9] :
+# 1302| mu1302_10(long) = Store : &:r1302_9, r1302_2
+#-----| Goto -> Block 4
+
+# 1302| Block 6
+# 1302| r1302_11(glval) = VariableAddress[y] :
+# 1302| r1302_12(long) = Load : &:r1302_11, ~mu1299_4
+# 1302| r1302_13(glval) = VariableAddress[#temp1302:9] :
+# 1302| mu1302_14(long) = Store : &:r1302_13, r1302_12
+#-----| Goto -> Block 4
+
+# 1303| Block 7
+# 1303| r1303_6(glval) = VariableAddress[#temp1303:9] :
+# 1303| r1303_7(int) = Load : &:r1303_6, ~mu1299_4
+# 1303| r1303_8(glval) = VariableAddress[z] :
+# 1303| mu1303_9(int) = Store : &:r1303_8, r1303_7
+# 1304| r1304_1(glval) = VariableAddress[x] :
+# 1304| r1304_2(int) = Load : &:r1304_1, ~mu1299_4
+# 1304| r1304_3(int) = Constant[0] :
+# 1304| r1304_4(bool) = CompareNE : r1304_2, r1304_3
+# 1304| v1304_5(void) = ConditionalBranch : r1304_4
+#-----| False -> Block 12
+#-----| True -> Block 11
+
+# 1303| Block 8
+# 1303| r1303_10(glval) = VariableAddress[#temp1303:9] :
+# 1303| mu1303_11(int) = Store : &:r1303_10, r1303_2
+#-----| Goto -> Block 7
+
+# 1303| Block 9
+# 1303| r1303_12(glval) = VariableAddress[x] :
+# 1303| r1303_13(int) = Load : &:r1303_12, ~mu1299_4
+# 1303| r1303_14(glval) = VariableAddress[#temp1303:9] :
+# 1303| mu1303_15(int) = Store : &:r1303_14, r1303_13
+#-----| Goto -> Block 7
+
+# 1304| Block 10
+# 1304| r1304_6(glval) = VariableAddress[#temp1304:9] :
+# 1304| r1304_7(long) = Load : &:r1304_6, ~mu1299_4
+# 1304| r1304_8(int) = Convert : r1304_7
+# 1304| r1304_9(glval) = VariableAddress[z] :
+# 1304| mu1304_10(int) = Store : &:r1304_9, r1304_8
+# 1305| r1305_1(glval) = VariableAddress[y] :
+# 1305| r1305_2(long) = Load : &:r1305_1, ~mu1299_4
+# 1305| r1305_3(long) = Constant[0] :
+# 1305| r1305_4(bool) = CompareNE : r1305_2, r1305_3
+# 1305| v1305_5(void) = ConditionalBranch : r1305_4
+#-----| False -> Block 15
+#-----| True -> Block 14
+
+# 1304| Block 11
+# 1304| r1304_11(glval) = VariableAddress[#temp1304:9] :
+# 1304| mu1304_12(long) = Store : &:r1304_11, r1304_2
+#-----| Goto -> Block 10
+
+# 1304| Block 12
+# 1304| r1304_13(glval) = VariableAddress[y] :
+# 1304| r1304_14(long) = Load : &:r1304_13, ~mu1299_4
+# 1304| r1304_15(glval) = VariableAddress[#temp1304:9] :
+# 1304| mu1304_16(long) = Store : &:r1304_15, r1304_14
+#-----| Goto -> Block 10
+
+# 1305| Block 13
+# 1305| r1305_6(glval) = VariableAddress[#temp1305:9] :
+# 1305| r1305_7(long) = Load : &:r1305_6, ~mu1299_4
+# 1305| r1305_8(int) = Convert : r1305_7
+# 1305| r1305_9(glval) = VariableAddress[z] :
+# 1305| mu1305_10(int) = Store : &:r1305_9, r1305_8
+# 1306| r1306_1(glval) = VariableAddress[y] :
+# 1306| r1306_2(long) = Load : &:r1306_1, ~mu1299_4
+# 1306| r1306_3(long) = Constant[0] :
+# 1306| r1306_4(bool) = CompareNE : r1306_2, r1306_3
+# 1306| v1306_5(void) = ConditionalBranch : r1306_4
+#-----| False -> Block 18
+#-----| True -> Block 17
+
+# 1305| Block 14
+# 1305| r1305_11(glval) = VariableAddress[#temp1305:9] :
+# 1305| mu1305_12(long) = Store : &:r1305_11, r1305_2
+#-----| Goto -> Block 13
+
+# 1305| Block 15
+# 1305| r1305_13(glval) = VariableAddress[x] :
+# 1305| r1305_14(int) = Load : &:r1305_13, ~mu1299_4
+# 1305| r1305_15(long) = Convert : r1305_14
+# 1305| r1305_16(glval) = VariableAddress[#temp1305:9] :
+# 1305| mu1305_17(long) = Store : &:r1305_16, r1305_15
+#-----| Goto -> Block 13
+
+# 1306| Block 16
+# 1306| r1306_6(glval) = VariableAddress[#temp1306:9] :
+# 1306| r1306_7(long) = Load : &:r1306_6, ~mu1299_4
+# 1306| r1306_8(int) = Convert : r1306_7
+# 1306| r1306_9(glval) = VariableAddress[z] :
+# 1306| mu1306_10(int) = Store : &:r1306_9, r1306_8
+# 1308| r1308_1(glval) = VariableAddress[x] :
+# 1308| r1308_2(int) = Load : &:r1308_1, ~mu1299_4
+# 1308| r1308_3(int) = Constant[0] :
+# 1308| r1308_4(bool) = CompareNE : r1308_2, r1308_3
+# 1308| v1308_5(void) = ConditionalBranch : r1308_4
+#-----| False -> Block 25
+#-----| True -> Block 24
+
+# 1306| Block 17
+# 1306| r1306_11(glval) = VariableAddress[#temp1306:9] :
+# 1306| mu1306_12(long) = Store : &:r1306_11, r1306_2
+#-----| Goto -> Block 16
+
+# 1306| Block 18
+# 1306| r1306_13(glval) = VariableAddress[y] :
+# 1306| r1306_14(long) = Load : &:r1306_13, ~mu1299_4
+# 1306| r1306_15(glval) = VariableAddress[#temp1306:9] :
+# 1306| mu1306_16(long) = Store : &:r1306_15, r1306_14
+#-----| Goto -> Block 16
+
+# 1308| Block 19
+# 1308| r1308_6(glval) = VariableAddress[#temp1308:9] :
+# 1308| r1308_7(int) = Load : &:r1308_6, ~mu1299_4
+# 1308| r1308_8(glval) = VariableAddress[z] :
+# 1308| mu1308_9(int) = Store : &:r1308_8, r1308_7
+# 1309| v1309_1(void) = NoOp :
+# 1299| v1299_11(void) = ReturnVoid :
+# 1299| v1299_12(void) = UnmodeledUse : mu*
+# 1299| v1299_13(void) = AliasedUse : ~mu1299_4
+# 1299| v1299_14(void) = ExitFunction :
+
+# 1308| Block 20
+# 1308| r1308_10(glval) = VariableAddress[#temp1308:9] :
+# 1308| mu1308_11(int) = Store : &:r1308_10, r1308_16
+#-----| Goto -> Block 19
+
+# 1308| Block 21
+# 1308| r1308_12(glval) = VariableAddress[#temp1308:10] :
+# 1308| r1308_13(bool) = Constant[0] :
+# 1308| mu1308_14(bool) = Store : &:r1308_12, r1308_13
+#-----| Goto -> Block 22
+
+# 1308| Block 22
+# 1308| r1308_15(glval) = VariableAddress[#temp1308:10] :
+# 1308| r1308_16(bool) = Load : &:r1308_15, ~mu1299_4
+# 1308| v1308_17(void) = ConditionalBranch : r1308_16
+#-----| False -> Block 26
+#-----| True -> Block 20
+
+# 1308| Block 23
+# 1308| r1308_18(glval) = VariableAddress[#temp1308:10] :
+# 1308| r1308_19(bool) = Constant[1] :
+# 1308| mu1308_20(bool) = Store : &:r1308_18, r1308_19
+#-----| Goto -> Block 22
+
+# 1308| Block 24
+# 1308| r1308_21(glval) = VariableAddress[b] :
+# 1308| r1308_22(bool) = Load : &:r1308_21, ~mu1299_4
+# 1308| v1308_23(void) = ConditionalBranch : r1308_22
+#-----| False -> Block 25
+#-----| True -> Block 23
+
+# 1308| Block 25
+# 1308| r1308_24(glval) = VariableAddress[y] :
+# 1308| r1308_25(long) = Load : &:r1308_24, ~mu1299_4
+# 1308| r1308_26(long) = Constant[0] :
+# 1308| r1308_27(bool) = CompareNE : r1308_25, r1308_26
+# 1308| v1308_28(void) = ConditionalBranch : r1308_27
+#-----| False -> Block 21
+#-----| True -> Block 23
+
+# 1308| Block 26
+# 1308| r1308_29(glval) = VariableAddress[x] :
+# 1308| r1308_30(int) = Load : &:r1308_29, ~mu1299_4
+# 1308| r1308_31(glval) = VariableAddress[#temp1308:9] :
+# 1308| mu1308_32(int) = Store : &:r1308_31, r1308_30
+#-----| Goto -> Block 19
+
+# 1314| int shortCircuitConditional(int, int)
+# 1314| Block 0
+# 1314| v1314_1(void) = EnterFunction :
+# 1314| mu1314_2(unknown) = AliasedDefinition :
+# 1314| mu1314_3(unknown) = InitializeNonLocal :
+# 1314| mu1314_4(unknown) = UnmodeledDefinition :
+# 1314| r1314_5(glval) = VariableAddress[x] :
+# 1314| mu1314_6(int) = InitializeParameter[x] : &:r1314_5
+# 1314| r1314_7(glval) = VariableAddress[y] :
+# 1314| mu1314_8(int) = InitializeParameter[y] : &:r1314_7
+# 1315| r1315_1(glval) = VariableAddress[#return] :
+# 1315| r1315_2(glval) = FunctionAddress[predicateA] :
+# 1315| r1315_3(bool) = Call : func:r1315_2
+# 1315| mu1315_4(unknown) = ^CallSideEffect : ~mu1314_4
+# 1315| v1315_5(void) = ConditionalBranch : r1315_3
+#-----| False -> Block 3
+#-----| True -> Block 1
+
+# 1315| Block 1
+# 1315| r1315_6(glval) = FunctionAddress[predicateB] :
+# 1315| r1315_7(bool) = Call : func:r1315_6
+# 1315| mu1315_8(unknown) = ^CallSideEffect : ~mu1314_4
+# 1315| v1315_9(void) = ConditionalBranch : r1315_7
+#-----| False -> Block 3
+#-----| True -> Block 2
+
+# 1315| Block 2
+# 1315| r1315_10(glval) = VariableAddress[x] :
+# 1315| r1315_11(int) = Load : &:r1315_10, ~mu1314_4
+# 1315| r1315_12(glval) = VariableAddress[#temp1315:12] :
+# 1315| mu1315_13(int) = Store : &:r1315_12, r1315_11
+#-----| Goto -> Block 4
+
+# 1315| Block 3
+# 1315| r1315_14(glval) = VariableAddress[y] :
+# 1315| r1315_15(int) = Load : &:r1315_14, ~mu1314_4
+# 1315| r1315_16(glval