Merge remote-tracking branch 'upstream/master' into dataflow-indirect-args

Conflicts:
	cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
	cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
	cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/defaulttainttracking.cpp
	cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected
	cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected
	cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected
This commit is contained in:
Jonas Jensen
2020-05-11 13:53:35 +02:00
894 changed files with 55639 additions and 38993 deletions

2
.gitignore vendored
View File

@@ -19,5 +19,5 @@
# It's useful (though not required) to be able to unpack codeql in the ql checkout itself # It's useful (though not required) to be able to unpack codeql in the ql checkout itself
/codeql/ /codeql/
.vscode/settings.json
csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json

1
.vscode/.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
*.json linguist-language=JSON-with-Comments

10
.vscode/extensions.json vendored Normal file
View File

@@ -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": []
}

27
.vscode/tasks.json vendored Normal file
View File

@@ -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": []
}
]
}

View File

@@ -1,11 +1,20 @@
/cpp/ @Semmle/cpp-analysis /cpp/ @github/codeql-c-analysis
/csharp/ @Semmle/cs /csharp/ @github/codeql-csharp
/java/ @Semmle/java /java/ @github/codeql-java
/javascript/ @Semmle/js /javascript/ @github/codeql-javascript
/python/ @Semmle/python /python/ @github/codeql-python
# Assign query help for docs review
/cpp/**/*.qhelp @hubwriter /cpp/**/*.qhelp @hubwriter
/csharp/**/*.qhelp @jf205 /csharp/**/*.qhelp @jf205
/java/**/*.qhelp @felicitymay /java/**/*.qhelp @felicitymay
/javascript/**/*.qhelp @mchammer01 /javascript/**/*.qhelp @mchammer01
/python/**/*.qhelp @felicitymay /python/**/*.qhelp @felicitymay
/docs/language/ @shati-patel @jf205 /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

View File

@@ -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: ## Our Standards
* Be friendly and patient: Remember you might not be communicating in someone elses 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, peoples 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.
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 ## Enforcement Responsibilities
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.
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.

View File

@@ -1,6 +1,6 @@
# Contributing to CodeQL # 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). 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` * Python: `python/ql/src`
Each language-specific directory contains further subdirectories that group queries based on their `@tags` or purpose. 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. - 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. - 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** 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** 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. - 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. 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). 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 CLA agreements. Under GDPR legislation, we do this
on the basis of our legitimate interest in creating the CodeQL product. on the basis of our legitimate interest in creating the CodeQL product.
Please do get in touch (privacy@semmle.com) if you have any questions about Please do get in touch (privacy@github.com) if you have any questions about
this or our data protection policies. 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 Semmles 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.

View File

@@ -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.

189
LICENSE
View File

@@ -1,176 +1,21 @@
Apache License MIT License
Version 2.0, January 2004
http://www.apache.org/licenses/
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, The above copyright notice and this permission notice shall be included in all
and distribution as defined by Sections 1 through 9 of this document. copies or substantial portions of the Software.
"Licensor" shall mean the copyright owner or entity authorized by THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
the copyright owner that is granting the License. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
"Legal Entity" shall mean the union of the acting entity and all AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
other entities that control, are controlled by, or are under common LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
control with that entity. For the purposes of this definition, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
"control" means (i) the power, direct or indirect, to cause the SOFTWARE.
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

View File

@@ -9,8 +9,20 @@ You can use the [interactive query console](https://lgtm.com/help/lgtm/using-que
## Contributing ## 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 ## 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.

View File

@@ -4,6 +4,8 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
## General improvements ## General improvements
You can now suppress alerts using either single-line block comments (`/* ... */`) or line comments (`// ...`).
## New queries ## New queries
| **Query** | **Tags** | **Purpose** | | **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 ## 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** | | **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. | | 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 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. | | 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. | | 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 | Functions containing `asm` statements are no longer highlighted by this query. | | 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. |
| 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 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. |
| 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. | | 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. |
| 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. |
| 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. | | 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`),<br> Possibly wrong buffer size in string copy (`cpp/bad-strncpy-size`),<br> 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. | | 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`. | | 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 ## Changes to libraries
* The data-flow library has been improved, which affects and improves some security queries. The improvements are: * The built-in C++20 "spaceship operator" (`<=>`) is now supported via the QL
- Track flow through functions that combine taint tracking with flow through fields. class `SpaceshipExpr`. Overloaded forms are modeled as calls to functions
- Track flow through clone-like functions, that is, functions that read contents of a field from a named `operator<=>`.
parameter and stores the value in the field of a returned object. * The data-flow library (`semmle.code.cpp.dataflow.DataFlow` and
* 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. `semmle.code.cpp.dataflow.TaintTracking`) has been improved, which affects
* 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. 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` * The new class `StackVariable` should be used in place of `LocalScopeVariable`
in most cases. The difference is that `StackVariable` does not include in most cases. The difference is that `StackVariable` does not include
variables declared with `static` or `thread_local`. variables declared with `static` or `thread_local`.
* As a rule of thumb, custom queries about the _values_ of variables should * As a rule of thumb, custom queries about the _values_ of variables should
be changed from `LocalScopeVariable` to `StackVariable`, while queries be changed from `LocalScopeVariable` to `StackVariable`, while queries
about the _name or scope_ of variables should remain unchanged. about the _name or scope_ of variables should remain unchanged.
* The `LocalScopeVariableReachability` library is deprecated in favor of * The `LocalScopeVariableReachability` library is deprecated in favor of
`StackVariableReachability`. The functionality is the same. `StackVariableReachability`. The functionality is the same.
* The models library models `strlen` in more detail, and includes common variations such as `wcslen`. * Taint tracking and data flow now features better modeling of commonly-used
* The models library models `gets` and similar functions. library functions:
* The models library now partially models `std::string`. * `gets` and similar functions,
* The taint tracking library (`semmle.code.cpp.dataflow.TaintTracking`) has had * the most common operations on `std::string`,
the following improvements: * `strdup` and similar functions, and
* The library now models data flow through `strdup` and similar functions. * formatting functions such as `sprintf`.
* 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.

View File

@@ -2,30 +2,31 @@
The following changes in version 1.24 affect C# analysis in all applications. 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 ## New queries
| **Query** | **Tags** | **Purpose** | | **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------| |-----------------------------|-----------|--------------------------------------------------------------------|
| Assembly path injection (`cs/assembly-path-injection`) | security, external/cwe/cwe-114 | Finds user-controlled data used to load an assembly. | | 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. | | 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. | | 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. | | 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. | | 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. | | 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 ## Changes to existing queries
| **Query** | **Expected impact** | **Change** | | **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`. | | 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. | | 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. |
| 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. |
## Removal of old queries | 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 ## Changes to code extraction
@@ -37,13 +38,11 @@ The following changes in version 1.24 affect C# analysis in all applications.
## Changes to libraries ## Changes to libraries
* The data-flow library has been improved, which affects and improves most security queries. The improvements are: * 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 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 - Track flow through clone-like methods, that is, methods that read the contents of a field from a
parameter and stores the value in the field of a returned object. 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. * 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. * [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()`. * 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`. * `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. * 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.
## Changes to autobuilder

View File

@@ -4,7 +4,7 @@ The following changes in version 1.24 affect Java analysis in all applications.
## General improvements ## 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. * A `Customizations.qll` file has been added to allow customizations of the standard library that apply to all queries.
## New queries ## New queries
@@ -21,16 +21,16 @@ The following changes in version 1.24 affect Java analysis in all applications.
| **Query** | **Expected impact** | **Change** | | **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. | | 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 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`. | | 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 positives | Useless checks on final fields with a non-null initializer are now reported. | | 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 ## Changes to libraries
* The data-flow library has been improved, which affects and improves most security queries. The improvements are: * 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 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 - 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. parameter and stores the value in the field of a returned object.
* Identification of test classes has been improved. Previously, one of the * Identification of test classes has been improved. Previously, one of the
match conditions would classify any class with a name containing the string 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 "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 general file classification mechanism and thus suppression of alerts, and
also any security queries using taint tracking, as test classes act as also any security queries using taint tracking, as test classes act as
default barriers stopping taint flow. 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 `ParExpr` class is empty. Instead, a parenthesized expression can be
identified with the `Expr.isParenthesized()` member predicate. identified with the `Expr.isParenthesized()` member predicate.

View File

@@ -4,67 +4,68 @@
* TypeScript 3.8 is now supported. * 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: * 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, - Imports with the `.js` extension can now be resolved to a TypeScript file,
when the import refers to a file generated by TypeScript. 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. - 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. - 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. * The analysis of sanitizers has improved, leading to more accurate results from the security queries.
In particular: In particular:
- Sanitizer guards now act across function boundaries in more cases. - 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. - 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: * 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 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 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 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: * Support for the following frameworks and libraries has been improved:
- [Electron](https://electronjs.org/) - [chrome-remote-interface](https://www.npmjs.com/package/chrome-remote-interface)
- [fstream](https://www.npmjs.com/package/fstream) - [Electron](https://electronjs.org/)
- [Handlebars](https://www.npmjs.com/package/handlebars) - [for-in](https://www.npmjs.com/package/for-in)
- [jsonfile](https://www.npmjs.com/package/jsonfile) - [for-own](https://www.npmjs.com/package/for-own)
- [Koa](https://www.npmjs.com/package/koa) - [fstream](https://www.npmjs.com/package/fstream)
- [Node.js](https://nodejs.org/) - [Handlebars](https://www.npmjs.com/package/handlebars)
- [Socket.IO](https://socket.io/) - [http2](https://nodejs.org/api/http2.html)
- [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) - [jQuery](https://jquery.com/)
- [chrome-remote-interface](https://www.npmjs.com/package/chrome-remote-interface) - [jsonfile](https://www.npmjs.com/package/jsonfile)
- [for-in](https://www.npmjs.com/package/for-in) - [Koa](https://www.npmjs.com/package/koa)
- [for-own](https://www.npmjs.com/package/for-own) - [lazy-cache](https://www.npmjs.com/package/lazy-cache)
- [http2](https://nodejs.org/api/http2.html) - [mongodb](https://www.npmjs.com/package/mongodb)
- [jQuery](https://jquery.com/) - [ncp](https://www.npmjs.com/package/ncp)
- [lazy-cache](https://www.npmjs.com/package/lazy-cache) - [Node.js](https://nodejs.org/)
- [mongodb](https://www.npmjs.com/package/mongodb) - [node-dir](https://www.npmjs.com/package/node-dir)
- [ncp](https://www.npmjs.com/package/ncp) - [path-exists](https://www.npmjs.com/package/path-exists)
- [node-dir](https://www.npmjs.com/package/node-dir) - [pg](https://www.npmjs.com/package/pg)
- [path-exists](https://www.npmjs.com/package/path-exists) - [react](https://www.npmjs.com/package/react)
- [pg](https://www.npmjs.com/package/pg) - [recursive-readdir](https://www.npmjs.com/package/recursive-readdir)
- [react](https://www.npmjs.com/package/react) - [request](https://www.npmjs.com/package/request)
- [recursive-readdir](https://www.npmjs.com/package/recursive-readdir) - [rimraf](https://www.npmjs.com/package/rimraf)
- [request](https://www.npmjs.com/package/request) - [send](https://www.npmjs.com/package/send)
- [rimraf](https://www.npmjs.com/package/rimraf) - [Socket.IO](https://socket.io/)
- [send](https://www.npmjs.com/package/send) - [SockJS](https://www.npmjs.com/package/sockjs)
- [SockJS](https://www.npmjs.com/package/sockjs) - [SockJS-client](https://www.npmjs.com/package/sockjs-client)
- [SockJS-client](https://www.npmjs.com/package/sockjs-client) - [typeahead.js](https://www.npmjs.com/package/typeahead.js)
- [typeahead.js](https://www.npmjs.com/package/typeahead.js) - [vinyl-fs](https://www.npmjs.com/package/vinyl-fs)
- [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) - [write-file-atomic](https://www.npmjs.com/package/write-file-atomic)
- [ws](https://github.com/websockets/ws) - [ws](https://github.com/websockets/ws)
## New queries ## New queries
| **Query** | **Tags** | **Purpose** | | **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. | | 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. | | 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. | | 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. | | 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. | | 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** | | **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. | | 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. | | Duplicate parameter names (`js/duplicate-parameter-name`) | Fewer results | This query now ignores 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. |
| 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. | | 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. | | 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. | | 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. |
| Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional ways of constructing arguments to `cmd.exe` and `/bin/sh`. |
| Syntax error (`js/syntax-error`) | Lower severity | This results of this query are now displayed with lower severity. | | 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. | | 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. |
| 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. | | Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional ways of constructing arguments to `cmd.exe` and `/bin/sh`. |
| Identical operands (`js/redundant-operation`) | Fewer results | This query now recognizes cases where the operands change a value using ++/-- expressions. | | Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional ways dangerous paths can be constructed and used. |
| 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. | | 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. |
| Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | More results | This query now recognizes more variations of URL scheme checks. | | 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 ## Changes to libraries
@@ -94,6 +95,6 @@
* An extensible model of the `EventEmitter` pattern has been implemented. * 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 * Taint-tracking configurations now interact differently with the `data` flow label, which may affect queries
that combine taint-tracking and flow labels. 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. - 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. - 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. To retain the old behavior, instead use a barrier, or block the `data` flow label using a labeled sanitizer.

View File

@@ -4,37 +4,52 @@ The following changes in version 1.24 affect Python analysis in all applications
## General improvements ## 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 ## Changes to existing queries
| **Query** | **Expected impact** | **Change** | | **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 ### Web framework support
The QL-library support for the web frameworks Bottle, CherryPy, Falcon, Pyramid, TurboGears, Tornado, and Twisted have 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:
been fixed so they provide a proper HttpRequestTaintSource, instead of a TaintSource. This will enable results for the following queries:
- py/path-injection - `py/path-injection`
- py/command-line-injection - `py/command-line-injection`
- py/reflective-xss - `py/reflective-xss`
- py/sql-injection - `py/sql-injection`
- py/code-injection - `py/code-injection`
- py/unsafe-deserialization - `py/unsafe-deserialization`
- py/url-redirection - `py/url-redirection`
The QL-library support for the web framework Twisted have been fixed so they provide a proper 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 `HttpResponseTaintSink`, instead of a `TaintSink`. This will enable results for the following
queries: queries:
- py/reflective-xss - `py/reflective-xss`
- py/stack-trace-exposure - `py/stack-trace-exposure`
## Changes to libraries ## Changes to libraries
### Taint tracking
- The `urlsplit` and `urlparse` functions now propagate taint appropriately.
- HTTP requests using the `requests` library are now modeled.

View File

@@ -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<int>.B`, no longer are considered unbound generics. (Such nested types do,
however, still have relevant `.getSourceDeclaration()`s, for example `A<>.B`.)
## Changes to autobuilder

View File

@@ -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.

View File

@@ -107,7 +107,7 @@ def choose_latest_file(files):
local_error_count = 0 local_error_count = 0
def emit_local_error(path, line, error): def emit_local_error(path, line, error):
print('ERROR: ' + path + ':' + line + " - " + error) print('ERROR: ' + path + ':' + str(line) + " - " + error)
global local_error_count global local_error_count
local_error_count += 1 local_error_count += 1

View File

@@ -1,5 +1,6 @@
import semmle.code.cpp.pointsto.PointsTo 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) { predicate closed(Expr e) {
fcloseCall(_, e) or fcloseCall(_, e) or
exists(ExprCall c | 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 { class ClosedExpr extends PointsToExpr {
ClosedExpr() { closed(this) } ClosedExpr() { closed(this) }
override predicate interesting() { 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) } predicate fopenCallMayBeClosed(FunctionCall fc) { fopenCall(fc) and anythingPointsTo(fc) }

View File

@@ -2,12 +2,24 @@
import cpp 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 { class ZeroAssignment extends AssignExpr {
ZeroAssignment() { ZeroAssignment() {
this.getAnOperand() instanceof VariableAccess and this.getAnOperand() instanceof VariableAccess and
this.getAnOperand() instanceof Zero this.getAnOperand() instanceof Zero
} }
/** Gets a variable that is assigned the value `0`. */
Variable assignedVariable() { result.getAnAccess() = this.getAnOperand() } Variable assignedVariable() { result.getAnAccess() = this.getAnOperand() }
} }

View File

@@ -4,15 +4,24 @@ private predicate freed(Expr e) {
e = any(DeallocationExpr de).getFreedExpr() e = any(DeallocationExpr de).getFreedExpr()
or or
exists(ExprCall c | 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 c.getAnArgument() = e
) )
} }
/** An expression that might be deallocated. */
class FreedExpr extends PointsToExpr { class FreedExpr extends PointsToExpr {
FreedExpr() { freed(this) } FreedExpr() { freed(this) }
override predicate interesting() { 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) } predicate allocMayBeFreed(AllocationExpr alloc) { anythingPointsTo(alloc) }

View File

@@ -1,10 +1,19 @@
import cpp 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) { predicate valueOfVar(Variable v, Expr val) {
val = v.getAnAccess() or val = v.getAnAccess() or
val.(AssignExpr).getLValue() = v.getAnAccess() 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) { predicate boundsCheckExpr(Variable v, Expr cond) {
exists(EQExpr eq | exists(EQExpr eq |
cond = eq and 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) { predicate conditionalSuccessor(ControlFlowNode node, ControlFlowNode succ) {
if node.isCondition() if node.isCondition()
then succ = node.getATrueSuccessor() or succ = node.getAFalseSuccessor() 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) { predicate boundsChecked(Variable v, ControlFlowNode node) {
exists(Expr test | exists(Expr test |
boundsCheckExpr(v, test) and 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) { predicate errorCondition(Variable v, Expr cond) {
exists(EQExpr eq | exists(EQExpr eq |
cond = eq and 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) { predicate successCondition(Variable v, Expr cond) {
exists(NEExpr ne | exists(NEExpr ne |
cond = ne and 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) { predicate errorSuccessor(Variable v, ControlFlowNode n) {
exists(Expr cond | exists(Expr cond |
errorCondition(v, cond) and n = cond.getATrueSuccessor() 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) { predicate successSuccessor(Variable v, ControlFlowNode n) {
exists(Expr cond | exists(Expr cond |
successCondition(v, cond) and n = cond.getATrueSuccessor() 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) { predicate checkedError(Variable v, ControlFlowNode n) {
errorSuccessor(v, n) errorSuccessor(v, n)
or 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) { predicate checkedSuccess(Variable v, ControlFlowNode n) {
successSuccessor(v, n) successSuccessor(v, n)
or or

View File

@@ -5,17 +5,34 @@
import cpp import cpp
import semmle.code.cpp.controlflow.SSA import semmle.code.cpp.controlflow.SSA
import semmle.code.cpp.dataflow.DataFlow 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 * Holds if `alloc` is a use of `malloc` or `new`. `kind` is
* a string describing the type of the allocation. * a string describing the type of the allocation.
*/ */
predicate allocExpr(Expr alloc, string kind) { predicate allocExpr(Expr alloc, string kind) {
isAllocationExpr(alloc) and
not alloc.isFromUninstantiatedTemplate(_) and
( (
alloc instanceof FunctionCall and exists(Function target |
kind = "malloc" 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 or
alloc instanceof NewExpr and alloc instanceof NewExpr and
kind = "new" and kind = "new" and
@@ -28,7 +45,8 @@ predicate allocExpr(Expr alloc, string kind) {
// exclude placement new and custom overloads as they // exclude placement new and custom overloads as they
// may not conform to assumptions // may not conform to assumptions
not alloc.(NewArrayExpr).getAllocatorCall().getTarget().getNumberOfParameters() > 1 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. * describing the type of that free or delete.
*/ */
predicate freeExpr(Expr free, Expr freed, string kind) { predicate freeExpr(Expr free, Expr freed, string kind) {
freeCall(free, freed) and exists(Function target |
kind = "free" 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 or
free.(DeleteExpr).getExpr() = freed and free.(DeleteExpr).getExpr() = freed and
kind = "delete" kind = "delete"

View File

@@ -30,7 +30,7 @@ predicate allowedTypedefs(TypedefType t) {
* Gets a type which appears literally in the declaration of `d`. * Gets a type which appears literally in the declaration of `d`.
*/ */
Type getAnImmediateUsedType(Declaration d) { Type getAnImmediateUsedType(Declaration d) {
d.isDefined() and d.hasDefinition() and
( (
result = d.(Function).getType() or result = d.(Function).getType() or
result = d.(Variable).getType() result = d.(Variable).getType()

View File

@@ -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. * @description Using the TLS or SSLv23 protocol from the boost::asio library, but not disabling deprecated protocols, or disabling minimum-recommended protocols.
* @kind problem * @kind problem
* @problem.severity error * @problem.severity error
* @id cpp/boost/tls_settings_misconfiguration * @id cpp/boost/tls-settings-misconfiguration
* @tags security * @tags security
*/ */

View File

@@ -8,6 +8,6 @@ struct S {
// Whereas here it does make a semantic difference. // Whereas here it does make a semantic difference.
auto getValCorrect() const -> int { auto getValCorrect() const -> int {
return val return val;
} }
}; };

View File

@@ -6,6 +6,7 @@
import cpp import cpp
pragma[inline]
private predicate arithTypesMatch(Type arg, Type parm) { private predicate arithTypesMatch(Type arg, Type parm) {
arg = parm arg = parm
or or

View File

@@ -4,7 +4,7 @@
* *
* By default they fall back to the reasonable defaults provided in * By default they fall back to the reasonable defaults provided in
* `DefaultOptions.qll`, but by modifying this file, you can customize * `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 import cpp

View File

@@ -2,7 +2,7 @@
* @name Uncontrolled data used in path expression * @name Uncontrolled data used in path expression
* @description Accessing paths influenced by users can allow an * @description Accessing paths influenced by users can allow an
* attacker to access unexpected resources. * attacker to access unexpected resources.
* @kind problem * @kind path-problem
* @problem.severity warning * @problem.severity warning
* @precision medium * @precision medium
* @id cpp/path-injection * @id cpp/path-injection
@@ -17,6 +17,7 @@ import cpp
import semmle.code.cpp.security.FunctionWithWrappers import semmle.code.cpp.security.FunctionWithWrappers
import semmle.code.cpp.security.Security import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking import semmle.code.cpp.security.TaintTracking
import TaintedWithPath
/** /**
* A function for opening a file. * A function for opening a file.
@@ -51,12 +52,19 @@ class FileFunction extends FunctionWithWrappers {
override predicate interestingArg(int arg) { arg = 0 } override predicate interestingArg(int arg) { arg = 0 }
} }
class TaintedPathConfiguration extends TaintTrackingConfiguration {
override predicate isSink(Element tainted) {
exists(FileFunction fileFunction | fileFunction.outermostWrapperFunctionCall(tainted, _))
}
}
from 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 where
fileFunction.outermostWrapperFunctionCall(taintedArg, callChain) and fileFunction.outermostWrapperFunctionCall(taintedArg, callChain) and
tainted(taintSource, taintedArg) and taintedWithPath(taintSource, taintedArg, sourceNode, sinkNode) and
isUserInput(taintSource, taintCause) isUserInput(taintSource, taintCause)
select taintedArg, select taintedArg, sourceNode, sinkNode,
"This argument to a file access function is derived from $@ and then passed to " + callChain, "This argument to a file access function is derived from $@ and then passed to " + callChain,
taintSource, "user input (" + taintCause + ")" taintSource, "user input (" + taintCause + ")"

View File

@@ -2,7 +2,7 @@
* @name CGI script vulnerable to cross-site scripting * @name CGI script vulnerable to cross-site scripting
* @description Writing user input directly to a web page * @description Writing user input directly to a web page
* allows for a cross-site scripting vulnerability. * allows for a cross-site scripting vulnerability.
* @kind problem * @kind path-problem
* @problem.severity error * @problem.severity error
* @precision high * @precision high
* @id cpp/cgi-xss * @id cpp/cgi-xss
@@ -13,6 +13,7 @@
import cpp import cpp
import semmle.code.cpp.commons.Environment import semmle.code.cpp.commons.Environment
import semmle.code.cpp.security.TaintTracking import semmle.code.cpp.security.TaintTracking
import TaintedWithPath
/** A call that prints its arguments to `stdout`. */ /** A call that prints its arguments to `stdout`. */
class PrintStdoutCall extends FunctionCall { class PrintStdoutCall extends FunctionCall {
@@ -27,8 +28,13 @@ class QueryString extends EnvironmentRead {
QueryString() { getEnvironmentVariable() = "QUERY_STRING" } QueryString() { getEnvironmentVariable() = "QUERY_STRING" }
} }
from QueryString query, PrintStdoutCall call, Element printedArg class Configuration extends TaintTrackingConfiguration {
where override predicate isSink(Element tainted) {
call.getAnArgument() = printedArg and exists(PrintStdoutCall call | call.getAnArgument() = tainted)
tainted(query, printedArg) }
select printedArg, "Cross-site scripting vulnerability due to $@.", query, "this query data" }
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"

View File

@@ -3,7 +3,7 @@
* @description Including user-supplied data in a SQL query without * @description Including user-supplied data in a SQL query without
* neutralizing special elements can make code vulnerable * neutralizing special elements can make code vulnerable
* to SQL Injection. * to SQL Injection.
* @kind problem * @kind path-problem
* @problem.severity error * @problem.severity error
* @precision high * @precision high
* @id cpp/sql-injection * @id cpp/sql-injection
@@ -15,6 +15,7 @@ import cpp
import semmle.code.cpp.security.Security import semmle.code.cpp.security.Security
import semmle.code.cpp.security.FunctionWithWrappers import semmle.code.cpp.security.FunctionWithWrappers
import semmle.code.cpp.security.TaintTracking import semmle.code.cpp.security.TaintTracking
import TaintedWithPath
class SQLLikeFunction extends FunctionWithWrappers { class SQLLikeFunction extends FunctionWithWrappers {
SQLLikeFunction() { sqlArgument(this.getName(), _) } SQLLikeFunction() { sqlArgument(this.getName(), _) }
@@ -22,11 +23,19 @@ class SQLLikeFunction extends FunctionWithWrappers {
override predicate interestingArg(int arg) { sqlArgument(this.getName(), arg) } 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 where
runSql.outermostWrapperFunctionCall(taintedArg, callChain) and runSql.outermostWrapperFunctionCall(taintedArg, callChain) and
tainted(taintSource, taintedArg) and taintedWithPath(taintSource, taintedArg, sourceNode, sinkNode) and
isUserInput(taintSource, taintCause) isUserInput(taintSource, taintCause)
select taintedArg, select taintedArg, sourceNode, sinkNode,
"This argument to a SQL query function is derived from $@ and then passed to " + callChain, "This argument to a SQL query function is derived from $@ and then passed to " + callChain,
taintSource, "user input (" + taintCause + ")" taintSource, "user input (" + taintCause + ")"

View File

@@ -3,7 +3,7 @@
* @description Using externally controlled strings in a process * @description Using externally controlled strings in a process
* operation can allow an attacker to execute malicious * operation can allow an attacker to execute malicious
* commands. * commands.
* @kind problem * @kind path-problem
* @problem.severity warning * @problem.severity warning
* @precision medium * @precision medium
* @id cpp/uncontrolled-process-operation * @id cpp/uncontrolled-process-operation
@@ -14,13 +14,24 @@
import cpp import cpp
import semmle.code.cpp.security.Security import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking 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 where
isProcessOperationArgument(processOperation, processOperationArg) and isProcessOperationExplanation(arg, processOperation) and
call.getTarget().getName() = processOperation and taintedWithPath(source, arg, sourceNode, sinkNode)
call.getArgument(processOperationArg) = arg and select arg, sourceNode, sinkNode,
tainted(source, arg)
select arg,
"The value of this argument may come from $@ and is being passed to " + processOperation, source, "The value of this argument may come from $@ and is being passed to " + processOperation, source,
source.toString() source.toString()

View File

@@ -2,7 +2,7 @@
* @name Unbounded write * @name Unbounded write
* @description Buffer write operations that do not control the length * @description Buffer write operations that do not control the length
* of data written may overflow. * of data written may overflow.
* @kind problem * @kind path-problem
* @problem.severity error * @problem.severity error
* @precision medium * @precision medium
* @id cpp/unbounded-write * @id cpp/unbounded-write
@@ -16,6 +16,7 @@
import semmle.code.cpp.security.BufferWrite import semmle.code.cpp.security.BufferWrite
import semmle.code.cpp.security.Security import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking import semmle.code.cpp.security.TaintTracking
import TaintedWithPath
/* /*
* --- Summary of CWE-120 alerts --- * --- 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 --- * --- user input reach ---
*/ */
/** class Configuration extends TaintTrackingConfiguration {
* Identifies expressions that are potentially tainted with user override predicate isSink(Element tainted) { unboundedWriteSource(tainted, _) }
* input. Most of the work for this is actually done by the
* TaintTracking library. override predicate taintThroughGlobals() { any() }
*/
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()
)
} }
/* /*
* --- put it together --- * --- 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 where
isUnboundedWrite(bw) and taintedWithPath(inputSource, tainted, sourceNode, sinkNode) and
tainted2(bw.getASource(), inputSource, inputCause) unboundedWriteSource(tainted, bw)
select bw, "This '" + bw.getBWDesc() + "' with input from $@ may overflow the destination.", select bw, sourceNode, sinkNode,
inputSource, inputCause "This '" + bw.getBWDesc() + "' with input from $@ may overflow the destination.", inputSource,
inputSource.toString()

View File

@@ -3,7 +3,7 @@
* @description Using externally-controlled format strings in * @description Using externally-controlled format strings in
* printf-style functions can lead to buffer overflows * printf-style functions can lead to buffer overflows
* or data representation problems. * or data representation problems.
* @kind problem * @kind path-problem
* @problem.severity warning * @problem.severity warning
* @precision medium * @precision medium
* @id cpp/tainted-format-string * @id cpp/tainted-format-string
@@ -16,12 +16,21 @@ import cpp
import semmle.code.cpp.security.Security import semmle.code.cpp.security.Security
import semmle.code.cpp.security.FunctionWithWrappers import semmle.code.cpp.security.FunctionWithWrappers
import semmle.code.cpp.security.TaintTracking 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 where
printf.outermostWrapperFunctionCall(arg, printfFunction) and printf.outermostWrapperFunctionCall(arg, printfFunction) and
tainted(userValue, arg) and taintedWithPath(userValue, arg, sourceNode, sinkNode) and
isUserInput(userValue, cause) 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 " + "The value of this argument may come from $@ and is being used as a formatting argument to " +
printfFunction, userValue, cause printfFunction, userValue, cause

View File

@@ -3,7 +3,7 @@
* @description Using externally-controlled format strings in * @description Using externally-controlled format strings in
* printf-style functions can lead to buffer overflows * printf-style functions can lead to buffer overflows
* or data representation problems. * or data representation problems.
* @kind problem * @kind path-problem
* @problem.severity warning * @problem.severity warning
* @precision medium * @precision medium
* @id cpp/tainted-format-string-through-global * @id cpp/tainted-format-string-through-global
@@ -16,15 +16,24 @@ import cpp
import semmle.code.cpp.security.FunctionWithWrappers import semmle.code.cpp.security.FunctionWithWrappers
import semmle.code.cpp.security.Security import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking 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 from
PrintfLikeFunction printf, Expr arg, string printfFunction, Expr userValue, string cause, PrintfLikeFunction printf, Expr arg, PathNode sourceNode, PathNode sinkNode,
string globalVar string printfFunction, Expr userValue, string cause
where where
printf.outermostWrapperFunctionCall(arg, printfFunction) and printf.outermostWrapperFunctionCall(arg, printfFunction) and
not tainted(_, arg) and not taintedWithoutGlobals(arg) and
taintedIncludingGlobalVars(userValue, arg, globalVar) and taintedWithPath(userValue, arg, sourceNode, sinkNode) and
isUserInput(userValue, cause) isUserInput(userValue, cause)
select arg, select arg, sourceNode, sinkNode,
"This value may flow through $@, originating from $@, and is a formatting argument to " + "The value of this argument may come from $@ and is being used as a formatting argument to " +
printfFunction + ".", globalVarFromId(globalVar), globalVar, userValue, cause printfFunction, userValue, cause

View File

@@ -2,7 +2,7 @@
* @name Uncontrolled data in arithmetic expression * @name Uncontrolled data in arithmetic expression
* @description Arithmetic operations on uncontrolled data that is not * @description Arithmetic operations on uncontrolled data that is not
* validated can cause overflows. * validated can cause overflows.
* @kind problem * @kind path-problem
* @problem.severity warning * @problem.severity warning
* @precision medium * @precision medium
* @id cpp/uncontrolled-arithmetic * @id cpp/uncontrolled-arithmetic
@@ -15,6 +15,7 @@ import cpp
import semmle.code.cpp.security.Overflow import semmle.code.cpp.security.Overflow
import semmle.code.cpp.security.Security import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking import semmle.code.cpp.security.TaintTracking
import TaintedWithPath
predicate isRandCall(FunctionCall fc) { fc.getTarget().getName() = "rand" } predicate isRandCall(FunctionCall fc) { fc.getTarget().getName() = "rand" }
@@ -40,9 +41,22 @@ class SecurityOptionsArith extends SecurityOptions {
} }
} }
predicate taintedVarAccess(Expr origin, VariableAccess va) { predicate isDiv(VariableAccess va) { exists(AssignDivExpr div | div.getLValue() = va) }
isUserInput(origin, _) and
tainted(origin, 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. * range.
*/ */
predicate guardedByAssignDiv(Expr origin) { predicate guardedByAssignDiv(Expr origin) {
isUserInput(origin, _) and exists(VariableAccess va |
exists(AssignDivExpr div, VariableAccess va | tainted(origin, va) and div.getLValue() = 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 where
taintedVarAccess(origin, va) and taintedWithPath(origin, va, sourceNode, sinkNode) and
op.getAnOperand() = va and missingGuard(va, effect) and
(
missingGuardAgainstUnderflow(op, va) and effect = "underflow"
or
missingGuardAgainstOverflow(op, va) and effect = "overflow"
) and
not guardedByAssignDiv(origin) not guardedByAssignDiv(origin)
select va, "$@ flows to here and is used in arithmetic, potentially causing an " + effect + ".", select va, sourceNode, sinkNode,
origin, "Uncontrolled value" "$@ flows to here and is used in arithmetic, potentially causing an " + effect + ".", origin,
"Uncontrolled value"

View File

@@ -2,7 +2,7 @@
* @name Overflow in uncontrolled allocation size * @name Overflow in uncontrolled allocation size
* @description Allocating memory with a size controlled by an external * @description Allocating memory with a size controlled by an external
* user can result in integer overflow. * user can result in integer overflow.
* @kind problem * @kind path-problem
* @problem.severity error * @problem.severity error
* @precision high * @precision high
* @id cpp/uncontrolled-allocation-size * @id cpp/uncontrolled-allocation-size
@@ -13,21 +13,33 @@
import cpp import cpp
import semmle.code.cpp.security.TaintTracking import semmle.code.cpp.security.TaintTracking
import TaintedWithPath
predicate taintedAllocSize(Expr e, Expr source, string taintCause) { /**
( * Holds if `alloc` is an allocation, and `tainted` is a child of it that is a
isAllocationExpr(e) or * taint sink.
any(MulExpr me | me.getAChild() instanceof SizeofOperator) = e */
) and 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 | exists(Expr tainted |
tainted = e.getAChild() and allocSink(alloc, tainted) and
tainted.getUnspecifiedType() instanceof IntegralType and taintedWithPath(source, tainted, sourceNode, sinkNode)
isUserInput(source, taintCause) and
tainted(source, tainted)
) )
} }
from Expr e, Expr source, string taintCause from Expr source, Expr alloc, PathNode sourceNode, PathNode sinkNode, string taintCause
where taintedAllocSize(e, source, taintCause) where taintedAllocSize(source, alloc, sourceNode, sinkNode, taintCause)
select e, "This allocation size is derived from $@ and might overflow", source, select alloc, sourceNode, sinkNode, "This allocation size is derived from $@ and might overflow",
"user input (" + taintCause + ")" source, "user input (" + taintCause + ")"

View File

@@ -3,7 +3,7 @@
* @description Authentication by checking that the peer's address * @description Authentication by checking that the peer's address
* matches a known IP or web address is unsafe as it is * matches a known IP or web address is unsafe as it is
* vulnerable to spoofing attacks. * vulnerable to spoofing attacks.
* @kind problem * @kind path-problem
* @problem.severity warning * @problem.severity warning
* @precision medium * @precision medium
* @id cpp/user-controlled-bypass * @id cpp/user-controlled-bypass
@@ -12,6 +12,7 @@
*/ */
import semmle.code.cpp.security.TaintTracking import semmle.code.cpp.security.TaintTracking
import TaintedWithPath
predicate hardCodedAddressOrIP(StringLiteral txt) { predicate hardCodedAddressOrIP(StringLiteral txt) {
exists(string s | s = txt.getValueText() | exists(string s | s = txt.getValueText() |
@@ -102,16 +103,21 @@ predicate useOfHardCodedAddressOrIP(Expr use) {
* untrusted input then it might be vulnerable to a spoofing * untrusted input then it might be vulnerable to a spoofing
* attack. * attack.
*/ */
predicate hardCodedAddressInCondition(Expr source, Expr condition) { predicate hardCodedAddressInCondition(Expr subexpression, Expr condition) {
// One of the sub-expressions of the condition is tainted. subexpression = condition.getAChild+() and
exists(Expr taintedExpr | taintedExpr.getParent+() = condition | tainted(source, taintedExpr)) and
// One of the sub-expressions of the condition is a hard-coded // One of the sub-expressions of the condition is a hard-coded
// IP or web-address. // 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() condition = any(IfStmt ifStmt).getCondition()
} }
from Expr source, Expr condition class Configuration extends TaintTrackingConfiguration {
where hardCodedAddressInCondition(source, condition) override predicate isSink(Element sink) { hardCodedAddressInCondition(sink, _) }
select condition, "Untrusted input $@ might be vulnerable to a spoofing attack.", source, }
source.toString()
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()

View File

@@ -2,7 +2,7 @@
* @name Cleartext storage of sensitive information in buffer * @name Cleartext storage of sensitive information in buffer
* @description Storing sensitive information in cleartext can expose it * @description Storing sensitive information in cleartext can expose it
* to an attacker. * to an attacker.
* @kind problem * @kind path-problem
* @problem.severity warning * @problem.severity warning
* @precision medium * @precision medium
* @id cpp/cleartext-storage-buffer * @id cpp/cleartext-storage-buffer
@@ -14,12 +14,20 @@ import cpp
import semmle.code.cpp.security.BufferWrite import semmle.code.cpp.security.BufferWrite
import semmle.code.cpp.security.TaintTracking import semmle.code.cpp.security.TaintTracking
import semmle.code.cpp.security.SensitiveExprs 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 where
tainted(taintSource, taintedArg) and taintedWithPath(taintSource, taintedArg, sourceNode, sinkNode) and
isUserInput(taintSource, taintCause) and isUserInput(taintSource, taintCause) and
w.getASource() = taintedArg and w.getASource() = taintedArg and
dest = w.getDest() 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 + ")" taintSource, "user input (" + taintCause + ")"

View File

@@ -2,7 +2,7 @@
* @name Cleartext storage of sensitive information in an SQLite database * @name Cleartext storage of sensitive information in an SQLite database
* @description Storing sensitive information in a non-encrypted * @description Storing sensitive information in a non-encrypted
* database can expose it to an attacker. * database can expose it to an attacker.
* @kind problem * @kind path-problem
* @problem.severity warning * @problem.severity warning
* @precision medium * @precision medium
* @id cpp/cleartext-storage-database * @id cpp/cleartext-storage-database
@@ -13,6 +13,7 @@
import cpp import cpp
import semmle.code.cpp.security.SensitiveExprs import semmle.code.cpp.security.SensitiveExprs
import semmle.code.cpp.security.TaintTracking import semmle.code.cpp.security.TaintTracking
import TaintedWithPath
class UserInputIsSensitiveExpr extends SecurityOptions { class UserInputIsSensitiveExpr extends SecurityOptions {
override predicate isUserInput(Expr expr, string cause) { override predicate isUserInput(Expr expr, string cause) {
@@ -32,10 +33,21 @@ predicate sqlite_encryption_used() {
any(FunctionCall fc).getTarget().getName().matches("sqlite%\\_key\\_%") 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 where
tainted(taintSource, taintedArg) and taintedWithPath(taintSource, taintedArg, sourceNode, sinkNode) and
taintedArg = sqliteCall.getASource() and taintedArg = sqliteCall.getASource()
not sqlite_encryption_used() select sqliteCall, sourceNode, sinkNode,
select sqliteCall, "This SQLite call may store $@ in a non-encrypted SQLite database", taintSource, "This SQLite call may store $@ in a non-encrypted SQLite database", taintSource,
"sensitive information" "sensitive information"

View File

@@ -198,12 +198,12 @@ class InitializationFunction extends Function {
) )
or or
// If we have no definition, we look at SAL annotations // If we have no definition, we look at SAL annotations
not this.isDefined() and not this.hasDefinition() and
this.getParameter(i).(SALParameter).isOut() and this.getParameter(i).(SALParameter).isOut() and
evidence = SuggestiveSALAnnotation() evidence = SuggestiveSALAnnotation()
or or
// We have some external information that this function conditionally initializes // 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 any(ValidatedExternalCondInitFunction vc).isExternallyVerified(this, i) and
evidence = ExternalEvidence() evidence = ExternalEvidence()
} }
@@ -406,7 +406,7 @@ class ConditionalInitializationFunction extends InitializationFunction {
* Explicitly ignore pure virtual functions. * Explicitly ignore pure virtual functions.
*/ */
this.isDefined() and this.hasDefinition() and
this.paramNotReassignedAt(this, i, c) and this.paramNotReassignedAt(this, i, c) and
not this instanceof PureVirtualFunction not this instanceof PureVirtualFunction
) )
@@ -616,11 +616,11 @@ private predicate functionSignature(Function f, string qualifiedName, string typ
* are never statically linked together. * are never statically linked together.
*/ */
private Function getAPossibleDefinition(Function undefinedFunction) { private Function getAPossibleDefinition(Function undefinedFunction) {
not undefinedFunction.isDefined() and not undefinedFunction.hasDefinition() and
exists(string qn, string typeSig | exists(string qn, string typeSig |
functionSignature(undefinedFunction, qn, typeSig) and functionSignature(result, qn, typeSig) functionSignature(undefinedFunction, qn, typeSig) and functionSignature(result, qn, typeSig)
) and ) and
result.isDefined() result.hasDefinition()
} }
/** /**
@@ -631,7 +631,7 @@ private Function getAPossibleDefinition(Function undefinedFunction) {
*/ */
private Function getTarget1(Call c) { private Function getTarget1(Call c) {
result = VirtualDispatch::getAViableTarget(c) and result = VirtualDispatch::getAViableTarget(c) and
result.isDefined() result.hasDefinition()
} }
/** /**

View File

@@ -21,7 +21,7 @@ where
destBase = baseType(destType) and destBase = baseType(destType) and
destBase.getSize() != sourceBase.getSize() and destBase.getSize() != sourceBase.getSize() and
not dest.isInMacroExpansion() 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 // produce a result, because it is likely to be a false
// positive. // positive.
not sourceBase instanceof CharType and not sourceBase instanceof CharType and

View File

@@ -21,7 +21,7 @@ where
destBase = baseType(destType) and destBase = baseType(destType) and
destBase.getSize() != sourceBase.getSize() and destBase.getSize() != sourceBase.getSize() and
not dest.isInMacroExpansion() 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 // produce a result, because it is likely to be a false
// positive. // positive.
not sourceBase instanceof CharType and not sourceBase instanceof CharType and

View File

@@ -24,9 +24,9 @@ private predicate isCharSzPtrExpr(Expr e) {
from Expr sizeofExpr, Expr e from Expr sizeofExpr, Expr e
where where
// If we see an addWithSizeof then we expect the type of // 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. // is probably a mistake.
addWithSizeof(e, sizeofExpr, _) and not isCharSzPtrExpr(e) addWithSizeof(e, sizeofExpr, _) and not isCharSzPtrExpr(e)
select sizeofExpr, select sizeofExpr,
"Suspicious sizeof offset in a pointer arithmetic expression. " + "The type of the pointer is " + "Suspicious sizeof offset in a pointer arithmetic expression. The type of the pointer is $@.",
e.getFullyConverted().getType().toString() + "." e.getFullyConverted().getType() as t, t.toString()

View File

@@ -3,7 +3,7 @@
* @description Using untrusted inputs in a statement that makes a * @description Using untrusted inputs in a statement that makes a
* security decision makes code vulnerable to * security decision makes code vulnerable to
* attack. * attack.
* @kind problem * @kind path-problem
* @problem.severity warning * @problem.severity warning
* @precision medium * @precision medium
* @id cpp/tainted-permissions-check * @id cpp/tainted-permissions-check
@@ -12,14 +12,9 @@
*/ */
import semmle.code.cpp.security.TaintTracking import semmle.code.cpp.security.TaintTracking
import TaintedWithPath
/** predicate sensitiveCondition(Expr condition, Expr raise) {
* 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
raisesPrivilege(raise) and raisesPrivilege(raise) and
exists(IfStmt ifstmt | exists(IfStmt ifstmt |
ifstmt.getCondition() = condition and ifstmt.getCondition() = condition and
@@ -27,7 +22,19 @@ predicate cwe807violation(Expr source, Expr condition, Expr raise) {
) )
} }
from Expr source, Expr condition, Expr raise class Configuration extends TaintTrackingConfiguration {
where cwe807violation(source, condition, raise) override predicate isSink(Element tainted) { sensitiveCondition(tainted, _) }
select condition, "Reliance on untrusted input $@ to raise privilege at $@", source, }
source.toString(), raise, raise.toString()
/*
* 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()

View File

@@ -12,3 +12,8 @@
- Critical/FileNeverClosed.ql - Critical/FileNeverClosed.ql
- Critical/MemoryMayNotBeFreed.ql - Critical/MemoryMayNotBeFreed.ql
- Critical/MemoryNeverFreed.ql - Critical/MemoryNeverFreed.ql
# These are only for IDE use.
- exclude:
tags contain:
- ide-contextual-queries/local-definitions
- ide-contextual-queries/local-references

View File

@@ -132,6 +132,7 @@ private predicate constructorCallTypeMention(ConstructorCall cc, TypeMention tm)
* - `"X"` for macro accesses * - `"X"` for macro accesses
* - `"I"` for import / include directives * - `"I"` for import / include directives
*/ */
cached
Top definitionOf(Top e, string kind) { Top definitionOf(Top e, string kind) {
( (
// call -> function called // call -> function called
@@ -213,3 +214,11 @@ Top definitionOf(Top e, string kind) {
// later on. // later on.
strictcount(result.getLocation()) < 10 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 }

View File

@@ -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)
)
}

View File

@@ -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

View File

@@ -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

View File

@@ -2,3 +2,4 @@ name: codeql-cpp
version: 0.0.0 version: 0.0.0
dbscheme: semmlecode.cpp.dbscheme dbscheme: semmlecode.cpp.dbscheme
suites: codeql-suites suites: codeql-suites
extractor: cpp

View File

@@ -458,6 +458,15 @@ class Class extends UserType {
exists(ClassDerivation d | d.getDerivedClass() = this and d = result) 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) { ClassDerivation getDerivation(int index) {
exists(ClassDerivation d | d.getDerivedClass() = this and d.getIndex() = index and d = result) 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 { class TemplateClass extends Class {
TemplateClass() { usertypes(underlyingElement(this), _, 6) } TemplateClass() { usertypes(underlyingElement(this), _, 6) }
/**
* Gets a class instantiated from this template.
*
* For example for `MyTemplateClass<T>` in the following code, the results are
* `MyTemplateClass<int>` and `MyTemplateClass<long>`:
* ```
* template<class T>
* class MyTemplateClass {
* ...
* };
*
* MyTemplateClass<int> instance;
*
* MyTemplateClass<long> instance;
* ```
*/
Class getAnInstantiation() { Class getAnInstantiation() {
result.isConstructedFrom(this) and result.isConstructedFrom(this) and
exists(result.getATemplateArgument()) exists(result.getATemplateArgument())

View File

@@ -13,8 +13,20 @@ class Comment extends Locatable, @comment {
override Location getLocation() { comments(underlyingElement(this), _, result) } 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, _) } 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() { Element getCommentedElement() {
commentbinding(underlyingElement(this), unresolveElement(result)) commentbinding(underlyingElement(this), unresolveElement(result))
} }

View File

@@ -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: * Three things happen to each file during a compilation:
* *
* 1. The file is compiled by a real compiler, such as gcc or VC. * 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 * 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, * This class provides CPU and elapsed time information for steps 2 and 3,
* but not for step 1. * but not for step 1.
@@ -40,6 +40,7 @@ class Compilation extends @compilation {
/** Gets a file compiled during this invocation. */ /** Gets a file compiled during this invocation. */
File getAFileCompiled() { result = getFileCompiled(_) } File getAFileCompiled() { result = getFileCompiled(_) }
/** Gets the `i`th file compiled during this invocation */
File getFileCompiled(int i) { compilation_compiling_files(this, i, unresolveElement(result)) } File getFileCompiled(int i) { compilation_compiling_files(this, i, unresolveElement(result)) }
/** /**

View File

@@ -25,7 +25,7 @@ private import semmle.code.cpp.internal.QualifiedName as Q
* `DeclarationEntry`, because they always have a unique source location. * `DeclarationEntry`, because they always have a unique source location.
* `EnumConstant` and `FriendDecl` are both examples of this. * `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. * 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. */ /** Holds if the declaration has a definition. */
predicate hasDefinition() { exists(this.getDefinition()) } predicate hasDefinition() { exists(this.getDefinition()) }
/** DEPRECATED: Use `hasDefinition` instead. */
predicate isDefined() { hasDefinition() } predicate isDefined() { hasDefinition() }
/** Gets the preferred location of this declaration, if any. */ /** 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. * available), or the name declared by this entry otherwise.
*/ */
string getCanonicalName() { string getCanonicalName() {
if getDeclaration().isDefined() if getDeclaration().hasDefinition()
then result = getDeclaration().getDefinition().getName() then result = getDeclaration().getDefinition().getName()
else result = getName() else result = getName()
} }

View File

@@ -11,6 +11,7 @@ class Diagnostic extends Locatable, @diagnostic {
/** Gets the error code for this compiler message. */ /** Gets the error code for this compiler message. */
string getTag() { diagnostics(underlyingElement(this), _, result, _, _, _) } string getTag() { diagnostics(underlyingElement(this), _, result, _, _, _) }
/** Holds if `s` is the error code for this compiler message. */
predicate hasTag(string s) { this.getTag() = s } predicate hasTag(string s) { this.getTag() = s }
/** /**

View File

@@ -3,7 +3,7 @@ import semmle.code.cpp.Declaration
import semmle.code.cpp.metrics.MetricFile import semmle.code.cpp.metrics.MetricFile
/** A file or folder. */ /** 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 * Gets the absolute, canonical path of this container, using forward slashes
* as path separator. * 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 * 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`. * 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. * 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). * 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 * Gets the relative path of this file or folder from the root folder of the

View File

@@ -103,6 +103,9 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
/** /**
* Holds if this function is declared to be `constexpr`. * 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") } predicate isDeclaredConstexpr() { this.hasSpecifier("declared_constexpr") }
@@ -115,9 +118,16 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
* template <typename T> constexpr int g(T x) { return f(x); } * template <typename T> constexpr int g(T x) { return f(x); }
* ``` * ```
* `g<int>` is declared constexpr, but is not constexpr. * `g<int>` is declared constexpr, but is not constexpr.
*
* Will also hold if this function is `consteval`.
*/ */
predicate isConstexpr() { this.hasSpecifier("is_constexpr") } 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 * Holds if this function is declared with `__attribute__((naked))` or
* `__declspec(naked)`. * `__declspec(naked)`.

View File

@@ -130,7 +130,7 @@ class NamespaceDeclarationEntry extends Locatable, @namespace_decl {
/** /**
* A C++ `using` directive or `using` declaration. * 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) } override Location getLocation() { usings(underlyingElement(this), _, result) }
} }

View File

@@ -376,6 +376,8 @@ private predicate isIntegralType(@builtintype type, int kind) {
kind = 43 kind = 43
or or
kind = 44 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 original = 43 and canonical = 43 and unsigned = -1 and signed = -1 // char16_t
or or
original = 44 and canonical = 44 and unsigned = -1 and signed = -1 // char32_t 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" } 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`, * The type domain of a floating-point type. One of `RealDomain`, `ComplexDomain`, or
* `double` and `long double` types. * `ImaginaryDomain`.
* ``` */
* float f; class TypeDomain extends TTypeDomain {
* double d; /** Gets a textual representation of this type domain. */
* long double ld; 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 { class FloatingPointType extends ArithmeticType {
final int base;
final TypeDomain domain;
final int realKind;
final boolean extended;
FloatingPointType() { FloatingPointType() {
exists(int kind | exists(int kind |
builtintypes(underlyingElement(this), _, kind, _, _, _) and builtintypes(underlyingElement(this), _, kind, _, _, _) and
( floatingPointTypeMapping(kind, base, domain, realKind, extended)
kind >= 24 and kind <= 32
or
kind >= 38 and kind <= 42
or
kind >= 45 and kind <= 50
)
) )
} }
/** 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; * float f;
* ``` * ```
*/ */
class FloatType extends FloatingPointType { class FloatType extends RealNumberType, BinaryFloatingPointType {
FloatType() { builtintypes(underlyingElement(this), _, 24, _, _, _) } FloatType() { builtintypes(underlyingElement(this), _, 24, _, _, _) }
override string getCanonicalQLClass() { result = "FloatType" } override string getCanonicalQLClass() { result = "FloatType" }
@@ -739,7 +903,7 @@ class FloatType extends FloatingPointType {
* double d; * double d;
* ``` * ```
*/ */
class DoubleType extends FloatingPointType { class DoubleType extends RealNumberType, BinaryFloatingPointType {
DoubleType() { builtintypes(underlyingElement(this), _, 25, _, _, _) } DoubleType() { builtintypes(underlyingElement(this), _, 25, _, _, _) }
override string getCanonicalQLClass() { result = "DoubleType" } override string getCanonicalQLClass() { result = "DoubleType" }
@@ -751,7 +915,7 @@ class DoubleType extends FloatingPointType {
* long double ld; * long double ld;
* ``` * ```
*/ */
class LongDoubleType extends FloatingPointType { class LongDoubleType extends RealNumberType, BinaryFloatingPointType {
LongDoubleType() { builtintypes(underlyingElement(this), _, 26, _, _, _) } LongDoubleType() { builtintypes(underlyingElement(this), _, 26, _, _, _) }
override string getCanonicalQLClass() { result = "LongDoubleType" } override string getCanonicalQLClass() { result = "LongDoubleType" }
@@ -763,7 +927,7 @@ class LongDoubleType extends FloatingPointType {
* __float128 f128; * __float128 f128;
* ``` * ```
*/ */
class Float128Type extends FloatingPointType { class Float128Type extends RealNumberType, BinaryFloatingPointType {
Float128Type() { builtintypes(underlyingElement(this), _, 38, _, _, _) } Float128Type() { builtintypes(underlyingElement(this), _, 38, _, _, _) }
override string getCanonicalQLClass() { result = "Float128Type" } override string getCanonicalQLClass() { result = "Float128Type" }
@@ -775,7 +939,7 @@ class Float128Type extends FloatingPointType {
* _Decimal32 d32; * _Decimal32 d32;
* ``` * ```
*/ */
class Decimal32Type extends FloatingPointType { class Decimal32Type extends RealNumberType, DecimalFloatingPointType {
Decimal32Type() { builtintypes(underlyingElement(this), _, 40, _, _, _) } Decimal32Type() { builtintypes(underlyingElement(this), _, 40, _, _, _) }
override string getCanonicalQLClass() { result = "Decimal32Type" } override string getCanonicalQLClass() { result = "Decimal32Type" }
@@ -787,7 +951,7 @@ class Decimal32Type extends FloatingPointType {
* _Decimal64 d64; * _Decimal64 d64;
* ``` * ```
*/ */
class Decimal64Type extends FloatingPointType { class Decimal64Type extends RealNumberType, DecimalFloatingPointType {
Decimal64Type() { builtintypes(underlyingElement(this), _, 41, _, _, _) } Decimal64Type() { builtintypes(underlyingElement(this), _, 41, _, _, _) }
override string getCanonicalQLClass() { result = "Decimal64Type" } override string getCanonicalQLClass() { result = "Decimal64Type" }
@@ -799,7 +963,7 @@ class Decimal64Type extends FloatingPointType {
* _Decimal128 d128; * _Decimal128 d128;
* ``` * ```
*/ */
class Decimal128Type extends FloatingPointType { class Decimal128Type extends RealNumberType, DecimalFloatingPointType {
Decimal128Type() { builtintypes(underlyingElement(this), _, 42, _, _, _) } Decimal128Type() { builtintypes(underlyingElement(this), _, 42, _, _, _) }
override string getCanonicalQLClass() { result = "Decimal128Type" } override string getCanonicalQLClass() { result = "Decimal128Type" }
@@ -833,6 +997,18 @@ class WideCharType extends IntegralType {
override string getCanonicalQLClass() { result = "WideCharType" } 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. * The C/C++ `char16_t` type. This is available starting with C11 and C++11.
* ``` * ```

View File

@@ -38,7 +38,7 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @
override Specifier getASpecifier() { result = Type.super.getASpecifier() } override Specifier getASpecifier() { result = Type.super.getASpecifier() }
override Location getLocation() { override Location getLocation() {
if isDefined() if hasDefinition()
then result = this.getDefinitionLocation() then result = this.getDefinitionLocation()
else result = this.getADeclarationLocation() else result = this.getADeclarationLocation()
} }

View File

@@ -126,10 +126,7 @@ class Variable extends Declaration, @variable {
or or
exists(AssignExpr ae | ae.getLValue().(Access).getTarget() = this and result = ae.getRValue()) exists(AssignExpr ae | ae.getLValue().(Access).getTarget() = this and result = ae.getRValue())
or or
exists(AggregateLiteral l | exists(ClassAggregateLiteral l | result = l.getFieldExpr(this))
this.getDeclaringType() = l.getType() and
result = l.getChild(this.(Field).getInitializationOrder())
)
} }
/** /**

View File

@@ -23,6 +23,8 @@ predicate freeFunction(Function f, int argNum) { argNum = f.(DeallocationFunctio
/** /**
* A call to a library routine that frees memory. * 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() } predicate freeCall(FunctionCall fc, Expr arg) { arg = fc.(DeallocationExpr).getFreedExpr() }

View File

@@ -12,12 +12,12 @@ abstract class Assertion extends Locatable {
} }
/** /**
* A libc assert, as defined in assert.h. A macro with the head * A libc assert, as defined in assert.h. A macro with a head
* "assert(expr)" that expands to a conditional expression which * that matches the prefix "assert(", and expands to a conditional
* may terminate the program. * expression which may terminate the program.
*/ */
class LibcAssert extends MacroInvocation, Assertion { class LibcAssert extends MacroInvocation, Assertion {
LibcAssert() { this.getMacro().getHead() = "assert(expr)" } LibcAssert() { this.getMacro().getHead().matches("assert(%") }
override Expr getAsserted() { override Expr getAsserted() {
exists(ConditionalExpr ce | this.getAGeneratedElement() = ce | result = ce.getCondition()) exists(ConditionalExpr ce | this.getAGeneratedElement() = ce | result = ce.getCondition())

View File

@@ -92,13 +92,7 @@ int getBufferSize(Expr bufferExpr, Element why) {
// dataflow (all sources must be the same size) // dataflow (all sources must be the same size)
bufferExprNode = DataFlow::exprNode(bufferExpr) and bufferExprNode = DataFlow::exprNode(bufferExpr) and
result = result =
min(Expr def | unique(Expr def |
DataFlow::localFlowStep(DataFlow::exprNode(def), bufferExprNode)
|
getBufferSize(def, _)
) and
result =
max(Expr def |
DataFlow::localFlowStep(DataFlow::exprNode(def), bufferExprNode) DataFlow::localFlowStep(DataFlow::exprNode(def), bufferExprNode)
| |
getBufferSize(def, _) getBufferSize(def, _)

View File

@@ -532,13 +532,7 @@ library class ExprEvaluator extends int {
interestingVariableAccess(e, va, v, true) and interestingVariableAccess(e, va, v, true) and
// All assignments must have the same int value // All assignments must have the same int value
result = result =
min(Expr value | unique(Expr value |
value = v.getAnAssignedValue() and not ignoreVariableAssignment(e, v, value)
|
getValueInternalNonSubExpr(value)
) and
result =
max(Expr value |
value = v.getAnAssignedValue() and not ignoreVariableAssignment(e, v, value) value = v.getAnAssignedValue() and not ignoreVariableAssignment(e, v, value)
| |
getValueInternalNonSubExpr(value) getValueInternalNonSubExpr(value)

View File

@@ -166,10 +166,13 @@ private predicate referenceFromVariableAccess(VariableAccess va, Expr reference)
) )
} }
private predicate valueMayEscapeAt(Expr e) { private predicate addressMayEscapeAt(Expr e) {
exists(Call call | exists(Call call |
e = call.getAnArgument().getFullyConverted() and e = call.getAnArgument().getFullyConverted() and
not stdIdentityFunction(call.getTarget()) not stdIdentityFunction(call.getTarget())
or
e = call.getQualifier().getFullyConverted() and
e.getUnderlyingType() instanceof PointerType
) )
or or
exists(AssignExpr assign | e = assign.getRValue().getFullyConverted()) exists(AssignExpr assign | e = assign.getRValue().getFullyConverted())
@@ -187,8 +190,8 @@ private predicate valueMayEscapeAt(Expr e) {
exists(AsmStmt asm | e = asm.getAChild().(Expr).getFullyConverted()) exists(AsmStmt asm | e = asm.getAChild().(Expr).getFullyConverted())
} }
private predicate valueMayEscapeMutablyAt(Expr e) { private predicate addressMayEscapeMutablyAt(Expr e) {
valueMayEscapeAt(e) and addressMayEscapeAt(e) and
exists(Type t | t = e.getType().getUnderlyingType() | exists(Type t | t = e.getType().getUnderlyingType() |
exists(PointerType pt | exists(PointerType pt |
pt = t 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) { private predicate addressFromVariableAccess(VariableAccess va, Expr e) {
pointerFromVariableAccess(va, e) pointerFromVariableAccess(va, e)
or or
@@ -253,8 +272,11 @@ private module EscapesTree_Cached {
*/ */
cached cached
predicate variableAddressEscapesTree(VariableAccess va, Expr e) { predicate variableAddressEscapesTree(VariableAccess va, Expr e) {
valueMayEscapeAt(e) and addressMayEscapeAt(e) and
addressFromVariableAccess(va, e) addressFromVariableAccess(va, e)
or
lvalueMayEscapeAt(e) and
lvalueFromVariableAccess(va, e)
} }
/** /**
@@ -283,8 +305,11 @@ private module EscapesTree_Cached {
*/ */
cached cached
predicate variableAddressEscapesTreeNonConst(VariableAccess va, Expr e) { predicate variableAddressEscapesTreeNonConst(VariableAccess va, Expr e) {
valueMayEscapeMutablyAt(e) and addressMayEscapeMutablyAt(e) and
addressFromVariableAccess(va, e) addressFromVariableAccess(va, e)
or
lvalueMayEscapeMutablyAt(e) and
lvalueFromVariableAccess(va, e)
} }
/** /**

View File

@@ -1,6 +1,6 @@
/** /**
* DEPRECATED: Recursion through `DataFlow::Configuration` is impossible in * 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 * impossible to accidentally depend on recursion through
* `DataFlow::Configuration` in current releases. * `DataFlow::Configuration` in current releases.
* *

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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 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 { private module FlowThrough {
/** /**
* The first flow-through approximation: * 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. * that indicates (non-)emptiness.
*/ */
private module Cand { private module Cand {
@@ -40,83 +57,47 @@ private module Cached {
* Holds if `p` can flow to `node` in the same callable using only * Holds if `p` can flow to `node` in the same callable using only
* value-preserving steps. * value-preserving steps.
* *
* `read` indicates whether it is contents of `p` that can flow to `node`, * `read` indicates whether it is contents of `p` that can flow to `node`.
* and `stored` indicates whether it flows to contents of `node`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate parameterValueFlowCand( private predicate parameterValueFlowCand(ParameterNode p, Node node, boolean read) {
ParameterNode p, Node node, boolean read, boolean stored
) {
p = node and p = node and
read = false and read = false
stored = false
or or
// local flow // local flow
exists(Node mid | exists(Node mid |
parameterValueFlowCand(p, mid, read, stored) and parameterValueFlowCand(p, mid, read) and
simpleLocalFlowStep(mid, node) simpleLocalFlowStep(mid, node)
) )
or or
// read // 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 | exists(Node mid |
parameterValueFlowCand(p, mid, read, false) and parameterValueFlowCand(p, mid, false) and
storeStep(mid, _, node) and readStep(mid, _, node) and
stored = true read = true
) )
or or
// flow through: no prior read or store // flow through: no prior read
exists(ArgumentNode arg | exists(ArgumentNode arg |
parameterValueFlowArgCand(p, arg, false, false) and parameterValueFlowArgCand(p, arg, false) and
argumentValueFlowsThroughCand(arg, node, read, stored) argumentValueFlowsThroughCand(arg, node, read)
) )
or or
// flow through: no read or store inside method // flow through: no read inside method
exists(ArgumentNode arg | exists(ArgumentNode arg |
parameterValueFlowArgCand(p, arg, read, stored) and parameterValueFlowArgCand(p, arg, read) and
argumentValueFlowsThroughCand(arg, node, false, false) argumentValueFlowsThroughCand(arg, node, 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)
) )
} }
pragma[nomagic] pragma[nomagic]
private predicate parameterValueFlowArgCand( private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) {
ParameterNode p, ArgumentNode arg, boolean read, boolean stored parameterValueFlowCand(p, arg, read)
) {
parameterValueFlowCand(p, arg, read, stored)
} }
pragma[nomagic] pragma[nomagic]
predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) { predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) {
parameterValueFlowCand(p, n.getPreUpdateNode(), false, false) parameterValueFlowCand(p, n.getPreUpdateNode(), false)
}
pragma[nomagic]
private predicate parameterValueFlowsToPostUpdateCand(
ParameterNode p, PostUpdateNode n, boolean read
) {
parameterValueFlowCand(p, n, read, true)
} }
/** /**
@@ -125,33 +106,21 @@ private module Cached {
* into account. * into account.
* *
* `read` indicates whether it is contents of `p` that can flow to the return * `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. * node.
*/ */
predicate parameterValueFlowReturnCand( predicate parameterValueFlowReturnCand(ParameterNode p, ReturnKind kind, boolean read) {
ParameterNode p, ReturnKindExt kind, boolean read, boolean stored
) {
exists(ReturnNode ret | exists(ReturnNode ret |
parameterValueFlowCand(p, ret, read, stored) and parameterValueFlowCand(p, ret, read) and
kind = TValueReturn(ret.getKind()) kind = 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
) )
} }
pragma[nomagic] pragma[nomagic]
private predicate argumentValueFlowsThroughCand0( 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) | 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, * Holds if `arg` flows to `out` through a call using only value-preserving steps,
* not taking call contexts into account. * not taking call contexts into account.
* *
* `read` indicates whether it is contents of `arg` that can flow to `out`, and * `read` indicates whether it is contents of `arg` that can flow to `out`.
* `stored` indicates whether it flows to contents of `out`.
*/ */
predicate argumentValueFlowsThroughCand( predicate argumentValueFlowsThroughCand(ArgumentNode arg, Node out, boolean read) {
ArgumentNode arg, Node out, boolean read, boolean stored exists(DataFlowCall call, ReturnKind kind |
) { argumentValueFlowsThroughCand0(call, arg, kind, read) and
exists(DataFlowCall call, ReturnKindExt kind | out = getAnOutNode(call, kind)
argumentValueFlowsThroughCand0(call, arg, kind, read, stored) and
out = kind.getAnOutNode(call)
) )
} }
predicate cand(ParameterNode p, Node n) { predicate cand(ParameterNode p, Node n) {
parameterValueFlowCand(p, n, _, _) and parameterValueFlowCand(p, n, _) and
( (
parameterValueFlowReturnCand(p, _, _, _) parameterValueFlowReturnCand(p, _, _)
or or
parameterValueFlowsToPreUpdateCand(p, _) parameterValueFlowsToPreUpdateCand(p, _)
) )
@@ -187,7 +153,6 @@ private module Cached {
( (
n instanceof ParameterNode or n instanceof ParameterNode or
n instanceof OutNode or n instanceof OutNode or
n instanceof PostUpdateNode or
readStep(_, _, n) or readStep(_, _, n) or
n instanceof CastNode n instanceof CastNode
) )
@@ -200,10 +165,6 @@ private module Cached {
or or
n instanceof ReturnNode n instanceof ReturnNode
or or
Cand::parameterValueFlowsToPreUpdateCand(_, n)
or
storeStep(n, _, _)
or
readStep(n, _, _) readStep(n, _, _)
or or
n instanceof CastNode n instanceof CastNode
@@ -237,230 +198,140 @@ private module Cached {
/** /**
* The final flow-through calculation: * 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 * that represents the head of the access path. `TContentNone()` means that
* the access path is unrestricted. * the access path is unrestricted.
* - Types are checked using the `compatibleTypes()` relation. * - Types are checked using the `compatibleTypes()` relation.
*/ */
cached
private module Final { private module Final {
/** /**
* Holds if `p` can flow to `node` in the same callable using only * Holds if `p` can flow to `node` in the same callable using only
* value-preserving steps, not taking call contexts into account. * value-preserving steps, not taking call contexts into account.
* *
* `contentIn` describes the content of `p` that can flow to `node` * `contentIn` describes the content of `p` that can flow to `node`
* (if any), and `contentOut` describes the content of `node` that * (if any).
* it flows to (if any).
*/ */
private predicate parameterValueFlow( predicate parameterValueFlow(ParameterNode p, Node node, ContentOption contentIn) {
ParameterNode p, Node node, ContentOption contentIn, ContentOption contentOut parameterValueFlow0(p, node, contentIn) and
) {
parameterValueFlow0(p, node, contentIn, contentOut) and
if node instanceof CastingNode if node instanceof CastingNode
then then
// normal flow through // normal flow through
contentIn = TContentNone() and contentIn = TContentNone() and
contentOut = TContentNone() and
compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node))
or or
// getter // getter
exists(Content fIn | exists(Content fIn |
contentIn.getContent() = fIn and contentIn.getContent() = fIn and
contentOut = TContentNone() and
compatibleTypes(fIn.getType(), getErasedNodeTypeBound(node)) compatibleTypes(fIn.getType(), getErasedNodeTypeBound(node))
) )
or
// (getter+)setter
exists(Content fOut |
contentOut.getContent() = fOut and
compatibleTypes(fOut.getContainerType(), getErasedNodeTypeBound(node))
)
else any() else any()
} }
pragma[nomagic] pragma[nomagic]
private predicate parameterValueFlow0( private predicate parameterValueFlow0(ParameterNode p, Node node, ContentOption contentIn) {
ParameterNode p, Node node, ContentOption contentIn, ContentOption contentOut
) {
p = node and p = node and
Cand::cand(p, _) and Cand::cand(p, _) and
contentIn = TContentNone() and contentIn = TContentNone()
contentOut = TContentNone()
or or
// local flow // local flow
exists(Node mid | exists(Node mid |
parameterValueFlow(p, mid, contentIn, contentOut) and parameterValueFlow(p, mid, contentIn) and
LocalFlowBigStep::localFlowBigStep(mid, node) LocalFlowBigStep::localFlowBigStep(mid, node)
) )
or or
// read // 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 | exists(Node mid, Content f |
parameterValueFlow(p, mid, contentIn, TContentNone()) and parameterValueFlow(p, mid, TContentNone()) and
storeStep(mid, f, node) and readStep(mid, f, node) and
contentOut.getContent() = f contentIn.getContent() = f and
| Cand::parameterValueFlowReturnCand(p, _, true) and
contentIn = TContentNone() and compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType())
compatibleTypes(getErasedNodeTypeBound(p), f.getType())
or
compatibleTypes(contentIn.getContent().getType(), f.getType())
) )
or or
// flow through: no prior read or store // flow through: no prior read
exists(ArgumentNode arg | exists(ArgumentNode arg |
parameterValueFlowArg(p, arg, TContentNone(), TContentNone()) and parameterValueFlowArg(p, arg, TContentNone()) and
argumentValueFlowsThrough(_, arg, contentIn, contentOut, node) argumentValueFlowsThrough(arg, contentIn, node)
) )
or or
// flow through: no read or store inside method // flow through: no read inside method
exists(ArgumentNode arg | exists(ArgumentNode arg |
parameterValueFlowArg(p, arg, contentIn, contentOut) and parameterValueFlowArg(p, arg, contentIn) and
argumentValueFlowsThrough(_, arg, TContentNone(), TContentNone(), node) argumentValueFlowsThrough(arg, 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)
) )
} }
pragma[nomagic] pragma[nomagic]
private predicate parameterValueFlowArg( private predicate parameterValueFlowArg(
ParameterNode p, ArgumentNode arg, ContentOption contentIn, ContentOption contentOut ParameterNode p, ArgumentNode arg, ContentOption contentIn
) { ) {
parameterValueFlow(p, arg, contentIn, contentOut) and parameterValueFlow(p, arg, contentIn) and
Cand::argumentValueFlowsThroughCand(arg, _, _, _) Cand::argumentValueFlowsThroughCand(arg, _, _)
} }
pragma[nomagic] pragma[nomagic]
private predicate argumentValueFlowsThrough0( private predicate argumentValueFlowsThrough0(
DataFlowCall call, ArgumentNode arg, ReturnKindExt kind, ContentOption contentIn, DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn
ContentOption contentOut
) { ) {
exists(ParameterNode param | viableParamArg(call, param, arg) | 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. * not taking call contexts into account.
* *
* `contentIn` describes the content of `arg` that can flow to `out` (if any), and * `contentIn` describes the content of `arg` that can flow to `out` (if any).
* `contentOut` describes the content of `out` that it flows to (if any).
*/ */
cached pragma[nomagic]
predicate argumentValueFlowsThrough( predicate argumentValueFlowsThrough(ArgumentNode arg, ContentOption contentIn, Node out) {
DataFlowCall call, ArgumentNode arg, ContentOption contentIn, ContentOption contentOut, exists(DataFlowCall call, ReturnKind kind |
Node out argumentValueFlowsThrough0(call, arg, kind, contentIn) and
) { out = getAnOutNode(call, kind)
exists(ReturnKindExt kind |
argumentValueFlowsThrough0(call, arg, kind, contentIn, contentOut) and
out = kind.getAnOutNode(call)
| |
// normal flow through // normal flow through
contentIn = TContentNone() and contentIn = TContentNone() and
contentOut = TContentNone() and
compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out))
or or
// getter // getter
exists(Content fIn | exists(Content fIn |
contentIn.getContent() = fIn and contentIn.getContent() = fIn and
contentOut = TContentNone() and
compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and
compatibleTypes(fIn.getType(), getErasedNodeTypeBound(out)) 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 * Holds if `p` can flow to a return node of kind `kind` in the same
* callable using only value-preserving steps. * callable using only value-preserving steps.
* *
* `contentIn` describes the content of `p` that can flow to the return * `contentIn` describes the content of `p` that can flow to the return
* node (if any), and `contentOut` describes the content of the return * node (if any).
* node that it flows to (if any).
*/ */
cached private predicate parameterValueFlowReturn(
predicate parameterValueFlowReturn( ParameterNode p, ReturnKind kind, ContentOption contentIn
ParameterNode p, Node ret, ReturnKindExt kind, ContentOption contentIn,
ContentOption contentOut
) { ) {
ret = exists(ReturnNode ret |
any(ReturnNode n | parameterValueFlow(p, ret, contentIn) and
parameterValueFlow(p, n, contentIn, contentOut) and kind = ret.getKind()
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
)
)
} }
} }
import Final 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 * Holds if data can flow from `node1` to `node2` via a direct assignment to
* `f`. * `f`.
@@ -469,14 +340,14 @@ private module Cached {
* been stored into, in order to handle cases like `x.f1.f2 = y`. * been stored into, in order to handle cases like `x.f1.f2 = y`.
*/ */
cached cached
predicate storeDirect(Node node1, Content f, Node node2) { predicate store(Node node1, Content f, Node node2) {
storeStep(node1, f, node2) and readStep(_, f, _) storeStep(node1, f, node2) and readStep(_, f, _)
or or
exists(Node n1, Node n2 | exists(Node n1, Node n2 |
n1 = node1.(PostUpdateNode).getPreUpdateNode() and n1 = node1.(PostUpdateNode).getPreUpdateNode() and
n2 = node2.(PostUpdateNode).getPreUpdateNode() n2 = node2.(PostUpdateNode).getPreUpdateNode()
| |
argumentValueFlowsThrough(_, n2, TContentSome(f), TContentNone(), n1) argumentValueFlowsThrough(n2, TContentSome(f), n1)
or or
readStep(n2, f, n1) readStep(n2, f, n1)
) )
@@ -520,6 +391,21 @@ private module Cached {
newtype TReturnKindExt = newtype TReturnKindExt =
TValueReturn(ReturnKind kind) or TValueReturn(ReturnKind kind) or
TParamUpdate(int pos) { exists(ParameterNode p | p.isParameterOf(_, pos)) } 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() { CastingNode() {
this instanceof ParameterNode or this instanceof ParameterNode or
this instanceof CastNode or this instanceof CastNode or
this instanceof OutNode or this instanceof OutNodeExt
this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode
} }
} }
@@ -538,7 +423,7 @@ newtype TContentOption =
TContentNone() or TContentNone() or
TContentSome(Content f) TContentSome(Content f)
class ContentOption extends TContentOption { private class ContentOption extends TContentOption {
Content getContent() { this = TContentSome(result) } Content getContent() { this = TContentSome(result) }
predicate hasContent() { exists(this.getContent()) } 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 * 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 * 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(); abstract string toString();
/** Gets a node corresponding to data flow out of `call`. */ /** 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 { class ValueReturnKind extends ReturnKindExt, TValueReturn {
@@ -700,7 +597,9 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn {
override string toString() { result = kind.toString() } 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 { class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
@@ -712,9 +611,9 @@ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
override string toString() { result = "param update " + pos } override string toString() { result = "param update " + pos }
override PostUpdateNode getAnOutNode(DataFlowCall call) { override OutNodeExt getAnOutNode(DataFlowCall call) {
exists(ArgumentNode arg | exists(ArgumentNode arg |
result.getPreUpdateNode() = arg and result.(PostUpdateNode).getPreUpdateNode() = arg and
arg.argumentOf(call, this.getPosition()) arg.argumentOf(call, this.getPosition())
) )
} }
@@ -779,77 +678,58 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
result = viableCallable(call) and cc instanceof CallContextReturn 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] pragma[noinline]
DataFlowType getErasedNodeTypeBound(Node n) { result = getErasedRepr(n.getTypeBound()) } 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 = "<none>"
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 = "<none>"
or
this = TAccessPathFrontSome(any(AccessPathFront apf | result = apf.toString()))
}
}

View File

@@ -43,7 +43,7 @@ class Node extends TNode {
/** /**
* INTERNAL: Do not use. Alternative name for `getFunction`. * 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. */ /** Gets the type of this node. */
Type getType() { none() } // overridden in subclasses Type getType() { none() } // overridden in subclasses
@@ -299,7 +299,7 @@ private class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNo
override Node getPreUpdateNode() { result.asExpr() = pd.getDefinedExpr() } override Node getPreUpdateNode() { result.asExpr() = pd.getDefinedExpr() }
override Location getLocation() { result = pd.getLocation() } override Location getLocation() { result = pd.getActualLocation() }
PartialDefinition getPartialDefinition() { result = pd } PartialDefinition getPartialDefinition() { result = pd }
@@ -496,8 +496,6 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
// Expr -> Expr // Expr -> Expr
exprToExprStep_nocfg(nodeFrom.asExpr(), nodeTo.asExpr()) exprToExprStep_nocfg(nodeFrom.asExpr(), nodeTo.asExpr())
or or
exprToExprStep_nocfg(nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr(), nodeTo.asExpr())
or
// Node -> FlowVar -> VariableAccess // Node -> FlowVar -> VariableAccess
exists(FlowVar var | exists(FlowVar var |
( (
@@ -657,7 +655,7 @@ private module FieldFlow {
exists(FieldConfiguration cfg | cfg.hasFlow(node1, node2)) and exists(FieldConfiguration cfg | cfg.hasFlow(node1, node2)) and
// This configuration should not be able to cross function boundaries, but // This configuration should not be able to cross function boundaries, but
// we double-check here just to be sure. // we double-check here just to be sure.
node1.getFunction() = node2.getFunction() node1.getEnclosingCallable() = node2.getEnclosingCallable()
} }
} }

View File

@@ -113,44 +113,39 @@ class FlowVar extends TFlowVar {
* ``` * ```
*/ */
private module PartialDefinitions { private module PartialDefinitions {
private newtype TPartialDefinition = private predicate isInstanceFieldWrite(FieldAccess fa, ControlFlowNode node) {
TExplicitFieldStoreQualifier(Expr qualifier, ControlFlowNode node) { assignmentLikeOperation(node, _, fa, _)
exists(FieldAccess fa | qualifier = fa.getQualifier() | }
class PartialDefinition extends Expr {
ControlFlowNode node;
PartialDefinition() {
exists(FieldAccess fa | this = fa.getQualifier() |
// `fa = ...`, `fa += ...`, etc.
isInstanceFieldWrite(fa, node) isInstanceFieldWrite(fa, node)
or or
// `fa.a = ...`, `f(&fa)`, etc.
exists(PartialDefinition pd | exists(PartialDefinition pd |
node = pd.getSubBasicBlockStart() and node = pd.getSubBasicBlockStart() and
fa = pd.getDefinedExpr() fa = pd.getDefinedExpr()
) )
) )
} or or
TExplicitCallQualifier(Expr qualifier) { // `e.f(...)`
exists(Call call | exists(Call call |
qualifier = call.getQualifier() and this = call.getQualifier() and
not call.getTarget().hasSpecifier("const") not call.getTarget().hasSpecifier("const")
) ) and
} or node = this
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)
or or
this = TExplicitCallQualifier(definedExpr) and node = definedExpr // `f(e)`, `f(&e)`, etc.
or referenceArgument(node, this)
this = TReferenceArgument(definedExpr, node)
} }
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. * Gets the subBasicBlock where this `PartialDefinition` is defined.
@@ -165,33 +160,29 @@ private module PartialDefinitions {
* ``` * ```
* The expression `x` is being partially defined. * 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 or
definedExpr.getLocation() instanceof UnknownLocation and this.getLocation() instanceof UnknownLocation and
result = definedExpr.getParent().getLocation() result = this.getParent().getLocation()
or 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. * A partial definition that's a definition by reference.
*/ */
class DefinitionByReference extends PartialDefinition, TReferenceArgument { class DefinitionByReference extends PartialDefinition {
VariableAccess va; VariableAccess va;
DefinitionByReference() { DefinitionByReference() { referenceArgument(va, this) }
// `this` is not restricted in this charpred. That's because the full
// extent of this class includes the charpred of the superclass, which
// relates `this` to `definedExpr`, and `va` is functionally determined
// by `definedExpr`.
referenceArgument(va, definedExpr)
}
VariableAccess getVariableAccess() { result = va } VariableAccess getVariableAccess() { result = va }

View File

@@ -338,7 +338,7 @@ class PointerToFieldLiteral extends ImplicitThisFieldAccess {
* int myFunctionTarget(int); * int myFunctionTarget(int);
* *
* void myFunction() { * void myFunction() {
* int (*myFunctionPointer)(int) = &myTarget; * int (*myFunctionPointer)(int) = &myFunctionTarget;
* } * }
* ``` * ```
*/ */

View File

@@ -9,9 +9,8 @@ private import semmle.code.cpp.dataflow.EscapesTree
*/ */
abstract class Call extends Expr, NameQualifiableElement { abstract class Call extends Expr, NameQualifiableElement {
/** /**
* Gets the number of actual parameters in this call; use * Gets the number of arguments (actual parameters) of this call. The count
* `getArgument(i)` with `i` between `0` and `result - 1` to * does _not_ include the qualifier of the call, if any.
* retrieve actuals.
*/ */
int getNumberOfArguments() { result = count(this.getAnArgument()) } int getNumberOfArguments() { result = count(this.getAnArgument()) }
@@ -32,21 +31,24 @@ abstract class Call extends Expr, NameQualifiableElement {
Expr getQualifier() { result = this.getChild(-1) } 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) } Expr getAnArgument() { exists(int i | result = this.getChild(i) and i >= 0) }
/** /**
* Gets the nth argument for this call. * 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 } 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 * 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) * 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)` * `2` at index 0; `3`, `4`, and `3 + 4` at index 1; and `g(4 + 5)`

View File

@@ -34,10 +34,10 @@ abstract class Conversion extends Expr {
* a `PointerBaseClassConversion`, or some other semantic conversion. Similarly, * a `PointerBaseClassConversion`, or some other semantic conversion. Similarly,
* a `PointerDerivedClassConversion` may also be a `CStyleCast` or a `StaticCast`. * 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. * 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 * Gets a string describing the semantic conversion operation being performed by
* this cast. * this cast.
@@ -699,7 +699,7 @@ class SizeofPackOperator extends Expr, @sizeof_pack {
/** /**
* A C/C++ sizeof expression. * A C/C++ sizeof expression.
*/ */
abstract class SizeofOperator extends Expr, @runtime_sizeof { class SizeofOperator extends Expr, @runtime_sizeof {
override int getPrecedence() { result = 16 } override int getPrecedence() { result = 16 }
} }
@@ -762,7 +762,7 @@ class SizeofTypeOperator extends SizeofOperator {
/** /**
* A C++11 `alignof` expression. * A C++11 `alignof` expression.
*/ */
abstract class AlignofOperator extends Expr, @runtime_alignof { class AlignofOperator extends Expr, @runtime_alignof {
override int getPrecedence() { result = 16 } override int getPrecedence() { result = 16 }
} }

View File

@@ -145,8 +145,6 @@ class HexLiteral extends Literal {
/** /**
* A C/C++ aggregate literal. * A C/C++ aggregate literal.
*
* For example:
*/ */
class AggregateLiteral extends Expr, @aggregateliteral { class AggregateLiteral extends Expr, @aggregateliteral {
override string getCanonicalQLClass() { result = "AggregateLiteral" } override string getCanonicalQLClass() { result = "AggregateLiteral" }

View File

@@ -2,6 +2,7 @@ import cpp
import semmle.code.cpp.security.Security import semmle.code.cpp.security.Security
private import semmle.code.cpp.ir.dataflow.DataFlow private import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.dataflow.DataFlow2 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.IR
private import semmle.code.cpp.ir.dataflow.internal.DataFlowDispatch as Dispatch private import semmle.code.cpp.ir.dataflow.internal.DataFlowDispatch as Dispatch
private import semmle.code.cpp.models.interfaces.Taint 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 // TODO: This coarse overapproximation, ported from the old taint tracking
// library, could be replaced with an actual semantic check that a particular // 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. // previously suppressed by this predicate by coincidence.
private predicate hasUpperBoundsCheck(Variable var) { private predicate hasUpperBoundsCheck(Variable var) {
exists(RelationalOperation oper, VariableAccess access | exists(RelationalOperation oper, VariableAccess access |
oper.getLeftOperand() = access and oper.getAnOperand() = access and
access.getTarget() = var and access.getTarget() = var and
// Comparing to 0 is not an upper bound check // 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)) node = getNodeForSource(any(Expr e))
} }
cached
private predicate instructionTaintStep(Instruction i1, Instruction i2) { private predicate instructionTaintStep(Instruction i1, Instruction i2) {
// Expressions computed from tainted data are also tainted // Expressions computed from tainted data are also tainted
exists(CallInstruction call, int argIndex | call = i2 | exists(CallInstruction call, int argIndex | call = i2 |
@@ -187,6 +199,14 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
// Flow through pointer dereference // Flow through pointer dereference
i2.(LoadInstruction).getSourceAddress() = i1 i2.(LoadInstruction).getSourceAddress() = i1
or 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 // Unary instructions tend to preserve enough information in practice that we
// want taint to flow through. // want taint to flow through.
// The exception is `FieldAddressInstruction`. Together with the rule for // 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 // unrelated field. This would happen across function boundaries, where the IR
// would not be able to match loads to stores. // would not be able to match loads to stores.
i2.(UnaryInstruction).getUnary() = i1 and i2.(UnaryInstruction).getUnary() = i1 and
not i2 instanceof FieldAddressInstruction (
not i2 instanceof FieldAddressInstruction
or
i2.(FieldAddressInstruction).getField().getDeclaringType() instanceof Union
)
or or
i2.(ChiInstruction).getPartial() = i1 and // Flow out of definition-by-reference
i2.(ChiInstruction).getPartial() = i1.(WriteSideEffectInstruction) and
not i2.isResultConflated() not i2.isResultConflated()
or 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 | exists(BinaryInstruction bin |
bin = i2 and bin = i2 and
predictableInstruction(i2.getAnOperand().getDef()) and predictableInstruction(i2.getAnOperand().getDef()) and
@@ -356,6 +393,16 @@ private Element adjustedSink(DataFlow::Node sink) {
result.(AssignOperation).getAnOperand() = sink.asExpr() 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 cached
predicate tainted(Expr source, Element tainted) { predicate tainted(Expr source, Element tainted) {
exists(DefaultTaintTrackingCfg cfg, DataFlow::Node sink | 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 cached
predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) { predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
tainted(source, tainted) and 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() } 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) { Function resolveCall(Call call) {
exists(CallInstruction callInstruction | exists(CallInstruction callInstruction |
callInstruction.getAST() = call and callInstruction.getAST() = call and
result = Dispatch::viableCallable(callInstruction) 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()
)
}
}

View File

@@ -70,8 +70,7 @@ private module VirtualDispatch {
// Call return // Call return
exists(DataFlowCall call, ReturnKind returnKind | exists(DataFlowCall call, ReturnKind returnKind |
other = getAnOutNode(call, returnKind) and other = getAnOutNode(call, returnKind) and
src.(ReturnNode).getKind() = returnKind and returnNodeWithKindAndEnclosingCallable(src, returnKind, call.getStaticCallTarget())
call.getStaticCallTarget() = src.getEnclosingCallable()
) and ) and
allowFromArg = false allowFromArg = false
or 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. */ /** Call through a function pointer. */
private class DataSensitiveExprCall extends DataSensitiveCall { private class DataSensitiveExprCall extends DataSensitiveCall {
DataSensitiveExprCall() { not exists(this.getStaticCallTarget()) } DataSensitiveExprCall() { not exists(this.getStaticCallTarget()) }

View File

@@ -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 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 { private module FlowThrough {
/** /**
* The first flow-through approximation: * 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. * that indicates (non-)emptiness.
*/ */
private module Cand { private module Cand {
@@ -40,83 +57,47 @@ private module Cached {
* Holds if `p` can flow to `node` in the same callable using only * Holds if `p` can flow to `node` in the same callable using only
* value-preserving steps. * value-preserving steps.
* *
* `read` indicates whether it is contents of `p` that can flow to `node`, * `read` indicates whether it is contents of `p` that can flow to `node`.
* and `stored` indicates whether it flows to contents of `node`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate parameterValueFlowCand( private predicate parameterValueFlowCand(ParameterNode p, Node node, boolean read) {
ParameterNode p, Node node, boolean read, boolean stored
) {
p = node and p = node and
read = false and read = false
stored = false
or or
// local flow // local flow
exists(Node mid | exists(Node mid |
parameterValueFlowCand(p, mid, read, stored) and parameterValueFlowCand(p, mid, read) and
simpleLocalFlowStep(mid, node) simpleLocalFlowStep(mid, node)
) )
or or
// read // 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 | exists(Node mid |
parameterValueFlowCand(p, mid, read, false) and parameterValueFlowCand(p, mid, false) and
storeStep(mid, _, node) and readStep(mid, _, node) and
stored = true read = true
) )
or or
// flow through: no prior read or store // flow through: no prior read
exists(ArgumentNode arg | exists(ArgumentNode arg |
parameterValueFlowArgCand(p, arg, false, false) and parameterValueFlowArgCand(p, arg, false) and
argumentValueFlowsThroughCand(arg, node, read, stored) argumentValueFlowsThroughCand(arg, node, read)
) )
or or
// flow through: no read or store inside method // flow through: no read inside method
exists(ArgumentNode arg | exists(ArgumentNode arg |
parameterValueFlowArgCand(p, arg, read, stored) and parameterValueFlowArgCand(p, arg, read) and
argumentValueFlowsThroughCand(arg, node, false, false) argumentValueFlowsThroughCand(arg, node, 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)
) )
} }
pragma[nomagic] pragma[nomagic]
private predicate parameterValueFlowArgCand( private predicate parameterValueFlowArgCand(ParameterNode p, ArgumentNode arg, boolean read) {
ParameterNode p, ArgumentNode arg, boolean read, boolean stored parameterValueFlowCand(p, arg, read)
) {
parameterValueFlowCand(p, arg, read, stored)
} }
pragma[nomagic] pragma[nomagic]
predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) { predicate parameterValueFlowsToPreUpdateCand(ParameterNode p, PostUpdateNode n) {
parameterValueFlowCand(p, n.getPreUpdateNode(), false, false) parameterValueFlowCand(p, n.getPreUpdateNode(), false)
}
pragma[nomagic]
private predicate parameterValueFlowsToPostUpdateCand(
ParameterNode p, PostUpdateNode n, boolean read
) {
parameterValueFlowCand(p, n, read, true)
} }
/** /**
@@ -125,33 +106,21 @@ private module Cached {
* into account. * into account.
* *
* `read` indicates whether it is contents of `p` that can flow to the return * `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. * node.
*/ */
predicate parameterValueFlowReturnCand( predicate parameterValueFlowReturnCand(ParameterNode p, ReturnKind kind, boolean read) {
ParameterNode p, ReturnKindExt kind, boolean read, boolean stored
) {
exists(ReturnNode ret | exists(ReturnNode ret |
parameterValueFlowCand(p, ret, read, stored) and parameterValueFlowCand(p, ret, read) and
kind = TValueReturn(ret.getKind()) kind = 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
) )
} }
pragma[nomagic] pragma[nomagic]
private predicate argumentValueFlowsThroughCand0( 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) | 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, * Holds if `arg` flows to `out` through a call using only value-preserving steps,
* not taking call contexts into account. * not taking call contexts into account.
* *
* `read` indicates whether it is contents of `arg` that can flow to `out`, and * `read` indicates whether it is contents of `arg` that can flow to `out`.
* `stored` indicates whether it flows to contents of `out`.
*/ */
predicate argumentValueFlowsThroughCand( predicate argumentValueFlowsThroughCand(ArgumentNode arg, Node out, boolean read) {
ArgumentNode arg, Node out, boolean read, boolean stored exists(DataFlowCall call, ReturnKind kind |
) { argumentValueFlowsThroughCand0(call, arg, kind, read) and
exists(DataFlowCall call, ReturnKindExt kind | out = getAnOutNode(call, kind)
argumentValueFlowsThroughCand0(call, arg, kind, read, stored) and
out = kind.getAnOutNode(call)
) )
} }
predicate cand(ParameterNode p, Node n) { predicate cand(ParameterNode p, Node n) {
parameterValueFlowCand(p, n, _, _) and parameterValueFlowCand(p, n, _) and
( (
parameterValueFlowReturnCand(p, _, _, _) parameterValueFlowReturnCand(p, _, _)
or or
parameterValueFlowsToPreUpdateCand(p, _) parameterValueFlowsToPreUpdateCand(p, _)
) )
@@ -187,7 +153,6 @@ private module Cached {
( (
n instanceof ParameterNode or n instanceof ParameterNode or
n instanceof OutNode or n instanceof OutNode or
n instanceof PostUpdateNode or
readStep(_, _, n) or readStep(_, _, n) or
n instanceof CastNode n instanceof CastNode
) )
@@ -200,10 +165,6 @@ private module Cached {
or or
n instanceof ReturnNode n instanceof ReturnNode
or or
Cand::parameterValueFlowsToPreUpdateCand(_, n)
or
storeStep(n, _, _)
or
readStep(n, _, _) readStep(n, _, _)
or or
n instanceof CastNode n instanceof CastNode
@@ -237,230 +198,140 @@ private module Cached {
/** /**
* The final flow-through calculation: * 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 * that represents the head of the access path. `TContentNone()` means that
* the access path is unrestricted. * the access path is unrestricted.
* - Types are checked using the `compatibleTypes()` relation. * - Types are checked using the `compatibleTypes()` relation.
*/ */
cached
private module Final { private module Final {
/** /**
* Holds if `p` can flow to `node` in the same callable using only * Holds if `p` can flow to `node` in the same callable using only
* value-preserving steps, not taking call contexts into account. * value-preserving steps, not taking call contexts into account.
* *
* `contentIn` describes the content of `p` that can flow to `node` * `contentIn` describes the content of `p` that can flow to `node`
* (if any), and `contentOut` describes the content of `node` that * (if any).
* it flows to (if any).
*/ */
private predicate parameterValueFlow( predicate parameterValueFlow(ParameterNode p, Node node, ContentOption contentIn) {
ParameterNode p, Node node, ContentOption contentIn, ContentOption contentOut parameterValueFlow0(p, node, contentIn) and
) {
parameterValueFlow0(p, node, contentIn, contentOut) and
if node instanceof CastingNode if node instanceof CastingNode
then then
// normal flow through // normal flow through
contentIn = TContentNone() and contentIn = TContentNone() and
contentOut = TContentNone() and
compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node)) compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node))
or or
// getter // getter
exists(Content fIn | exists(Content fIn |
contentIn.getContent() = fIn and contentIn.getContent() = fIn and
contentOut = TContentNone() and
compatibleTypes(fIn.getType(), getErasedNodeTypeBound(node)) compatibleTypes(fIn.getType(), getErasedNodeTypeBound(node))
) )
or
// (getter+)setter
exists(Content fOut |
contentOut.getContent() = fOut and
compatibleTypes(fOut.getContainerType(), getErasedNodeTypeBound(node))
)
else any() else any()
} }
pragma[nomagic] pragma[nomagic]
private predicate parameterValueFlow0( private predicate parameterValueFlow0(ParameterNode p, Node node, ContentOption contentIn) {
ParameterNode p, Node node, ContentOption contentIn, ContentOption contentOut
) {
p = node and p = node and
Cand::cand(p, _) and Cand::cand(p, _) and
contentIn = TContentNone() and contentIn = TContentNone()
contentOut = TContentNone()
or or
// local flow // local flow
exists(Node mid | exists(Node mid |
parameterValueFlow(p, mid, contentIn, contentOut) and parameterValueFlow(p, mid, contentIn) and
LocalFlowBigStep::localFlowBigStep(mid, node) LocalFlowBigStep::localFlowBigStep(mid, node)
) )
or or
// read // 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 | exists(Node mid, Content f |
parameterValueFlow(p, mid, contentIn, TContentNone()) and parameterValueFlow(p, mid, TContentNone()) and
storeStep(mid, f, node) and readStep(mid, f, node) and
contentOut.getContent() = f contentIn.getContent() = f and
| Cand::parameterValueFlowReturnCand(p, _, true) and
contentIn = TContentNone() and compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType())
compatibleTypes(getErasedNodeTypeBound(p), f.getType())
or
compatibleTypes(contentIn.getContent().getType(), f.getType())
) )
or or
// flow through: no prior read or store // flow through: no prior read
exists(ArgumentNode arg | exists(ArgumentNode arg |
parameterValueFlowArg(p, arg, TContentNone(), TContentNone()) and parameterValueFlowArg(p, arg, TContentNone()) and
argumentValueFlowsThrough(_, arg, contentIn, contentOut, node) argumentValueFlowsThrough(arg, contentIn, node)
) )
or or
// flow through: no read or store inside method // flow through: no read inside method
exists(ArgumentNode arg | exists(ArgumentNode arg |
parameterValueFlowArg(p, arg, contentIn, contentOut) and parameterValueFlowArg(p, arg, contentIn) and
argumentValueFlowsThrough(_, arg, TContentNone(), TContentNone(), node) argumentValueFlowsThrough(arg, 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)
) )
} }
pragma[nomagic] pragma[nomagic]
private predicate parameterValueFlowArg( private predicate parameterValueFlowArg(
ParameterNode p, ArgumentNode arg, ContentOption contentIn, ContentOption contentOut ParameterNode p, ArgumentNode arg, ContentOption contentIn
) { ) {
parameterValueFlow(p, arg, contentIn, contentOut) and parameterValueFlow(p, arg, contentIn) and
Cand::argumentValueFlowsThroughCand(arg, _, _, _) Cand::argumentValueFlowsThroughCand(arg, _, _)
} }
pragma[nomagic] pragma[nomagic]
private predicate argumentValueFlowsThrough0( private predicate argumentValueFlowsThrough0(
DataFlowCall call, ArgumentNode arg, ReturnKindExt kind, ContentOption contentIn, DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn
ContentOption contentOut
) { ) {
exists(ParameterNode param | viableParamArg(call, param, arg) | 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. * not taking call contexts into account.
* *
* `contentIn` describes the content of `arg` that can flow to `out` (if any), and * `contentIn` describes the content of `arg` that can flow to `out` (if any).
* `contentOut` describes the content of `out` that it flows to (if any).
*/ */
cached pragma[nomagic]
predicate argumentValueFlowsThrough( predicate argumentValueFlowsThrough(ArgumentNode arg, ContentOption contentIn, Node out) {
DataFlowCall call, ArgumentNode arg, ContentOption contentIn, ContentOption contentOut, exists(DataFlowCall call, ReturnKind kind |
Node out argumentValueFlowsThrough0(call, arg, kind, contentIn) and
) { out = getAnOutNode(call, kind)
exists(ReturnKindExt kind |
argumentValueFlowsThrough0(call, arg, kind, contentIn, contentOut) and
out = kind.getAnOutNode(call)
| |
// normal flow through // normal flow through
contentIn = TContentNone() and contentIn = TContentNone() and
contentOut = TContentNone() and
compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out)) compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out))
or or
// getter // getter
exists(Content fIn | exists(Content fIn |
contentIn.getContent() = fIn and contentIn.getContent() = fIn and
contentOut = TContentNone() and
compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and
compatibleTypes(fIn.getType(), getErasedNodeTypeBound(out)) 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 * Holds if `p` can flow to a return node of kind `kind` in the same
* callable using only value-preserving steps. * callable using only value-preserving steps.
* *
* `contentIn` describes the content of `p` that can flow to the return * `contentIn` describes the content of `p` that can flow to the return
* node (if any), and `contentOut` describes the content of the return * node (if any).
* node that it flows to (if any).
*/ */
cached private predicate parameterValueFlowReturn(
predicate parameterValueFlowReturn( ParameterNode p, ReturnKind kind, ContentOption contentIn
ParameterNode p, Node ret, ReturnKindExt kind, ContentOption contentIn,
ContentOption contentOut
) { ) {
ret = exists(ReturnNode ret |
any(ReturnNode n | parameterValueFlow(p, ret, contentIn) and
parameterValueFlow(p, n, contentIn, contentOut) and kind = ret.getKind()
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
)
)
} }
} }
import Final 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 * Holds if data can flow from `node1` to `node2` via a direct assignment to
* `f`. * `f`.
@@ -469,14 +340,14 @@ private module Cached {
* been stored into, in order to handle cases like `x.f1.f2 = y`. * been stored into, in order to handle cases like `x.f1.f2 = y`.
*/ */
cached cached
predicate storeDirect(Node node1, Content f, Node node2) { predicate store(Node node1, Content f, Node node2) {
storeStep(node1, f, node2) and readStep(_, f, _) storeStep(node1, f, node2) and readStep(_, f, _)
or or
exists(Node n1, Node n2 | exists(Node n1, Node n2 |
n1 = node1.(PostUpdateNode).getPreUpdateNode() and n1 = node1.(PostUpdateNode).getPreUpdateNode() and
n2 = node2.(PostUpdateNode).getPreUpdateNode() n2 = node2.(PostUpdateNode).getPreUpdateNode()
| |
argumentValueFlowsThrough(_, n2, TContentSome(f), TContentNone(), n1) argumentValueFlowsThrough(n2, TContentSome(f), n1)
or or
readStep(n2, f, n1) readStep(n2, f, n1)
) )
@@ -520,6 +391,21 @@ private module Cached {
newtype TReturnKindExt = newtype TReturnKindExt =
TValueReturn(ReturnKind kind) or TValueReturn(ReturnKind kind) or
TParamUpdate(int pos) { exists(ParameterNode p | p.isParameterOf(_, pos)) } 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() { CastingNode() {
this instanceof ParameterNode or this instanceof ParameterNode or
this instanceof CastNode or this instanceof CastNode or
this instanceof OutNode or this instanceof OutNodeExt
this.(PostUpdateNode).getPreUpdateNode() instanceof ArgumentNode
} }
} }
@@ -538,7 +423,7 @@ newtype TContentOption =
TContentNone() or TContentNone() or
TContentSome(Content f) TContentSome(Content f)
class ContentOption extends TContentOption { private class ContentOption extends TContentOption {
Content getContent() { this = TContentSome(result) } Content getContent() { this = TContentSome(result) }
predicate hasContent() { exists(this.getContent()) } 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 * 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 * 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(); abstract string toString();
/** Gets a node corresponding to data flow out of `call`. */ /** 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 { class ValueReturnKind extends ReturnKindExt, TValueReturn {
@@ -700,7 +597,9 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn {
override string toString() { result = kind.toString() } 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 { class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
@@ -712,9 +611,9 @@ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
override string toString() { result = "param update " + pos } override string toString() { result = "param update " + pos }
override PostUpdateNode getAnOutNode(DataFlowCall call) { override OutNodeExt getAnOutNode(DataFlowCall call) {
exists(ArgumentNode arg | exists(ArgumentNode arg |
result.getPreUpdateNode() = arg and result.(PostUpdateNode).getPreUpdateNode() = arg and
arg.argumentOf(call, this.getPosition()) arg.argumentOf(call, this.getPosition())
) )
} }
@@ -779,77 +678,58 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
result = viableCallable(call) and cc instanceof CallContextReturn 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] pragma[noinline]
DataFlowType getErasedNodeTypeBound(Node n) { result = getErasedRepr(n.getTypeBound()) } 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 = "<none>"
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 = "<none>"
or
this = TAccessPathFrontSome(any(AccessPathFront apf | result = apf.toString()))
}
}

View File

@@ -36,7 +36,9 @@ class ArgumentNode extends InstructionNode {
DataFlowCall getCall() { this.argumentOf(result, _) } 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 * 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 { class ReturnKind extends TReturnKind {
/** Gets a textual representation of this return kind. */ /** 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`. */ /** A data flow node that occurs as the result of a `ReturnStmt`. */
class ReturnNode extends InstructionNode { 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. */ /** 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. */ /** A data flow node that represents the output of a call. */
class OutNode extends InstructionNode { class OutNode extends InstructionNode {
override CallInstruction instr; OutNode() {
instr instanceof CallInstruction or
instr instanceof WriteSideEffectInstruction
}
/** Gets the underlying call. */ /** 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) { OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
result.getCall() = call and result.getCall() = call and
kind = TNormalReturnKind() result.getReturnKind() = kind
} }
/** /**
@@ -137,13 +192,32 @@ private class ArrayContent extends Content, TArrayContent {
override Type getType() { none() } 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`. * 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 * Thus, `node2` references an object with a field `f` that contains the
* value of `node1`. * value of `node1`.
*/ */
predicate storeStep(Node node1, Content f, PostUpdateNode node2) { 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`. * `node2`.
*/ */
predicate readStep(Node node1, Content f, Node 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`. */ /** 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 * Holds if `t1` and `t2` are compatible, that is, whether data can flow from

View File

@@ -63,6 +63,18 @@ class Node extends TIRDataFlowNode {
*/ */
Variable asVariable() { result = this.(VariableNode).getVariable() } 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. * DEPRECATED: See UninitializedNode.
* *
@@ -96,6 +108,9 @@ class Node extends TIRDataFlowNode {
string toString() { none() } // overridden by subclasses string toString() { none() } // overridden by subclasses
} }
/**
* An instruction, viewed as a node in a data flow graph.
*/
class InstructionNode extends Node, TInstructionNode { class InstructionNode extends Node, TInstructionNode {
Instruction instr; 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 * 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 * To match a specific kind of parameter, consider using one of the subclasses
* `ExplicitParameterNode`, `InstanceParameterNode`, or * `ExplicitParameterNode`, `ThisParameterNode`, or
* `ParameterIndirectionNode`. * `ParameterIndirectionNode`.
*/ */
class ParameterNode extends InstructionNode { class ParameterNode extends InstructionNode {
ParameterNode() { ParameterNode() {
// To avoid making this class abstract, we enumerate its values here // To avoid making this class abstract, we enumerate its values here
instr instanceof InitializeThisInstruction
or
instr instanceof InitializeParameterInstruction instr instanceof InitializeParameterInstruction
or or
instr instanceof InitializeThisInstruction
or
instr instanceof InitializeIndirectionInstruction instr instanceof InitializeIndirectionInstruction
} }
@@ -178,19 +194,7 @@ class ParameterNode extends InstructionNode {
* implicit `this` parameter is considered to have position `-1`, and * implicit `this` parameter is considered to have position `-1`, and
* pointer-indirection parameters are at further negative positions. * pointer-indirection parameters are at further negative positions.
*/ */
predicate isParameterOf(Function f, int pos) { none() } // overridden in subclasses predicate isParameterOf(Function f, int pos) { none() } // overridden by 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" }
} }
/** An explicit positional parameter, not including `this` or `...`. */ /** An explicit positional parameter, not including `this` or `...`. */
@@ -204,7 +208,18 @@ class ExplicitParameterNode extends ParameterNode {
/** Gets the `Parameter` associated with this node. */ /** Gets the `Parameter` associated with this node. */
Parameter getParameter() { result = instr.getParameter() } 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. */ /** 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(); 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 * 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. * may have changed the variable because it's passed by reference.
@@ -297,6 +363,17 @@ class DefinitionByReferenceNode extends InstructionNode {
Parameter getParameter() { Parameter getParameter() {
exists(CallInstruction ci | result = ci.getStaticCallTarget().getParameter(instr.getIndex())) 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 } 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 } 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 * Gets the `Node` corresponding to `e`, if any. Here, `e` may be a
* `Conversion`. * `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. * 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()) 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) { private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction iTo) {
iTo.(CopyInstruction).getSourceValue() = iFrom iTo.(CopyInstruction).getSourceValue() = iFrom
or or
@@ -424,6 +515,36 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
// for now. // for now.
iTo.getAnOperand().(ChiTotalOperand).getDef() = iFrom iTo.getAnOperand().(ChiTotalOperand).getDef() = iFrom
or 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 // Flow through modeled functions
modelFlow(iFrom, iTo) modelFlow(iFrom, iTo)
} }

View File

@@ -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
}

View File

@@ -1,5 +1,8 @@
private import semmle.code.cpp.ir.IR private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.DataFlow 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 * Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
@@ -45,6 +48,25 @@ private predicate localInstructionTaintStep(Instruction nodeFrom, Instruction no
) )
or or
nodeTo.(LoadInstruction).getSourceAddress() = nodeFrom 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. * but not in local taint.
*/ */
predicate defaultTaintBarrier(DataFlow::Node node) { none() } 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)
)
}

View File

@@ -12,7 +12,9 @@ private newtype TIRType =
TIRBooleanType(int byteSize) { Language::hasBooleanType(byteSize) } or TIRBooleanType(int byteSize) { Language::hasBooleanType(byteSize) } or
TIRSignedIntegerType(int byteSize) { Language::hasSignedIntegerType(byteSize) } or TIRSignedIntegerType(int byteSize) { Language::hasSignedIntegerType(byteSize) } or
TIRUnsignedIntegerType(int byteSize) { Language::hasUnsignedIntegerType(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 TIRAddressType(int byteSize) { Language::hasAddressType(byteSize) } or
TIRFunctionAddressType(int byteSize) { Language::hasFunctionAddressType(byteSize) } or TIRFunctionAddressType(int byteSize) { Language::hasFunctionAddressType(byteSize) } or
TIROpaqueType(Language::OpaqueTypeTag tag, int byteSize) { TIROpaqueType(Language::OpaqueTypeTag tag, int byteSize) {
@@ -104,7 +106,7 @@ private class IRSizedType extends IRType {
this = TIRBooleanType(byteSize) or this = TIRBooleanType(byteSize) or
this = TIRSignedIntegerType(byteSize) or this = TIRSignedIntegerType(byteSize) or
this = TIRUnsignedIntegerType(byteSize) or this = TIRUnsignedIntegerType(byteSize) or
this = TIRFloatingPointType(byteSize) or this = TIRFloatingPointType(byteSize, _, _) or
this = TIRAddressType(byteSize) or this = TIRAddressType(byteSize) or
this = TIRFunctionAddressType(byteSize) or this = TIRFunctionAddressType(byteSize) or
this = TIROpaqueType(_, byteSize) this = TIROpaqueType(_, byteSize)
@@ -133,7 +135,7 @@ class IRNumericType extends IRSizedType {
IRNumericType() { IRNumericType() {
this = TIRSignedIntegerType(byteSize) or this = TIRSignedIntegerType(byteSize) or
this = TIRUnsignedIntegerType(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. * A floating-point type.
*/ */
class IRFloatingPointType extends IRNumericType, TIRFloatingPointType { 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() { final override Language::LanguageType getCanonicalLanguageType() {
result = Language::getCanonicalFloatingPointType(byteSize) result = Language::getCanonicalFloatingPointType(byteSize, base, domain)
} }
pragma[noinline] pragma[noinline]
final override int getByteSize() { result = byteSize } 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"
}
} }
/** /**

View File

@@ -101,23 +101,24 @@ class IRBlock extends IRBlockBase {
private predicate startsBasicBlock(Instruction instr) { private predicate startsBasicBlock(Instruction instr) {
not instr instanceof PhiInstruction and not instr instanceof PhiInstruction and
( not adjacentInBlock(_, instr)
count(Instruction predecessor | instr = predecessor.getASuccessor()) != 1 // Multiple predecessors or no predecessor }
or
exists(Instruction predecessor | /** Holds if `i2` follows `i1` in a `IRBlock`. */
instr = predecessor.getASuccessor() and private predicate adjacentInBlock(Instruction i1, Instruction i2) {
strictcount(Instruction other | other = predecessor.getASuccessor()) > 1 // - i2 must be the only successor of i1
) // Predecessor has multiple successors i2 = unique(Instruction i | i = i1.getASuccessor()) and
or // - i1 must be the only predecessor of i2
exists(Instruction predecessor, EdgeKind kind | i1 = unique(Instruction i | i.getASuccessor() = i2) and
instr = predecessor.getSuccessor(kind) and // - The edge between the two must be a GotoEdge. We just check that one
not kind instanceof GotoEdge // exists since we've already checked that it's unique.
) // Incoming edge is not a GotoEdge exists(GotoEdge edgeKind | exists(i1.getSuccessor(edgeKind))) and
or // - The edge must not be a back edge. This means we get the same back edges
exists(Instruction predecessor | // in the basic-block graph as we do in the raw CFG.
instr = Construction::getInstructionBackEdgeSuccessor(predecessor, _) not exists(Construction::getInstructionBackEdgeSuccessor(i1, _))
) // A back edge enters this instruction // 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) { private predicate isEntryBlock(TIRBlock block) {
@@ -129,12 +130,6 @@ private module Cached {
cached cached
newtype TIRBlock = MkIRBlock(Instruction firstInstr) { startsBasicBlock(firstInstr) } 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`. */ /** Holds if `i` is the `index`th instruction the block starting with `first`. */
private Instruction getInstructionFromFirst(Instruction first, int index) = private Instruction getInstructionFromFirst(Instruction first, int index) =
shortestDistances(startsBasicBlock/1, adjacentInBlock/2)(first, result, index) shortestDistances(startsBasicBlock/1, adjacentInBlock/2)(first, result, index)

View File

@@ -149,6 +149,26 @@ module InstructionSanity {
count(instr.getBlock().getAPredecessor()) < 2 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 * Holds if operand `operand` consumes a value that was defined in
* a different function. * a different function.

View File

@@ -190,14 +190,15 @@ class Instruction extends Construction::TInstruction {
final Language::Location getLocation() { result = getAST().getLocation() } 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() { final Language::Expr getConvertedResultExpression() {
result = Construction::getInstructionConvertedResultExpression(this) 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() { final Language::Expr getUnconvertedResultExpression() {
result = Construction::getInstructionUnconvertedResultExpression(this) result = Construction::getInstructionUnconvertedResultExpression(this)
@@ -525,7 +526,7 @@ class ReturnValueInstruction extends ReturnInstruction {
final Instruction getReturnValue() { result = getReturnValueOperand().getDef() } final Instruction getReturnValue() { result = getReturnValueOperand().getDef() }
} }
class ReturnIndirectionInstruction extends Instruction { class ReturnIndirectionInstruction extends VariableInstruction {
ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection }
final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } final SideEffectOperand getSideEffectOperand() { result = getAnOperand() }
@@ -535,6 +536,12 @@ class ReturnIndirectionInstruction extends Instruction {
final AddressOperand getSourceAddressOperand() { result = getAnOperand() } final AddressOperand getSourceAddressOperand() { result = getAnOperand() }
final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } 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 { class CopyInstruction extends Instruction {

View File

@@ -14,8 +14,7 @@ int getConstantValue(Instruction instr) {
or or
exists(PhiInstruction phi | exists(PhiInstruction phi |
phi = instr and phi = instr and
result = max(Operand op | op = phi.getAnInputOperand() | getConstantValue(op.getDef())) and result = unique(Operand op | op = phi.getAnInputOperand() | getConstantValue(op.getDef()))
result = min(Operand op | op = phi.getAnInputOperand() | getConstantValue(op.getDef()))
) )
} }

View File

@@ -6,11 +6,12 @@ private import DebugSSA
bindingset[offset] bindingset[offset]
private string getKeySuffixForOffset(int offset) { private string getKeySuffixForOffset(int offset) {
offset >= 0 and
if offset % 2 = 0 then result = "" else result = "_Chi" if offset % 2 = 0 then result = "" else result = "_Chi"
} }
bindingset[offset] 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 * Property provide that dumps the memory access of each result. Useful for debugging SSA

View File

@@ -101,23 +101,24 @@ class IRBlock extends IRBlockBase {
private predicate startsBasicBlock(Instruction instr) { private predicate startsBasicBlock(Instruction instr) {
not instr instanceof PhiInstruction and not instr instanceof PhiInstruction and
( not adjacentInBlock(_, instr)
count(Instruction predecessor | instr = predecessor.getASuccessor()) != 1 // Multiple predecessors or no predecessor }
or
exists(Instruction predecessor | /** Holds if `i2` follows `i1` in a `IRBlock`. */
instr = predecessor.getASuccessor() and private predicate adjacentInBlock(Instruction i1, Instruction i2) {
strictcount(Instruction other | other = predecessor.getASuccessor()) > 1 // - i2 must be the only successor of i1
) // Predecessor has multiple successors i2 = unique(Instruction i | i = i1.getASuccessor()) and
or // - i1 must be the only predecessor of i2
exists(Instruction predecessor, EdgeKind kind | i1 = unique(Instruction i | i.getASuccessor() = i2) and
instr = predecessor.getSuccessor(kind) and // - The edge between the two must be a GotoEdge. We just check that one
not kind instanceof GotoEdge // exists since we've already checked that it's unique.
) // Incoming edge is not a GotoEdge exists(GotoEdge edgeKind | exists(i1.getSuccessor(edgeKind))) and
or // - The edge must not be a back edge. This means we get the same back edges
exists(Instruction predecessor | // in the basic-block graph as we do in the raw CFG.
instr = Construction::getInstructionBackEdgeSuccessor(predecessor, _) not exists(Construction::getInstructionBackEdgeSuccessor(i1, _))
) // A back edge enters this instruction // 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) { private predicate isEntryBlock(TIRBlock block) {
@@ -129,12 +130,6 @@ private module Cached {
cached cached
newtype TIRBlock = MkIRBlock(Instruction firstInstr) { startsBasicBlock(firstInstr) } 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`. */ /** Holds if `i` is the `index`th instruction the block starting with `first`. */
private Instruction getInstructionFromFirst(Instruction first, int index) = private Instruction getInstructionFromFirst(Instruction first, int index) =
shortestDistances(startsBasicBlock/1, adjacentInBlock/2)(first, result, index) shortestDistances(startsBasicBlock/1, adjacentInBlock/2)(first, result, index)

View File

@@ -149,6 +149,26 @@ module InstructionSanity {
count(instr.getBlock().getAPredecessor()) < 2 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 * Holds if operand `operand` consumes a value that was defined in
* a different function. * a different function.

View File

@@ -190,14 +190,15 @@ class Instruction extends Construction::TInstruction {
final Language::Location getLocation() { result = getAST().getLocation() } 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() { final Language::Expr getConvertedResultExpression() {
result = Construction::getInstructionConvertedResultExpression(this) 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() { final Language::Expr getUnconvertedResultExpression() {
result = Construction::getInstructionUnconvertedResultExpression(this) result = Construction::getInstructionUnconvertedResultExpression(this)
@@ -525,7 +526,7 @@ class ReturnValueInstruction extends ReturnInstruction {
final Instruction getReturnValue() { result = getReturnValueOperand().getDef() } final Instruction getReturnValue() { result = getReturnValueOperand().getDef() }
} }
class ReturnIndirectionInstruction extends Instruction { class ReturnIndirectionInstruction extends VariableInstruction {
ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection } ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection }
final SideEffectOperand getSideEffectOperand() { result = getAnOperand() } final SideEffectOperand getSideEffectOperand() { result = getAnOperand() }
@@ -535,6 +536,12 @@ class ReturnIndirectionInstruction extends Instruction {
final AddressOperand getSourceAddressOperand() { result = getAnOperand() } final AddressOperand getSourceAddressOperand() { result = getAnOperand() }
final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() } 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 { class CopyInstruction extends Instruction {

Some files were not shown because too many files have changed in this diff Show More