mirror of
https://github.com/github/codeql.git
synced 2026-05-21 14:47:10 +02:00
Merge remote-tracking branch 'origin/master' into HEAD
This commit is contained in:
9
.devcontainer/devcontainer.json
Normal file
9
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extensions": [
|
||||
"github.vscode-codeql",
|
||||
"slevesque.vscode-zipexplorer"
|
||||
],
|
||||
"settings": {
|
||||
"codeQL.experimentalBqrsParsing": true
|
||||
}
|
||||
}
|
||||
4
.github/codeql/codeql-config.yml
vendored
Normal file
4
.github/codeql/codeql-config.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
name: "CodeQL config"
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
52
.github/workflows/codeql-analysis.yml
vendored
Normal file
52
.github/workflows/codeql-analysis.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
name: "Code scanning - action"
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
schedule:
|
||||
- cron: '0 9 * * 1'
|
||||
|
||||
jobs:
|
||||
CodeQL-Build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
|
||||
# If this run was triggered by a pull request event, then checkout
|
||||
# the head of the pull request instead of the merge commit.
|
||||
- run: git checkout HEAD^2
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
with:
|
||||
languages: csharp
|
||||
config-file: ./.github/codeql/codeql-config.yml
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -21,4 +21,3 @@
|
||||
/codeql/
|
||||
|
||||
csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json
|
||||
.vscode
|
||||
|
||||
1
.vscode/.gitattributes
vendored
Normal file
1
.vscode/.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.json linguist-language=JSON-with-Comments
|
||||
10
.vscode/extensions.json
vendored
Normal file
10
.vscode/extensions.json
vendored
Normal 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
27
.vscode/tasks.json
vendored
Normal 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": []
|
||||
}
|
||||
]
|
||||
}
|
||||
19
CODEOWNERS
19
CODEOWNERS
@@ -1,11 +1,20 @@
|
||||
/cpp/ @Semmle/cpp-analysis
|
||||
/csharp/ @Semmle/cs
|
||||
/java/ @Semmle/java
|
||||
/javascript/ @Semmle/js
|
||||
/python/ @Semmle/python
|
||||
/cpp/ @github/codeql-c-analysis
|
||||
/csharp/ @github/codeql-csharp
|
||||
/java/ @github/codeql-java
|
||||
/javascript/ @github/codeql-javascript
|
||||
/python/ @github/codeql-python
|
||||
|
||||
# Assign query help for docs review
|
||||
/cpp/**/*.qhelp @hubwriter
|
||||
/csharp/**/*.qhelp @jf205
|
||||
/java/**/*.qhelp @felicitymay
|
||||
/javascript/**/*.qhelp @mchammer01
|
||||
/python/**/*.qhelp @felicitymay
|
||||
/docs/language/ @shati-patel @jf205
|
||||
|
||||
# Exclude help for experimental queries from docs review
|
||||
/cpp/**/experimental/**/*.qhelp @github/codeql-c-analysis
|
||||
/csharp/**/experimental/**/*.qhelp @github/codeql-csharp
|
||||
/java/**/experimental/**/*.qhelp @github/codeql-java
|
||||
/javascript/**/experimental/**/*.qhelp @github/codeql-javascript
|
||||
/python/**/experimental/**/*.qhelp @github/codeql-python
|
||||
|
||||
@@ -1,39 +1,126 @@
|
||||
# Code of Conduct
|
||||
## Our Pledge
|
||||
|
||||
This code of conduct outlines expectations for participation in the Semmle open source community, including any open source repositories on GitHub.com, as well as steps for reporting unacceptable behavior. We are committed to providing a welcoming and inspiring community for all.
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
People violating this code of conduct may be banned from the community.
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
Our community strives to:
|
||||
* Be friendly and patient: Remember you might not be communicating in someone else’s primary spoken or programming language, and others may not have your level of understanding.
|
||||
* Be welcoming: Our community welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, color, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability.
|
||||
* Be respectful: We are a world-wide community of professionals, and we conduct ourselves professionally. Disagreement is no excuse for poor behavior and poor manners. Disrespectful and unacceptable behavior includes, but is not limited to:
|
||||
* Violent threats or language.
|
||||
* Discriminatory or derogatory jokes and language.
|
||||
* Posting sexually explicit or violent material.
|
||||
* Posting, or threatening to post, people’s personally identifying information (“doxing”).
|
||||
* Insults, especially those using discriminatory terms or slurs.
|
||||
* Behavior that could be perceived as sexual attention.
|
||||
* Advocating for or encouraging any of the above behaviors.
|
||||
* Understand disagreements: Disagreements, both social and technical, are useful learning opportunities. Seek to understand others’ viewpoints and resolve differences constructively.
|
||||
## Our Standards
|
||||
|
||||
This code is not exhaustive or complete. It serves to capture our common understanding of a productive, collaborative environment. We expect the code to be followed in spirit as much as in the letter.
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
# Scope
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
This code of conduct applies to all repositories and communities for Semmle open source projects, regardless of whether or not the repository explicitly calls out its use of this code. The code also applies in public spaces when an individual is representing the Semmle open source community. Examples include using an official project email address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
# Reporting Code of Conduct Issues
|
||||
We encourage members of the community to resolve issues on their own whenever possible. This builds a broader and deeper understanding and ultimately a healthier interaction. In the event that an issue cannot be resolved locally, please feel free to report your concerns by contacting code-of-conduct@semmle.com.
|
||||
In your report please include:
|
||||
* Your contact information.
|
||||
* Names (real, usernames or pseudonyms) of any individuals involved. If there are additional witnesses, please include them as well.
|
||||
* Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly available record (e.g. a mailing list archive or a public chat log), please include a link or attachment.
|
||||
* Any additional information that may be helpful.
|
||||
## Enforcement Responsibilities
|
||||
|
||||
All reports will be reviewed by a multi-person team and will result in a response that is deemed necessary and appropriate to the circumstances. Where additional perspectives are needed, the team may seek insight from others with relevant expertise or experience. The confidentiality of the person reporting the incident will be kept at all times. Involved parties are never part of the review team.
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Anyone asked to stop unacceptable behavior is expected to comply immediately. If an individual engages in unacceptable behavior, the review team may take any action they deem appropriate, including a permanent ban from the community.
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
*This text is licensed under the [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/) license. It is based on a template established by the [TODO Group](http://todogroup.org/) and variants thereof used by numerous other large communities (e.g., [Microsoft](https://microsoft.github.io/codeofconduct/), [Facebook](https://code.fb.com/codeofconduct/), [Yahoo](https://yahoo.github.io/codeofconduct), [Twitter](https://github.com/twitter/code-of-conduct), [GitHub](https://blog.github.com/2015-07-20-adopting-the-open-code-of-conduct/)) and the Scope section from the [Contributor Covenant version 1.4](http://contributor-covenant.org/version/1/4/).*
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
opensource@github.com.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
We welcome contributions to our CodeQL libraries and queries. Got an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request! Contributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [project's open source license](LICENSE).
|
||||
|
||||
There is lots of useful documentation to help you write queries, ranging from information about query file structure to tutorials for specific target languages. For more information on the documentation available, see [Writing CodeQL queries](https://help.semmle.com/QL/learn-ql/writing-queries/writing-queries.html) on [help.semmle.com](https://help.semmle.com).
|
||||
There is lots of useful documentation to help you write queries, ranging from information about query file structure to tutorials for specific target languages. For more information on the documentation available, see [CodeQL queries](https://help.semmle.com/QL/learn-ql/writing-queries/writing-queries.html) on [help.semmle.com](https://help.semmle.com).
|
||||
|
||||
|
||||
## Submitting a new experimental query
|
||||
@@ -20,7 +20,7 @@ If you have an idea for a query that you would like to share with other CodeQL u
|
||||
* Python: `python/ql/src`
|
||||
|
||||
Each language-specific directory contains further subdirectories that group queries based on their `@tags` or purpose.
|
||||
- Experimental queries and libraries are stored in the `experimental` subdirectory within each language-specific directory in the [CodeQL repository](https://github.com/Semmle/ql). For example, experimental Java queries and libraries are stored in `java/ql/src/experimental` and any corresponding tests in `java/ql/test/experimental`.
|
||||
- Experimental queries and libraries are stored in the `experimental` subdirectory within each language-specific directory in the [CodeQL repository](https://github.com/github/codeql). For example, experimental Java queries and libraries are stored in `java/ql/src/experimental` and any corresponding tests in `java/ql/test/experimental`.
|
||||
- The structure of an `experimental` subdirectory mirrors the structure of its parent directory.
|
||||
- Select or create an appropriate directory in `experimental` based on the existing directory structure of `experimental` or its parent directory.
|
||||
|
||||
@@ -32,11 +32,11 @@ If you have an idea for a query that you would like to share with other CodeQL u
|
||||
|
||||
For details, see the [guide on query metadata](docs/query-metadata-style-guide.md).
|
||||
|
||||
Make sure the `select` statement is compatible with the query `@kind`. See [Introduction to query files](https://help.semmle.com/QL/learn-ql/writing-queries/introduction-to-queries.html#select-clause) on help.semmle.com.
|
||||
Make sure the `select` statement is compatible with the query `@kind`. See [About CodeQL queries](https://help.semmle.com/QL/learn-ql/writing-queries/introduction-to-queries.html#select-clause) on help.semmle.com.
|
||||
|
||||
3. **Formatting**
|
||||
|
||||
- The queries and libraries must be [autoformatted](https://help.semmle.com/codeql/codeql-for-vscode/reference/editor.html#autoformatting).
|
||||
- The queries and libraries must be autoformatted, for example using the "Format Document" command in [CodeQL for Visual Studio Code](https://help.semmle.com/codeql/codeql-for-vscode/procedures/about-codeql-for-vscode.html).
|
||||
|
||||
4. **Compilation**
|
||||
|
||||
@@ -53,14 +53,6 @@ After the experimental query is merged, we welcome pull requests to improve it.
|
||||
|
||||
## Using your personal data
|
||||
|
||||
If you contribute to this project, we will record your name and email
|
||||
address (as provided by you with your contributions) as part of the code
|
||||
repositories, which are public. We might also use this information
|
||||
to contact you in relation to your contributions, as well as in the
|
||||
normal course of software development. We also store records of your
|
||||
CLA agreements. Under GDPR legislation, we do this
|
||||
on the basis of our legitimate interest in creating the CodeQL product.
|
||||
|
||||
Please do get in touch (privacy@github.com) if you have any questions about
|
||||
this or our data protection policies.
|
||||
If you contribute to this project, we will record your name and email address (as provided by you with your contributions) as part of the code repositories, which are public. We might also use this information to contact you in relation to your contributions, as well as in the normal course of software development. We also store records of CLA agreements signed in the past, but no longer require contributors to sign a CLA. Under GDPR legislation, we do this on the basis of our legitimate interest in creating the CodeQL product.
|
||||
|
||||
Please do get in touch (privacy@github.com) if you have any questions about this or our data protection policies.
|
||||
|
||||
18
README.md
18
README.md
@@ -1,6 +1,6 @@
|
||||
# CodeQL
|
||||
|
||||
This open source repository contains the standard CodeQL libraries and queries that power [LGTM](https://lgtm.com) and the other CodeQL products that [GitHub](https://github.com) makes available to its customers worldwide.
|
||||
This open source repository contains the standard CodeQL libraries and queries that power [LGTM](https://lgtm.com) and the other CodeQL products that [GitHub](https://github.com) makes available to its customers worldwide. For the queries, libraries, and extractor that power Go analysis, visit the [CodeQL for Go repository](https://github.com/github/codeql-go).
|
||||
|
||||
## How do I learn CodeQL and run queries?
|
||||
|
||||
@@ -9,8 +9,20 @@ You can use the [interactive query console](https://lgtm.com/help/lgtm/using-que
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions to our standard library and standard checks. Do you have an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request! Before you do, though, please take the time to read our [contributing guidelines](CONTRIBUTING.md). You can also consult our [style guides](https://github.com/Semmle/ql/tree/master/docs) to learn how to format your code for consistency and clarity, how to write query metadata, and how to write query help documentation for your query.
|
||||
We welcome contributions to our standard library and standard checks. Do you have an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request! Before you do, though, please take the time to read our [contributing guidelines](CONTRIBUTING.md). You can also consult our [style guides](https://github.com/github/codeql/tree/master/docs) to learn how to format your code for consistency and clarity, how to write query metadata, and how to write query help documentation for your query.
|
||||
|
||||
## License
|
||||
|
||||
The code in this repository is licensed under [Apache License 2.0](LICENSE) by [GitHub](https://github.com).
|
||||
The code in this repository is licensed under the [MIT License](LICENSE) by [GitHub](https://github.com).
|
||||
|
||||
## Visual Studio Code integration
|
||||
|
||||
If you use Visual Studio Code to work in this repository, there are a few integration features to make development easier.
|
||||
|
||||
### CodeQL for Visual Studio Code
|
||||
|
||||
You can install the [CodeQL for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-codeql) extension to get syntax highlighting, IntelliSense, and code navigation for the QL language, as well as unit test support for testing CodeQL libraries and queries.
|
||||
|
||||
### Tasks
|
||||
|
||||
The `.vscode/tasks.json` file defines custom tasks specific to working in this repository. To invoke one of these tasks, select the `Terminal | Run Task...` menu option, and then select the desired task from the dropdown. You can also invoke the `Tasks: Run Task` command from the command palette.
|
||||
|
||||
@@ -4,6 +4,8 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
|
||||
|
||||
## General improvements
|
||||
|
||||
You can now suppress alerts using either single-line block comments (`/* ... */`) or line comments (`// ...`).
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
@@ -12,46 +14,71 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
A new taint-tracking library is used by all the security queries that track tainted values
|
||||
(`cpp/path-injection`, `cpp/cgi-xss`, `cpp/sql-injection`, `cpp/uncontrolled-process-operation`,
|
||||
`cpp/unbounded-write`, `cpp/tainted-format-string`, `cpp/tainted-format-string-through-global`,
|
||||
`cpp/uncontrolled-arithmetic`, `cpp/uncontrolled-allocation-size`, `cpp/user-controlled-bypass`,
|
||||
`cpp/cleartext-storage-buffer`, `cpp/tainted-permissions-check`).
|
||||
These queries now have more precise results and also offer _path explanations_ so you can explore the results easily.
|
||||
There is a performance cost to this, and the LGTM query suite will overall run slower than before.
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|----------------------------|------------------------|------------------------------------------------------------------|
|
||||
| Boost\_asio TLS Settings Misconfiguration (`cpp/boost/tls-settings-misconfiguration`) | Query id change | The identifier was updated to use dashes in place of underscores (previous identifier `cpp/boost/tls_settings_misconfiguration`). |
|
||||
| Buffer not sufficient for string (`cpp/overflow-calculated`) | More true positive results | This query now identifies a wider variety of buffer allocations using the `semmle.code.cpp.models.interfaces.Allocation` library. |
|
||||
| No space for zero terminator (`cpp/no-space-for-terminator`) | More true positive results | This query now identifies a wider variety of buffer allocations using the `semmle.code.cpp.models.interfaces.Allocation` library. |
|
||||
| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | | This query is no longer run on LGTM. |
|
||||
| Memory is never freed (`cpp/memory-never-freed`) | More true positive results | This query now identifies a wider variety of buffer allocations using the `semmle.code.cpp.models.interfaces.Allocation` library. |
|
||||
| Memory may not be freed (`cpp/memory-may-not-be-freed`) | More true positive results | This query now identifies a wider variety of buffer allocations using the `semmle.code.cpp.models.interfaces.Allocation` library. |
|
||||
| Mismatching new/free or malloc/delete (`cpp/new-free-mismatch`) | Fewer false positive results | Fixed false positive results in template code. |
|
||||
| Missing return statement (`cpp/missing-return`) | Fewer false positive results | Functions containing `asm` statements are no longer highlighted by this query. |
|
||||
| Missing return statement (`cpp/missing-return`) | More accurate locations | Locations reported by this query are now more accurate in some cases. |
|
||||
| No space for zero terminator (`cpp/no-space-for-terminator`) | More correct results | String arguments to formatting functions are now (usually) expected to be null terminated strings. |
|
||||
| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | | This query is no longer run on LGTM. |
|
||||
| No space for zero terminator (`cpp/no-space-for-terminator`) | Fewer false positive results | This query has been modified to be more conservative when identifying which pointers point to null-terminated strings. This approach produces fewer, more accurate results. |
|
||||
| Overflow in uncontrolled allocation size (`cpp/uncontrolled-allocation-size`) | Fewer false positive results | Cases where the tainted allocation size is range checked are now more reliably excluded. |
|
||||
| Mismatching new/free or malloc/delete (`cpp/new-free-mismatch`) | Fewer false positive results | Improved handling of template code gives greater precision. |
|
||||
| Missing return statement (`cpp/missing-return`) | Fewer false positive results and more accurate locations | Functions containing `asm` statements are no longer highlighted by this query. The locations reported by this query are now more accurate in some cases. |
|
||||
| No space for zero terminator (`cpp/no-space-for-terminator`) | More results with greater precision | The query gives more precise results for a wider variety of buffer allocations. String arguments to formatting functions are now (usually) expected to be null terminated strings. Use of the `semmle.code.cpp.models.interfaces.Allocation` library identifies problems with a wider variety of buffer allocations. This query is also more conservative when identifying which pointers point to null-terminated strings. |
|
||||
| Overflow in uncontrolled allocation size (`cpp/uncontrolled-allocation-size`) | Fewer false positive results | The query now produces fewer, more accurate results. Cases where the tainted allocation size is range checked are more reliably excluded. |
|
||||
| Overloaded assignment does not return 'this' (`cpp/assignment-does-not-return-this`) | Fewer false positive results | This query no longer reports incorrect results in template classes. |
|
||||
| Pointer overflow check (`cpp/pointer-overflow-check`),<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. |
|
||||
| Unsigned comparison to zero (`cpp/unsigned-comparison-zero`) | More correct results | This query now also looks for comparisons of the form `0 <= x`. |
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
* The data-flow library has been improved, which affects and improves some security queries. The improvements are:
|
||||
- Track flow through functions that combine taint tracking with flow through fields.
|
||||
- Track flow through clone-like functions, that is, functions that read contents of a field from a
|
||||
parameter and stores the value in the field of a returned object.
|
||||
* Created the `semmle.code.cpp.models.interfaces.Allocation` library to model allocation such as `new` expressions and calls to `malloc`. This in intended to replace the functionality in `semmle.code.cpp.commons.Alloc` with a more consistent and useful interface.
|
||||
* Created the `semmle.code.cpp.models.interfaces.Deallocation` library to model deallocation such as `delete` expressions and calls to `free`. This in intended to replace the functionality in `semmle.code.cpp.commons.Alloc` with a more consistent and useful interface.
|
||||
* The built-in C++20 "spaceship operator" (`<=>`) is now supported via the QL
|
||||
class `SpaceshipExpr`. Overloaded forms are modeled as calls to functions
|
||||
named `operator<=>`.
|
||||
* The data-flow library (`semmle.code.cpp.dataflow.DataFlow` and
|
||||
`semmle.code.cpp.dataflow.TaintTracking`) has been improved, which affects
|
||||
and improves some security queries. The improvements are:
|
||||
- Track flow through functions that combine taint tracking with flow through fields.
|
||||
- Track flow through clone-like functions, that is, functions that read contents of a field from a
|
||||
parameter and stores the value in the field of a returned object.
|
||||
* The security pack taint tracking library
|
||||
(`semmle.code.cpp.security.TaintTracking`) uses a new intermediate
|
||||
representation. This provides a more precise analysis of flow through
|
||||
parameters and pointers. For new queries, however, we continue to recommend
|
||||
using `semmle.code.cpp.dataflow.TaintTracking`.
|
||||
* The global value numbering library
|
||||
(`semmle.code.cpp.valuenumbering.GlobalValueNumbering`) uses a new
|
||||
intermediate representation to provide a more precise analysis of
|
||||
heap-allocated memory and pointers to stack variables.
|
||||
* New libraries have been created to provide a more consistent and useful interface
|
||||
for modeling allocation and deallocation. These replace the old
|
||||
`semmle.code.cpp.commons.Alloc` library.
|
||||
* The new `semmle.code.cpp.models.interfaces.Allocation` library models
|
||||
allocations, such as `new` expressions and calls to `malloc`.
|
||||
* The new `semmle.code.cpp.models.interfaces.Deallocation` library
|
||||
models deallocations, such as `delete` expressions and calls to `free`.
|
||||
* The predicate `freeCall` in `semmle.code.cpp.commons.Alloc` has been
|
||||
deprecated. The `Allocation` and `Deallocation` models in
|
||||
`semmle.code.cpp.models.interfaces` should be used instead.
|
||||
* The new class `StackVariable` should be used in place of `LocalScopeVariable`
|
||||
in most cases. The difference is that `StackVariable` does not include
|
||||
variables declared with `static` or `thread_local`.
|
||||
* As a rule of thumb, custom queries about the _values_ of variables should
|
||||
be changed from `LocalScopeVariable` to `StackVariable`, while queries
|
||||
about the _name or scope_ of variables should remain unchanged.
|
||||
* The `LocalScopeVariableReachability` library is deprecated in favor of
|
||||
`StackVariableReachability`. The functionality is the same.
|
||||
* The models library models `strlen` in more detail, and includes common variations such as `wcslen`.
|
||||
* The models library models `gets` and similar functions.
|
||||
* The models library now partially models `std::string`.
|
||||
* The taint tracking library (`semmle.code.cpp.dataflow.TaintTracking`) has had
|
||||
the following improvements:
|
||||
* The library now models data flow through `strdup` and similar functions.
|
||||
* The library now models data flow through formatting functions such as `sprintf`.
|
||||
* The security pack taint tracking library (`semmle.code.cpp.security.TaintTracking`) uses a new intermediate representation. This provides a more precise analysis of pointers to stack variables and flow through parameters, improving the results of many security queries.
|
||||
* The global value numbering library (`semmle.code.cpp.valuenumbering.GlobalValueNumbering`) uses a new intermediate representation to provide a more precise analysis of heap allocated memory and pointers to stack variables.
|
||||
* `freeCall` in `semmle.code.cpp.commons.Alloc` has been deprecated. The`Allocation` and `Deallocation` models in `semmle.code.cpp.models.interfaces` should be used instead.
|
||||
* As a rule of thumb, custom queries about the _values_ of variables should
|
||||
be changed from `LocalScopeVariable` to `StackVariable`, while queries
|
||||
about the _name or scope_ of variables should remain unchanged.
|
||||
* The `LocalScopeVariableReachability` library is deprecated in favor of
|
||||
`StackVariableReachability`. The functionality is the same.
|
||||
* Taint tracking and data flow now features better modeling of commonly-used
|
||||
library functions:
|
||||
* `gets` and similar functions,
|
||||
* the most common operations on `std::string`,
|
||||
* `strdup` and similar functions, and
|
||||
* formatting functions such as `sprintf`.
|
||||
|
||||
@@ -2,30 +2,31 @@
|
||||
|
||||
The following changes in version 1.24 affect C# analysis in all applications.
|
||||
|
||||
## General improvements
|
||||
|
||||
You can now suppress alerts using either single-line block comments (`/* ... */`) or line comments (`// ...`).
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
| Assembly path injection (`cs/assembly-path-injection`) | security, external/cwe/cwe-114 | Finds user-controlled data used to load an assembly. |
|
||||
| Insecure configuration for ASP.NET requestValidationMode (`cs/insecure-request-validation-mode`) | security, external/cwe/cwe-016 | Finds where this attribute has been set to a value less than 4.5, which turns off some validation features and makes the application less secure. |
|
||||
| Insecure SQL connection (`cs/insecure-sql-connection`) | security, external/cwe/cwe-327 | Finds unencrypted SQL connection strings. |
|
||||
| Page request validation is disabled (`cs/web/request-validation-disabled`) | security, frameworks/asp.net, external/cwe/cwe-016 | Finds where ASP.NET page request validation has been disabled, which could make the application less secure. |
|
||||
| Serialization check bypass (`cs/serialization-check-bypass`) | security, external/cwe/cwe-20 | Finds where data is not validated in a deserialization method. |
|
||||
| XML injection (`cs/xml-injection`) | security, external/cwe/cwe-091 | Finds user-controlled data that is used to write directly to an XML document. |
|
||||
| Assembly path injection (`cs/assembly-path-injection`) | security, external/cwe/cwe-114 | Finds user-controlled data used to load an assembly. Results are shown on LGTM by default. |
|
||||
| Insecure configuration for ASP.NET requestValidationMode (`cs/insecure-request-validation-mode`) | security, external/cwe/cwe-016 | Finds where this attribute has been set to a value less than 4.5, which turns off some validation features and makes the application less secure. By default, the query is not run on LGTM. |
|
||||
| Insecure SQL connection (`cs/insecure-sql-connection`) | security, external/cwe/cwe-327 | Finds unencrypted SQL connection strings. Results are not shown on LGTM by default. |
|
||||
| Page request validation is disabled (`cs/web/request-validation-disabled`) | security, frameworks/asp.net, external/cwe/cwe-016 | Finds where ASP.NET page request validation has been disabled, which could make the application less secure. By default, the query is not run on LGTM. |
|
||||
| Serialization check bypass (`cs/serialization-check-bypass`) | security, external/cwe/cwe-20 | Finds where data is not validated in a deserialization method. Results are not shown on LGTM by default. |
|
||||
| XML injection (`cs/xml-injection`) | security, external/cwe/cwe-091 | Finds user-controlled data that is used to write directly to an XML document. Results are shown on LGTM by default. |
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|------------------------------|------------------------|-----------------------------------|
|
||||
| Useless assignment to local variable (`cs/useless-assignment-to-local`) | Fewer false positive results | Results have been removed when the variable is named `_` in a `foreach` statement. |
|
||||
| Potentially dangerous use of non-short-circuit logic (`cs/non-short-circuit`) | Fewer false positive results | Results have been removed when the expression contains an `out` parameter. |
|
||||
| Dereferenced variable may be null (`cs/dereferenced-value-may-be-null`) | More results | Results are reported from parameters with a default value of `null`. |
|
||||
| Useless assignment to local variable (`cs/useless-assignment-to-local`) | Fewer false positive results | Results have been removed when the value assigned is an (implicitly or explicitly) cast default-like value. For example, `var s = (string)null` and `string s = default`. |
|
||||
| XPath injection (`cs/xml/xpath-injection`) | More results | The query now recognizes calls to methods on `System.Xml.XPath.XPathNavigator` objects. |
|
||||
| Information exposure through transmitted data (`cs/sensitive-data-transmission`) | More results | The query now recognizes writes to cookies and writes to ASP.NET (`Inner`)`Text` properties as additional sinks. |
|
||||
| Information exposure through an exception (`cs/information-exposure-through-exception`) | More results | The query now recognizes writes to cookies, writes to ASP.NET (`Inner`)`Text` properties, and email contents as additional sinks. |
|
||||
|
||||
## Removal of old queries
|
||||
| Information exposure through transmitted data (`cs/sensitive-data-transmission`) | More results | The query now recognizes writes to cookies and writes to ASP.NET (`Inner`)`Text` properties as additional sinks. |
|
||||
| Potentially dangerous use of non-short-circuit logic (`cs/non-short-circuit`) | Fewer false positive results | Results have been removed when the expression contains an `out` parameter. |
|
||||
| Useless assignment to local variable (`cs/useless-assignment-to-local`) | Fewer false positive results | Results have been removed when the value assigned is an (implicitly or explicitly) cast default-like value. For example, `var s = (string)null` and `string s = default`. Results have also been removed when the variable is named `_` in a `foreach` statement. |
|
||||
| XPath injection (`cs/xml/xpath-injection`) | More results | The query now recognizes calls to methods on `System.Xml.XPath.XPathNavigator` objects. |
|
||||
|
||||
## Changes to code extraction
|
||||
|
||||
@@ -37,13 +38,11 @@ The following changes in version 1.24 affect C# analysis in all applications.
|
||||
## Changes to libraries
|
||||
|
||||
* The data-flow library has been improved, which affects and improves most security queries. The improvements are:
|
||||
- Track flow through methods that combine taint tracking with flow through fields.
|
||||
- Track flow through clone-like methods, that is, methods that read contents of a field from a
|
||||
parameter and stores the value in the field of a returned object.
|
||||
- Track flow through methods that combine taint tracking with flow through fields.
|
||||
- Track flow through clone-like methods, that is, methods that read the contents of a field from a
|
||||
parameter and store the value in the field of a returned object.
|
||||
* The taint tracking library now tracks flow through (implicit or explicit) conversion operator calls.
|
||||
* [Code contracts](https://docs.microsoft.com/en-us/dotnet/framework/debug-trace-profile/code-contracts) are now recognized, and are treated like any other assertion methods.
|
||||
* Expression nullability flow state is given by the predicates `Expr.hasNotNullFlowState()` and `Expr.hasMaybeNullFlowState()`.
|
||||
* `stackalloc` array creations are now represented by the QL class `Stackalloc`. Previously they were represented by the class `ArrayCreation`.
|
||||
* A new class `RemoteFlowSink` has been added to model sinks where data might be exposed to external users. Examples include web page output, e-mails, and cookies.
|
||||
|
||||
## Changes to autobuilder
|
||||
* A new class `RemoteFlowSink` has been added to model sinks where data might be exposed to external users. Examples include web page output, emails, and cookies.
|
||||
|
||||
@@ -4,7 +4,7 @@ The following changes in version 1.24 affect Java analysis in all applications.
|
||||
|
||||
## General improvements
|
||||
|
||||
* Alert suppression can now be done with single-line block comments (`/* ... */`) as well as line comments (`// ...`).
|
||||
* You can now suppress alerts using either single-line block comments (`/* ... */`) or line comments (`// ...`).
|
||||
* A `Customizations.qll` file has been added to allow customizations of the standard library that apply to all queries.
|
||||
|
||||
## New queries
|
||||
@@ -21,16 +21,16 @@ The following changes in version 1.24 affect Java analysis in all applications.
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|------------------------------|------------------------|-----------------------------------|
|
||||
| Dereferenced variable may be null (`java/dereferenced-value-may-be-null`) | Fewer false positives | Final fields with a non-null initializer are no longer reported. |
|
||||
| Expression always evaluates to the same value (`java/evaluation-to-constant`) | Fewer false positives | Expressions of the form `0 * x` are usually intended and no longer reported. Also left shift of ints by 32 bits and longs by 64 bits are no longer reported as they are not constant, these results are instead reported by the new query `java/lshift-larger-than-type-width`. |
|
||||
| Useless null check (`java/useless-null-check`) | More true positives | Useless checks on final fields with a non-null initializer are now reported. |
|
||||
| Dereferenced variable may be null (`java/dereferenced-value-may-be-null`) | Fewer false positive results | Final fields with a non-null initializer are no longer reported. |
|
||||
| Expression always evaluates to the same value (`java/evaluation-to-constant`) | Fewer false positive results | Expressions of the form `0 * x` are usually intended and no longer reported. Also left shift of ints by 32 bits and longs by 64 bits are no longer reported as they are not constant, these results are instead reported by the new query `java/lshift-larger-than-type-width`. |
|
||||
| Useless null check (`java/useless-null-check`) | More true positive results | Useless checks on final fields with a non-null initializer are now reported. |
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
* The data-flow library has been improved, which affects and improves most security queries. The improvements are:
|
||||
- Track flow through methods that combine taint tracking with flow through fields.
|
||||
- Track flow through clone-like methods, that is, methods that read contents of a field from a
|
||||
parameter and stores the value in the field of a returned object.
|
||||
- Track flow through methods that combine taint tracking with flow through fields.
|
||||
- Track flow through clone-like methods, that is, methods that read contents of a field from a
|
||||
parameter and stores the value in the field of a returned object.
|
||||
* Identification of test classes has been improved. Previously, one of the
|
||||
match conditions would classify any class with a name containing the string
|
||||
"Test" as a test class, but now this matching has been replaced with one that
|
||||
@@ -38,6 +38,6 @@ The following changes in version 1.24 affect Java analysis in all applications.
|
||||
general file classification mechanism and thus suppression of alerts, and
|
||||
also any security queries using taint tracking, as test classes act as
|
||||
default barriers stopping taint flow.
|
||||
* Parentheses are now no longer modelled directly in the AST, that is, the
|
||||
* Parentheses are now no longer modeled directly in the AST, that is, the
|
||||
`ParExpr` class is empty. Instead, a parenthesized expression can be
|
||||
identified with the `Expr.isParenthesized()` member predicate.
|
||||
|
||||
@@ -4,67 +4,68 @@
|
||||
|
||||
* TypeScript 3.8 is now supported.
|
||||
|
||||
* Alert suppression can now be done with single-line block comments (`/* ... */`) as well as line comments (`// ...`).
|
||||
* You can now suppress alerts using either single-line block comments (`/* ... */`) or line comments (`// ...`).
|
||||
|
||||
* Resolution of imports has improved, leading to more results from the security queries:
|
||||
- Imports with the `.js` extension can now be resolved to a TypeScript file,
|
||||
when the import refers to a file generated by TypeScript.
|
||||
- Imports that rely on path-mappings from a `tsconfig.json` file can now be resolved.
|
||||
- Export declarations of the form `export * as ns from "x"` are now analyzed more precisely.
|
||||
- Imports with the `.js` extension can now be resolved to a TypeScript file,
|
||||
when the import refers to a file generated by TypeScript.
|
||||
- Imports that rely on path-mappings from a `tsconfig.json` file can now be resolved.
|
||||
- Export declarations of the form `export * as ns from "x"` are now analyzed more precisely.
|
||||
|
||||
* The analysis of sanitizers has improved, leading to more accurate results from the security queries.
|
||||
In particular:
|
||||
- Sanitizer guards now act across function boundaries in more cases.
|
||||
- Sanitizers can now better distinguish between a tainted value and an object _containing_ a tainted value.
|
||||
- Sanitizer guards now act across function boundaries in more cases.
|
||||
- Sanitizers can now better distinguish between a tainted value and an object _containing_ a tainted value.
|
||||
|
||||
* Call graph construction has been improved, leading to more results from the security queries:
|
||||
- Calls can now be resolved to indirectly-defined class members in more cases.
|
||||
- Calls through partial invocations such as `.bind` can now be resolved in more cases.
|
||||
- Calls can now be resolved to indirectly-defined class members in more cases.
|
||||
- Calls through partial invocations such as `.bind` can now be resolved in more cases.
|
||||
|
||||
* Support for flow summaries has been more clearly marked as being experimental and moved to the new `experimental` folder.
|
||||
|
||||
* Support for the following frameworks and libraries has been improved:
|
||||
- [Electron](https://electronjs.org/)
|
||||
- [fstream](https://www.npmjs.com/package/fstream)
|
||||
- [Handlebars](https://www.npmjs.com/package/handlebars)
|
||||
- [jsonfile](https://www.npmjs.com/package/jsonfile)
|
||||
- [Koa](https://www.npmjs.com/package/koa)
|
||||
- [Node.js](https://nodejs.org/)
|
||||
- [Socket.IO](https://socket.io/)
|
||||
- [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API)
|
||||
- [chrome-remote-interface](https://www.npmjs.com/package/chrome-remote-interface)
|
||||
- [for-in](https://www.npmjs.com/package/for-in)
|
||||
- [for-own](https://www.npmjs.com/package/for-own)
|
||||
- [http2](https://nodejs.org/api/http2.html)
|
||||
- [jQuery](https://jquery.com/)
|
||||
- [lazy-cache](https://www.npmjs.com/package/lazy-cache)
|
||||
- [mongodb](https://www.npmjs.com/package/mongodb)
|
||||
- [ncp](https://www.npmjs.com/package/ncp)
|
||||
- [node-dir](https://www.npmjs.com/package/node-dir)
|
||||
- [path-exists](https://www.npmjs.com/package/path-exists)
|
||||
- [pg](https://www.npmjs.com/package/pg)
|
||||
- [react](https://www.npmjs.com/package/react)
|
||||
- [recursive-readdir](https://www.npmjs.com/package/recursive-readdir)
|
||||
- [request](https://www.npmjs.com/package/request)
|
||||
- [rimraf](https://www.npmjs.com/package/rimraf)
|
||||
- [send](https://www.npmjs.com/package/send)
|
||||
- [SockJS](https://www.npmjs.com/package/sockjs)
|
||||
- [SockJS-client](https://www.npmjs.com/package/sockjs-client)
|
||||
- [typeahead.js](https://www.npmjs.com/package/typeahead.js)
|
||||
- [vinyl-fs](https://www.npmjs.com/package/vinyl-fs)
|
||||
- [write-file-atomic](https://www.npmjs.com/package/write-file-atomic)
|
||||
- [ws](https://github.com/websockets/ws)
|
||||
- [chrome-remote-interface](https://www.npmjs.com/package/chrome-remote-interface)
|
||||
- [Electron](https://electronjs.org/)
|
||||
- [for-in](https://www.npmjs.com/package/for-in)
|
||||
- [for-own](https://www.npmjs.com/package/for-own)
|
||||
- [fstream](https://www.npmjs.com/package/fstream)
|
||||
- [Handlebars](https://www.npmjs.com/package/handlebars)
|
||||
- [http2](https://nodejs.org/api/http2.html)
|
||||
- [jQuery](https://jquery.com/)
|
||||
- [jsonfile](https://www.npmjs.com/package/jsonfile)
|
||||
- [Koa](https://www.npmjs.com/package/koa)
|
||||
- [lazy-cache](https://www.npmjs.com/package/lazy-cache)
|
||||
- [mongodb](https://www.npmjs.com/package/mongodb)
|
||||
- [ncp](https://www.npmjs.com/package/ncp)
|
||||
- [Node.js](https://nodejs.org/)
|
||||
- [node-dir](https://www.npmjs.com/package/node-dir)
|
||||
- [path-exists](https://www.npmjs.com/package/path-exists)
|
||||
- [pg](https://www.npmjs.com/package/pg)
|
||||
- [react](https://www.npmjs.com/package/react)
|
||||
- [recursive-readdir](https://www.npmjs.com/package/recursive-readdir)
|
||||
- [request](https://www.npmjs.com/package/request)
|
||||
- [rimraf](https://www.npmjs.com/package/rimraf)
|
||||
- [send](https://www.npmjs.com/package/send)
|
||||
- [Socket.IO](https://socket.io/)
|
||||
- [SockJS](https://www.npmjs.com/package/sockjs)
|
||||
- [SockJS-client](https://www.npmjs.com/package/sockjs-client)
|
||||
- [typeahead.js](https://www.npmjs.com/package/typeahead.js)
|
||||
- [vinyl-fs](https://www.npmjs.com/package/vinyl-fs)
|
||||
- [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API)
|
||||
- [write-file-atomic](https://www.npmjs.com/package/write-file-atomic)
|
||||
- [ws](https://github.com/websockets/ws)
|
||||
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|---------------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Cross-site scripting through exception (`js/xss-through-exception`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities where an exception is written to the DOM. Results are not shown on LGTM by default. |
|
||||
| Regular expression always matches (`js/regex/always-matches`) | correctness, regular-expressions | Highlights regular expression checks that trivially succeed by matching an empty substring. Results are shown on LGTM by default. |
|
||||
| Missing await (`js/missing-await`) | correctness | Highlights expressions that operate directly on a promise object in a nonsensical way, instead of awaiting its result. Results are shown on LGTM by default. |
|
||||
| Polynomial regular expression used on uncontrolled data (`js/polynomial-redos`) | security, external/cwe/cwe-730, external/cwe/cwe-400 | Highlights expensive regular expressions that may be used on malicious input. Results are shown on LGTM by default. |
|
||||
| Prototype pollution in utility function (`js/prototype-pollution-utility`) | security, external/cwe/cwe-400, external/cwe/cwe-471 | Highlights recursive assignment operations that are susceptible to prototype pollution. Results are shown on LGTM by default. |
|
||||
| Unsafe jQuery plugin (`js/unsafe-jquery-plugin`) | Highlights potential XSS vulnerabilities in unsafely designed jQuery plugins. Results are shown on LGTM by default. |
|
||||
| Regular expression always matches (`js/regex/always-matches`) | correctness, regular-expressions | Highlights regular expression checks that trivially succeed by matching an empty substring. Results are shown on LGTM by default. |
|
||||
| Unsafe jQuery plugin (`js/unsafe-jquery-plugin`) | | Highlights potential XSS vulnerabilities in unsafely designed jQuery plugins. Results are shown on LGTM by default. |
|
||||
| Unnecessary use of `cat` process (`js/unnecessary-use-of-cat`) | correctness, security, maintainability | Highlights command executions of `cat` where the fs API should be used instead. Results are shown on LGTM by default. |
|
||||
|
||||
|
||||
@@ -73,20 +74,20 @@
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
|
||||
| Clear-text logging of sensitive information (`js/clear-text-logging`) | More results | More results involving `process.env` and indirect calls to logging methods are recognized. |
|
||||
| Duplicate parameter names (`js/duplicate-parameter-name`) | Fewer results | This query now recognizes additional parameters that reasonably can have duplicated names. |
|
||||
| Incomplete string escaping or encoding (`js/incomplete-sanitization`) | Fewer false positive results | This query now recognizes additional cases where a single replacement is likely to be intentional. |
|
||||
| Unbound event handler receiver (`js/unbound-event-handler-receiver`) | Fewer false positive results | This query now recognizes additional ways event handler receivers can be bound. |
|
||||
| Duplicate parameter names (`js/duplicate-parameter-name`) | Fewer results | This query now ignores additional parameters that reasonably can have duplicated names. |
|
||||
| Expression has no effect (`js/useless-expression`) | Fewer false positive results | The query now recognizes block-level flow type annotations and ignores the first statement of a try block. |
|
||||
| Use of call stack introspection in strict mode (`js/strict-mode-call-stack-introspection`) | Fewer false positive results | The query no longer flags expression statements. |
|
||||
| Identical operands (`js/redundant-operation`) | Fewer results | This query now excludes cases where the operands change a value using ++/-- expressions. |
|
||||
| Incomplete string escaping or encoding (`js/incomplete-sanitization`) | Fewer false positive results | This query now recognizes and excludes additional cases where a single replacement is likely to be intentional. |
|
||||
| Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | More results | This query now recognizes additional variations of URL scheme checks. |
|
||||
| Missing CSRF middleware (`js/missing-token-validation`) | Fewer false positive results | The query reports fewer duplicates and only flags handlers that explicitly access cookie data. |
|
||||
| Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional ways dangerous paths can be constructed and used. |
|
||||
| Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional ways of constructing arguments to `cmd.exe` and `/bin/sh`. |
|
||||
| Superfluous trailing arguments (`js/superfluous-trailing-arguments`) | Fewer results | This query now excludes cases where a function uses the `Function.arguments` value to process a variable number of parameters. |
|
||||
| Syntax error (`js/syntax-error`) | Lower severity | This results of this query are now displayed with lower severity. |
|
||||
| Use of password hash with insufficient computational effort (`js/insufficient-password-hash`) | Fewer false positive results | This query now recognizes additional cases that do not require secure hashing. |
|
||||
| Useless regular-expression character escape (`js/useless-regexp-character-escape`) | Fewer false positive results | This query now distinguishes escapes in strings and regular expression literals. |
|
||||
| Identical operands (`js/redundant-operation`) | Fewer results | This query now recognizes cases where the operands change a value using ++/-- expressions. |
|
||||
| Superfluous trailing arguments (`js/superfluous-trailing-arguments`) | Fewer results | This query now recognizes cases where a function uses the `Function.arguments` value to process a variable number of parameters. |
|
||||
| Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | More results | This query now recognizes more variations of URL scheme checks. |
|
||||
| Unbound event handler receiver (`js/unbound-event-handler-receiver`) | Fewer false positive results | This query now recognizes additional ways event handler receivers can be bound. |
|
||||
| Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional ways of constructing arguments to `cmd.exe` and `/bin/sh`. |
|
||||
| Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional ways dangerous paths can be constructed and used. |
|
||||
| Use of call stack introspection in strict mode (`js/strict-mode-call-stack-introspection`) | Fewer false positive results | The query no longer flags expression statements. |
|
||||
| Use of password hash with insufficient computational effort (`js/insufficient-password-hash`) | Fewer false positive results | This query now recognizes and excludes additional cases that do not require secure hashing. |
|
||||
| Useless regular-expression character escape (`js/useless-regexp-character-escape`) | Fewer false positive results | This query now distinguishes between escapes in strings and regular expression literals. |
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
@@ -94,6 +95,6 @@
|
||||
* An extensible model of the `EventEmitter` pattern has been implemented.
|
||||
* Taint-tracking configurations now interact differently with the `data` flow label, which may affect queries
|
||||
that combine taint-tracking and flow labels.
|
||||
- Sources added by the 1-argument `isSource` predicate are associated with the `taint` label now, instead of the `data` label.
|
||||
- Sanitizers now only block the `taint` label. As a result, sanitizers no longer block the flow of tainted values wrapped inside a property of an object.
|
||||
To retain the old behavior, instead use a barrier, or block the `data` flow label using a labeled sanitizer.
|
||||
- Sources added by the 1-argument `isSource` predicate are associated with the `taint` label now, instead of the `data` label.
|
||||
- Sanitizers now only block the `taint` label. As a result, sanitizers no longer block the flow of tainted values wrapped inside a property of an object.
|
||||
To retain the old behavior, instead use a barrier, or block the `data` flow label using a labeled sanitizer.
|
||||
|
||||
@@ -4,37 +4,52 @@ The following changes in version 1.24 affect Python analysis in all applications
|
||||
|
||||
## General improvements
|
||||
|
||||
Support for Django version 2.x and 3.x
|
||||
- Support for Django version 2.x and 3.x
|
||||
|
||||
## New queries
|
||||
- Taint tracking now correctly tracks taint in destructuring assignments. For example, if `tainted_list` is a list of tainted tainted elements, then
|
||||
```python
|
||||
head, *tail = tainted_list
|
||||
```
|
||||
will result in `tail` being tainted with the same taint as `tainted_list`, and `head` being tainted with the taint of the elements of `tainted_list`.
|
||||
|
||||
- A large number of libraries and queries have been moved to the new `Value` API, which should result in more precise results.
|
||||
|
||||
- The `Value` interface has been extended in various ways:
|
||||
- A new `StringValue` class has been added, for tracking string literals.
|
||||
- Values now have a `booleanValue` method which returns the boolean interpretation of the given value.
|
||||
- Built-in methods for which the return type is not fixed are now modeled as returning an unknown value by default.
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|----------------------------|------------------------|------------------------------------------------------------------|
|
||||
| Uncontrolled command line (`py/command-line-injection`) | More results | We now model the `fabric` and `invoke` pacakges for command execution. |
|
||||
| Arbitrary file write during tarfile extraction (`py/tarslip`) | Fewer false negative results | Negations are now handled correctly in conditional expressions that may sanitize tainted values. |
|
||||
| First parameter of a method is not named 'self' (`py/not-named-self`) | Fewer false positive results | `__class_getitem__` is now recognized as a class method. |
|
||||
| Import of deprecated module (`py/import-deprecated-module`) | Fewer false positive results | Deprecated modules that are used to provide backwards compatibility are no longer reported.|
|
||||
| Module imports itself (`py/import-own-module`) | Fewer false positive results | Imports local to a given package are no longer classified as self-imports. |
|
||||
| Uncontrolled command line (`py/command-line-injection`) | More results | We now model the `fabric` and `invoke` packages for command execution. |
|
||||
|
||||
### Web framework support
|
||||
|
||||
The QL-library support for the web frameworks Bottle, CherryPy, Falcon, Pyramid, TurboGears, Tornado, and Twisted have
|
||||
been fixed so they provide a proper HttpRequestTaintSource, instead of a TaintSource. This will enable results for the following queries:
|
||||
The CodeQL library has improved support for the web frameworks: Bottle, CherryPy, Falcon, Pyramid, TurboGears, Tornado, and Twisted. They now provide a proper `HttpRequestTaintSource`, instead of a `TaintSource`. This will enable results for the following queries:
|
||||
|
||||
- py/path-injection
|
||||
- py/command-line-injection
|
||||
- py/reflective-xss
|
||||
- py/sql-injection
|
||||
- py/code-injection
|
||||
- py/unsafe-deserialization
|
||||
- py/url-redirection
|
||||
- `py/path-injection`
|
||||
- `py/command-line-injection`
|
||||
- `py/reflective-xss`
|
||||
- `py/sql-injection`
|
||||
- `py/code-injection`
|
||||
- `py/unsafe-deserialization`
|
||||
- `py/url-redirection`
|
||||
|
||||
The QL-library support for the web framework Twisted have been fixed so they provide a proper
|
||||
HttpResponseTaintSink, instead of a TaintSink. This will enable results for the following
|
||||
The library also has improved support for the web framework Twisted. It now provides a proper
|
||||
`HttpResponseTaintSink`, instead of a `TaintSink`. This will enable results for the following
|
||||
queries:
|
||||
|
||||
- py/reflective-xss
|
||||
- py/stack-trace-exposure
|
||||
- `py/reflective-xss`
|
||||
- `py/stack-trace-exposure`
|
||||
|
||||
## Changes to libraries
|
||||
### Taint tracking
|
||||
- The `urlsplit` and `urlparse` functions now propagate taint appropriately.
|
||||
- HTTP requests using the `requests` library are now modeled.
|
||||
|
||||
44
change-notes/1.25/analysis-cpp.md
Normal file
44
change-notes/1.25/analysis-cpp.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Improvements to C/C++ analysis
|
||||
|
||||
The following changes in version 1.25 affect C/C++ analysis in all applications.
|
||||
|
||||
## General improvements
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|----------------------------|------------------------|------------------------------------------------------------------|
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
* The library `VCS.qll` and all queries that imported it have been removed.
|
||||
* The data-flow library has been improved, which affects most security queries by potentially
|
||||
adding more results. Flow through functions now takes nested field reads/writes into account.
|
||||
For example, the library is able to track flow from `taint()` to `sink()` via the method
|
||||
`getf2f1()` in
|
||||
```c
|
||||
struct C {
|
||||
int f1;
|
||||
};
|
||||
|
||||
struct C2
|
||||
{
|
||||
C f2;
|
||||
|
||||
int getf2f1() {
|
||||
return f2.f1; // Nested field read
|
||||
}
|
||||
|
||||
void m() {
|
||||
f2.f1 = taint();
|
||||
sink(getf2f1()); // NEW: taint() reaches here
|
||||
}
|
||||
};
|
||||
```
|
||||
* The security pack taint tracking library (`semmle.code.cpp.security.TaintTracking`) now considers that equality checks may block the flow of taint. This results in fewer false positive results from queries that use this library.
|
||||
* The length of a tainted string (such as the return value of a call to `strlen` or `strftime` with tainted parameters) is no longer itself considered tainted by the `models` library. This leads to fewer false positive results in queries that use any of our taint libraries.
|
||||
54
change-notes/1.25/analysis-csharp.md
Normal file
54
change-notes/1.25/analysis-csharp.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Improvements to C# analysis
|
||||
|
||||
The following changes in version 1.25 affect C# analysis in all applications.
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|------------------------------|------------------------|-----------------------------------|
|
||||
|
||||
|
||||
## Removal of old queries
|
||||
|
||||
## Changes to code extraction
|
||||
|
||||
* Index initializers, of the form `{ [1] = "one" }`, are extracted correctly. Previously, the kind of the
|
||||
expression was incorrect, and the index was not extracted.
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
* The class `UnboundGeneric` has been refined to only be those declarations that actually
|
||||
have type parameters. This means that non-generic nested types inside constructed types,
|
||||
such as `A<int>.B`, no longer are considered unbound generics. (Such nested types do,
|
||||
however, still have relevant `.getSourceDeclaration()`s, for example `A<>.B`.)
|
||||
* The data-flow library has been improved, which affects most security queries by potentially
|
||||
adding more results. Flow through methods now takes nested field reads/writes into account.
|
||||
For example, the library is able to track flow from `"taint"` to `Sink()` via the method
|
||||
`GetF2F1()` in
|
||||
```csharp
|
||||
class C1
|
||||
{
|
||||
string F1;
|
||||
}
|
||||
|
||||
class C2
|
||||
{
|
||||
C1 F2;
|
||||
|
||||
string GetF2F1() => F2.F1; // Nested field read
|
||||
|
||||
void M()
|
||||
{
|
||||
F2 = new C1() { F1 = "taint" };
|
||||
Sink(GetF2F1()); // NEW: "taint" reaches here
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Changes to autobuilder
|
||||
41
change-notes/1.25/analysis-java.md
Normal file
41
change-notes/1.25/analysis-java.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Improvements to Java analysis
|
||||
|
||||
The following changes in version 1.25 affect Java analysis in all applications.
|
||||
|
||||
## General improvements
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|------------------------------|------------------------|-----------------------------------|
|
||||
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
* The data-flow library has been improved, which affects most security queries by potentially
|
||||
adding more results. Flow through methods now takes nested field reads/writes into account.
|
||||
For example, the library is able to track flow from `"taint"` to `sink()` via the method
|
||||
`getF2F1()` in
|
||||
```java
|
||||
class C1 {
|
||||
String f1;
|
||||
C1(String f1) { this.f1 = f1; }
|
||||
}
|
||||
|
||||
class C2 {
|
||||
C1 f2;
|
||||
String getF2F1() {
|
||||
return this.f2.f1; // Nested field read
|
||||
}
|
||||
void m() {
|
||||
this.f2 = new C1("taint");
|
||||
sink(this.getF2F1()); // NEW: "taint" reaches here
|
||||
}
|
||||
}
|
||||
```
|
||||
89
change-notes/1.25/analysis-javascript.md
Normal file
89
change-notes/1.25/analysis-javascript.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# Improvements to JavaScript analysis
|
||||
|
||||
## General improvements
|
||||
|
||||
* Support for the following frameworks and libraries has been improved:
|
||||
- [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
|
||||
- [bluebird](http://bluebirdjs.com/)
|
||||
- [express](https://www.npmjs.com/package/express)
|
||||
- [fastify](https://www.npmjs.com/package/fastify)
|
||||
- [fstream](https://www.npmjs.com/package/fstream)
|
||||
- [jGrowl](https://github.com/stanlemon/jGrowl)
|
||||
- [jQuery](https://jquery.com/)
|
||||
- [marsdb](https://www.npmjs.com/package/marsdb)
|
||||
- [minimongo](https://www.npmjs.com/package/minimongo/)
|
||||
- [mssql](https://www.npmjs.com/package/mssql)
|
||||
- [mysql](https://www.npmjs.com/package/mysql)
|
||||
- [pg](https://www.npmjs.com/package/pg)
|
||||
- [sequelize](https://www.npmjs.com/package/sequelize)
|
||||
- [spanner](https://www.npmjs.com/package/spanner)
|
||||
- [sqlite](https://www.npmjs.com/package/sqlite)
|
||||
- [ssh2-streams](https://www.npmjs.com/package/ssh2-streams)
|
||||
- [ssh2](https://www.npmjs.com/package/ssh2)
|
||||
- [webpack-dev-server](https://www.npmjs.com/package/webpack-dev-server)
|
||||
|
||||
* TypeScript 3.9 is now supported.
|
||||
|
||||
* The analysis of sanitizers has improved, leading to more accurate
|
||||
results from the security queries.
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|---------------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Cross-site scripting through DOM (`js/xss-through-dom`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities where existing text from the DOM is used as HTML. Results are not shown on LGTM by default. |
|
||||
| Incomplete HTML attribute sanitization (`js/incomplete-html-attribute-sanitization`) | security, external/cwe/cwe-20, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities due to incomplete sanitization of HTML meta-characters. Results are shown on LGTM by default. |
|
||||
| Unsafe expansion of self-closing HTML tag (`js/unsafe-html-expansion`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities caused by unsafe expansion of self-closing HTML tags. |
|
||||
| Unsafe shell command constructed from library input (`js/shell-command-constructed-from-input`) | correctness, security, external/cwe/cwe-078, external/cwe/cwe-088 | Highlights potential command injections due to a shell command being constructed from library inputs. Results are shown on LGTM by default. |
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
|
||||
| Client-side cross-site scripting (`js/xss`) | Fewer results | This query now recognizes additional safe patterns of constructing HTML. |
|
||||
| Client-side URL redirect (`js/client-side-unvalidated-url-redirection`) | Fewer results | This query now recognizes additional safe patterns of doing URL redirects. |
|
||||
| Code injection (`js/code-injection`) | More results | More potential vulnerabilities involving NoSQL code operators are now recognized. |
|
||||
| Expression has no effect (`js/useless-expression`) | Fewer results | This query no longer flags an expression when that expression is the only content of the containing file. |
|
||||
| Hard-coded credentials (`js/hardcoded-credentials`) | More results | This query now recognizes hard-coded credentials sent via HTTP authorization headers. |
|
||||
| Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | More results | This query now recognizes additional url scheme checks. |
|
||||
| Misspelled variable name (`js/misspelled-variable-name`) | Message changed | The message for this query now correctly identifies the misspelled variable in additional cases. |
|
||||
| Non-linear pattern (`js/non-linear-pattern`) | Fewer duplicates and message changed | This query now generates fewer duplicate alerts and has a clearer explanation in case of type annotations used in a pattern. |
|
||||
| Prototype pollution in utility function (`js/prototype-pollution-utility`) | More results | This query now recognizes additional utility functions as vulnerable to prototype polution. |
|
||||
| Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional command execution calls. |
|
||||
| Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional file system calls. |
|
||||
| Unknown directive (`js/unknown-directive`) | Fewer results | This query no longer flags directives generated by the Babel compiler. |
|
||||
| Unused property (`js/unused-property`) | Fewer results | This query no longer flags properties of objects that are operands of `yield` expressions. |
|
||||
| Zip Slip (`js/zipslip`) | More results | This query now recognizes additional vulnerabilities. |
|
||||
|
||||
The following low-precision queries are no longer run by default on LGTM (their results already were not displayed):
|
||||
|
||||
- `js/angular/dead-event-listener`
|
||||
- `js/angular/unused-dependency`
|
||||
- `js/bitwise-sign-check`
|
||||
- `js/comparison-of-identical-expressions`
|
||||
- `js/conflicting-html-attribute`
|
||||
- `js/ignored-setter-parameter`
|
||||
- `js/jsdoc/malformed-param-tag`
|
||||
- `js/jsdoc/missing-parameter`
|
||||
- `js/jsdoc/unknown-parameter`
|
||||
- `js/json-in-javascript-file`
|
||||
- `js/misspelled-identifier`
|
||||
- `js/nested-loops-with-same-variable`
|
||||
- `js/node/cyclic-import`
|
||||
- `js/node/unused-npm-dependency`
|
||||
- `js/omitted-array-element`
|
||||
- `js/return-outside-function`
|
||||
- `js/single-run-loop`
|
||||
- `js/too-many-parameters`
|
||||
- `js/unused-property`
|
||||
- `js/useless-assignment-to-global`
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
* A library `semmle.javascript.explore.CallGraph` has been added to help write queries for exploring the call graph.
|
||||
* Added data flow for `Map` and `Set`, and added matching type-tracking steps that can accessed using the `CollectionsTypeTracking` module.
|
||||
* The data-flow node representing a parameter or destructuring pattern is now always the `ValueNode` corresponding to that AST node. This has a few consequences:
|
||||
- `Parameter.flow()` now gets the correct data flow node for a parameter. Previously this had a result, but the node was disconnected from the data flow graph.
|
||||
- `ParameterNode.asExpr()` and `.getAstNode()` now gets the parameter's AST node, whereas previously it had no result.
|
||||
- `Expr.flow()` now has a more meaningful result for destructuring patterns. Previously this node was disconnected from the data flow graph. Now it represents the values being destructured by the pattern.
|
||||
* The global data-flow and taint-tracking libraries now model indirect parameter accesses through the `arguments` object in some cases, which may lead to additional results from some of the security queries, particularly "Prototype pollution in utility function".
|
||||
22
change-notes/1.25/analysis-python.md
Normal file
22
change-notes/1.25/analysis-python.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Improvements to Python analysis
|
||||
|
||||
The following changes in version 1.25 affect Python analysis in all applications.
|
||||
|
||||
## General improvements
|
||||
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|----------------------------|------------------------|------------------------------------------------------------------|
|
||||
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
* Importing `semmle.python.web.HttpRequest` will no longer import `UntrustedStringKind` transitively. `UntrustedStringKind` is the most commonly used non-abstract subclass of `ExternalStringKind`. If not imported (by one mean or another), taint-tracking queries that concern `ExternalStringKind` will not produce any results. Please ensure such queries contain an explicit import (`import semmle.python.security.strings.Untrusted`).
|
||||
@@ -53,114 +53,122 @@
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll"
|
||||
],
|
||||
"IR IRBlock": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRBlock.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRBlock.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll"
|
||||
],
|
||||
"IR IRVariable": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll"
|
||||
],
|
||||
"IR IRFunction": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRFunction.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRFunction.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRFunction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRFunction.qll"
|
||||
],
|
||||
"IR Operand": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Operand.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Operand.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/Operand.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll"
|
||||
],
|
||||
"IR IRType": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/IRType.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/IRType.qll"
|
||||
],
|
||||
"IR IRConfiguration": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/IRConfiguration.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/IRConfiguration.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/IRConfiguration.qll"
|
||||
],
|
||||
"IR UseSoundEscapeAnalysis": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/UseSoundEscapeAnalysis.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/UseSoundEscapeAnalysis.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/UseSoundEscapeAnalysis.qll"
|
||||
],
|
||||
"IR IRFunctionBase": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/IRFunctionBase.qll"
|
||||
],
|
||||
"IR Operand Tag": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/internal/OperandTag.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll"
|
||||
],
|
||||
"IR TInstruction":[
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll"
|
||||
],
|
||||
"IR TIRVariable":[
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TIRVariable.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/TIRVariable.qll"
|
||||
],
|
||||
"IR IR": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IR.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IR.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IR.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll"
|
||||
],
|
||||
"IR IRSanity": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRSanity.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRSanity.qll"
|
||||
"IR IRConsistency": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRConsistency.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRConsistency.qll"
|
||||
],
|
||||
"IR PrintIR": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/PrintIR.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/PrintIR.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll"
|
||||
],
|
||||
"IR IntegerConstant": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/internal/IntegerConstant.qll"
|
||||
"csharp/ql/src/experimental/ir/internal/IntegerConstant.qll"
|
||||
],
|
||||
"IR IntegerInteval": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerInterval.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/internal/IntegerInterval.qll"
|
||||
"csharp/ql/src/experimental/ir/internal/IntegerInterval.qll"
|
||||
],
|
||||
"IR IntegerPartial": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerPartial.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/internal/IntegerPartial.qll"
|
||||
"csharp/ql/src/experimental/ir/internal/IntegerPartial.qll"
|
||||
],
|
||||
"IR Overlap": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/internal/Overlap.qll"
|
||||
"csharp/ql/src/experimental/ir/internal/Overlap.qll"
|
||||
],
|
||||
"IR EdgeKind": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/EdgeKind.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/EdgeKind.qll"
|
||||
],
|
||||
"IR MemoryAccessKind": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/MemoryAccessKind.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/MemoryAccessKind.qll"
|
||||
],
|
||||
"IR TempVariableTag": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/TempVariableTag.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/TempVariableTag.qll"
|
||||
],
|
||||
"IR Opcode": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/Opcode.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/Opcode.qll"
|
||||
],
|
||||
"IR SSASanity": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSASanity.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSASanity.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSASanity.qll"
|
||||
"IR SSAConsistency": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll"
|
||||
],
|
||||
"C++ IR InstructionImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll",
|
||||
@@ -177,6 +185,11 @@
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRBlockImports.qll"
|
||||
],
|
||||
"C++ IR IRFunctionImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll"
|
||||
],
|
||||
"C++ IR IRVariableImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRVariableImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll",
|
||||
@@ -199,7 +212,7 @@
|
||||
"SSA AliasAnalysis": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll"
|
||||
],
|
||||
"C++ SSA AliasAnalysisImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll",
|
||||
@@ -212,42 +225,42 @@
|
||||
],
|
||||
"IR SSA SimpleSSA": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll"
|
||||
],
|
||||
"IR AliasConfiguration (unaliased_ssa)": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll"
|
||||
],
|
||||
"IR SSA SSAConstruction": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll"
|
||||
],
|
||||
"IR SSA PrintSSA": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/PrintSSA.qll"
|
||||
],
|
||||
"IR ValueNumberInternal": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll"
|
||||
],
|
||||
"C++ IR ValueNumber": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll"
|
||||
],
|
||||
"C++ IR PrintValueNumbering": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/PrintValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/PrintValueNumbering.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/PrintValueNumbering.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/PrintValueNumbering.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll"
|
||||
],
|
||||
"C++ IR ConstantAnalysis": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll",
|
||||
@@ -276,32 +289,36 @@
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
|
||||
],
|
||||
"C# IR InstructionImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionImports.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/InstructionImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/InstructionImports.qll"
|
||||
],
|
||||
"C# IR IRImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRImports.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRImports.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRImports.qll"
|
||||
],
|
||||
"C# IR IRBlockImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRBlockImports.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRBlockImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll"
|
||||
],
|
||||
"C# IR IRFunctionImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRFunctionImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll"
|
||||
],
|
||||
"C# IR IRVariableImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRVariableImports.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRVariableImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll"
|
||||
],
|
||||
"C# IR OperandImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/OperandImports.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/OperandImports.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/OperandImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/OperandImports.qll"
|
||||
],
|
||||
"C# IR PrintIRImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/PrintIRImports.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/PrintIRImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll"
|
||||
],
|
||||
"C# IR ValueNumberingImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll"
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll"
|
||||
],
|
||||
"XML": [
|
||||
"cpp/ql/src/semmle/code/cpp/XML.qll",
|
||||
|
||||
@@ -59,21 +59,32 @@ def file_checksum(filename):
|
||||
return hashlib.sha1(file_handle.read()).hexdigest()
|
||||
|
||||
def check_group(group_name, files, master_file_picker, emit_error):
|
||||
checksums = {file_checksum(f) for f in files}
|
||||
|
||||
if len(checksums) == 1:
|
||||
extant_files = [f for f in files if path.isfile(f)]
|
||||
if len(extant_files) == 0:
|
||||
emit_error(__file__, 0, "No files found from group '" + group_name + "'.")
|
||||
emit_error(__file__, 0,
|
||||
"Create one of the following files, and then run this script with "
|
||||
"the --latest switch to sync it to the other file locations.")
|
||||
for filename in files:
|
||||
emit_error(__file__, 0, " " + filename)
|
||||
return
|
||||
|
||||
master_file = master_file_picker(files)
|
||||
checksums = {file_checksum(f) for f in extant_files}
|
||||
|
||||
if len(checksums) == 1 and len(extant_files) == len(files):
|
||||
# All files are present and identical.
|
||||
return
|
||||
|
||||
master_file = master_file_picker(extant_files)
|
||||
if master_file is None:
|
||||
emit_error(__file__, 0,
|
||||
"Files from group '"+ group_name +"' not in sync.")
|
||||
emit_error(__file__, 0,
|
||||
"Run this script with a file-name argument among the "
|
||||
"following to overwrite the remaining files with the contents "
|
||||
"of that file or run with the --latest switch to update each "
|
||||
"of that file, or run with the --latest switch to update each "
|
||||
"group of files from the most recently modified file in the group.")
|
||||
for filename in files:
|
||||
for filename in extant_files:
|
||||
emit_error(__file__, 0, " " + filename)
|
||||
else:
|
||||
print(" Syncing others from", master_file)
|
||||
@@ -81,7 +92,8 @@ def check_group(group_name, files, master_file_picker, emit_error):
|
||||
if filename == master_file:
|
||||
continue
|
||||
print(" " + filename)
|
||||
os.replace(filename, filename + '~')
|
||||
if path.isfile(filename):
|
||||
os.replace(filename, filename + '~')
|
||||
shutil.copy(master_file, filename)
|
||||
print(" Backups written with '~' appended to file names")
|
||||
|
||||
@@ -107,7 +119,7 @@ def choose_latest_file(files):
|
||||
|
||||
local_error_count = 0
|
||||
def emit_local_error(path, line, error):
|
||||
print('ERROR: ' + path + ':' + line + " - " + error)
|
||||
print('ERROR: ' + path + ':' + str(line) + " - " + error)
|
||||
global local_error_count
|
||||
local_error_count += 1
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import semmle.code.cpp.pointsto.PointsTo
|
||||
|
||||
/** Holds if there exists a call to a function that might close the file specified by `e`. */
|
||||
predicate closed(Expr e) {
|
||||
fcloseCall(_, e) or
|
||||
exists(ExprCall c |
|
||||
@@ -8,10 +9,19 @@ predicate closed(Expr e) {
|
||||
)
|
||||
}
|
||||
|
||||
/** An expression for which there exists a function call that might close it. */
|
||||
class ClosedExpr extends PointsToExpr {
|
||||
ClosedExpr() { closed(this) }
|
||||
|
||||
override predicate interesting() { closed(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fc` is a call to a function that opens a file that might be closed. For example:
|
||||
* ```
|
||||
* FILE* f = fopen("file.txt", "r");
|
||||
* ...
|
||||
* fclose(f);
|
||||
* ```
|
||||
*/
|
||||
predicate fopenCallMayBeClosed(FunctionCall fc) { fopenCall(fc) and anythingPointsTo(fc) }
|
||||
|
||||
@@ -2,12 +2,24 @@
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* An assignment to a variable with the value `0`. For example:
|
||||
* ```
|
||||
* int x;
|
||||
* x = 0;
|
||||
* ```
|
||||
* but not:
|
||||
* ```
|
||||
* int x = 0;
|
||||
* ```
|
||||
*/
|
||||
class ZeroAssignment extends AssignExpr {
|
||||
ZeroAssignment() {
|
||||
this.getAnOperand() instanceof VariableAccess and
|
||||
this.getAnOperand() instanceof Zero
|
||||
}
|
||||
|
||||
/** Gets a variable that is assigned the value `0`. */
|
||||
Variable assignedVariable() { result.getAnAccess() = this.getAnOperand() }
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,19 @@ private predicate freed(Expr e) {
|
||||
)
|
||||
}
|
||||
|
||||
/** An expression that might be deallocated. */
|
||||
class FreedExpr extends PointsToExpr {
|
||||
FreedExpr() { freed(this) }
|
||||
|
||||
override predicate interesting() { freed(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An allocation expression that might be deallocated. For example:
|
||||
* ```
|
||||
* int* p = new int;
|
||||
* ...
|
||||
* delete p;
|
||||
* ```
|
||||
*/
|
||||
predicate allocMayBeFreed(AllocationExpr alloc) { anythingPointsTo(alloc) }
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Holds if `val` is an access to the variable `v`, or if `val`
|
||||
* is an assignment with an access to `v` on the left-hand side.
|
||||
*/
|
||||
predicate valueOfVar(Variable v, Expr val) {
|
||||
val = v.getAnAccess() or
|
||||
val.(AssignExpr).getLValue() = v.getAnAccess()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if either:
|
||||
* - `cond` is an (in)equality expression that compares the variable `v` to the value `-1`, or
|
||||
* - `cond` is a relational expression that compares the variable `v` to a constant.
|
||||
*/
|
||||
predicate boundsCheckExpr(Variable v, Expr cond) {
|
||||
exists(EQExpr eq |
|
||||
cond = eq and
|
||||
@@ -43,6 +52,18 @@ predicate boundsCheckExpr(Variable v, Expr cond) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is an expression in a conditional statement and `succ` is an
|
||||
* immediate successor of `node` that may be reached after evaluating `node`.
|
||||
* For example, given
|
||||
* ```
|
||||
* if (a < 10 && b) func1();
|
||||
* else func2();
|
||||
* ```
|
||||
* this predicate holds when either:
|
||||
* - `node` is `a < 10` and `succ` is `func2()` or `b`, or
|
||||
* - `node` is `b` and `succ` is `func1()` or `func2()`
|
||||
*/
|
||||
predicate conditionalSuccessor(ControlFlowNode node, ControlFlowNode succ) {
|
||||
if node.isCondition()
|
||||
then succ = node.getATrueSuccessor() or succ = node.getAFalseSuccessor()
|
||||
@@ -52,6 +73,12 @@ predicate conditionalSuccessor(ControlFlowNode node, ControlFlowNode succ) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the current value of the variable `v` at control-flow
|
||||
* node `n` has been used either in:
|
||||
* - an (in)equality comparison with the value `-1`, or
|
||||
* - a relational comparison that compares `v` to a constant.
|
||||
*/
|
||||
predicate boundsChecked(Variable v, ControlFlowNode node) {
|
||||
exists(Expr test |
|
||||
boundsCheckExpr(v, test) and
|
||||
@@ -63,6 +90,14 @@ predicate boundsChecked(Variable v, ControlFlowNode node) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `cond` compares `v` to some common error values. Specifically, this
|
||||
* predicate holds when:
|
||||
* - `cond` checks that `v` is equal to `-1`, or
|
||||
* - `cond` checks that `v` is less than `0`, or
|
||||
* - `cond` checks that `v` is less than or equal to `-1`, or
|
||||
* - `cond` checks that `v` is not some common success value (see `successCondition`).
|
||||
*/
|
||||
predicate errorCondition(Variable v, Expr cond) {
|
||||
exists(EQExpr eq |
|
||||
cond = eq and
|
||||
@@ -88,6 +123,14 @@ predicate errorCondition(Variable v, Expr cond) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `cond` compares `v` to some common success values. Specifically, this
|
||||
* predicate holds when:
|
||||
* - `cond` checks that `v` is not equal to `-1`, or
|
||||
* - `cond` checks that `v` is greater than or equal than `0`, or
|
||||
* - `cond` checks that `v` is greater than `-1`, or
|
||||
* - `cond` checks that `v` is not some common error value (see `errorCondition`).
|
||||
*/
|
||||
predicate successCondition(Variable v, Expr cond) {
|
||||
exists(NEExpr ne |
|
||||
cond = ne and
|
||||
@@ -113,6 +156,11 @@ predicate successCondition(Variable v, Expr cond) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there exists a comparison operation that checks whether `v`
|
||||
* represents some common *error* values, and `n` may be reached
|
||||
* immediately following the comparison operation.
|
||||
*/
|
||||
predicate errorSuccessor(Variable v, ControlFlowNode n) {
|
||||
exists(Expr cond |
|
||||
errorCondition(v, cond) and n = cond.getATrueSuccessor()
|
||||
@@ -121,6 +169,11 @@ predicate errorSuccessor(Variable v, ControlFlowNode n) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there exists a comparison operation that checks whether `v`
|
||||
* represents some common *success* values, and `n` may be reached
|
||||
* immediately following the comparison operation.
|
||||
*/
|
||||
predicate successSuccessor(Variable v, ControlFlowNode n) {
|
||||
exists(Expr cond |
|
||||
successCondition(v, cond) and n = cond.getATrueSuccessor()
|
||||
@@ -129,6 +182,10 @@ predicate successSuccessor(Variable v, ControlFlowNode n) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the current value of the variable `v` at control-flow node
|
||||
* `n` may have been checked against a common set of *error* values.
|
||||
*/
|
||||
predicate checkedError(Variable v, ControlFlowNode n) {
|
||||
errorSuccessor(v, n)
|
||||
or
|
||||
@@ -139,6 +196,10 @@ predicate checkedError(Variable v, ControlFlowNode n) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the current value of the variable `v` at control-flow node
|
||||
* `n` may have been checked against a common set of *success* values.
|
||||
*/
|
||||
predicate checkedSuccess(Variable v, ControlFlowNode n) {
|
||||
successSuccessor(v, n)
|
||||
or
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Gets a string representation of the comment `c` containing the caption 'TODO' or 'FIXME'.
|
||||
* If `c` spans multiple lines, all lines after the first are abbreviated as [...].
|
||||
*/
|
||||
string getCommentTextCaptioned(Comment c, string caption) {
|
||||
(caption = "TODO" or caption = "FIXME") and
|
||||
exists(
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Provides classes and predicates for identifying C/C++ comments that look like code.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
@@ -137,8 +141,14 @@ class CommentBlock extends Comment {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last comment associated with this comment block.
|
||||
*/
|
||||
Comment lastComment() { result = this.getComment(max(int i | exists(this.getComment(i)))) }
|
||||
|
||||
/**
|
||||
* Gets the contents of the `i`'th comment associated with this comment block.
|
||||
*/
|
||||
string getLine(int i) {
|
||||
this instanceof CStyleComment and
|
||||
result = this.getContents().regexpCapture("(?s)/\\*+(.*)\\*+/", 1).splitAt("\n", i)
|
||||
@@ -146,14 +156,24 @@ class CommentBlock extends Comment {
|
||||
this instanceof CppStyleComment and result = this.getComment(i).getContents().suffix(2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of lines in the comments associated with this comment block.
|
||||
*/
|
||||
int numLines() {
|
||||
result = strictcount(int i, string line | line = this.getLine(i) and line.trim() != "")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of lines that look like code in the comments associated with this comment block.
|
||||
*/
|
||||
int numCodeLines() {
|
||||
result = strictcount(int i, string line | line = this.getLine(i) and looksLikeCode(line))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the comment block is a C-style comment, and each
|
||||
* comment line starts with a *.
|
||||
*/
|
||||
predicate isDocumentation() {
|
||||
// If a C-style comment starts each line with a *, then it's
|
||||
// probably documentation rather than code.
|
||||
@@ -161,6 +181,12 @@ class CommentBlock extends Comment {
|
||||
forex(int i | i in [1 .. this.numLines() - 1] | this.getLine(i).trim().matches("*%"))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this comment block looks like code that has been commented out. Specifically:
|
||||
* 1. It does not look like documentation (see `isDocumentation`).
|
||||
* 2. It is not in a header file without any declaration entries or top level declarations.
|
||||
* 3. More than half of the lines in the comment block look like code.
|
||||
*/
|
||||
predicate isCommentedOutCode() {
|
||||
not this.isDocumentation() and
|
||||
not this.getFile().(HeaderFile).noTopLevelCode() and
|
||||
|
||||
@@ -30,7 +30,7 @@ predicate allowedTypedefs(TypedefType t) {
|
||||
* Gets a type which appears literally in the declaration of `d`.
|
||||
*/
|
||||
Type getAnImmediateUsedType(Declaration d) {
|
||||
d.isDefined() and
|
||||
d.hasDefinition() and
|
||||
(
|
||||
result = d.(Function).getType() or
|
||||
result = d.(Variable).getType()
|
||||
|
||||
@@ -23,7 +23,7 @@ import semmle.code.cpp.ir.ValueNumbering
|
||||
class NullInstruction extends ConstantValueInstruction {
|
||||
NullInstruction() {
|
||||
this.getValue() = "0" and
|
||||
this.getResultType().getUnspecifiedType() instanceof PointerType
|
||||
this.getResultIRType() instanceof IRAddressType
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ predicate explicitNullTestOfInstruction(Instruction checked, Instruction bool) {
|
||||
bool =
|
||||
any(ConvertInstruction convert |
|
||||
checked = convert.getUnary() and
|
||||
convert.getResultType() instanceof BoolType and
|
||||
checked.getResultType() instanceof PointerType
|
||||
convert.getResultIRType() instanceof IRBooleanType and
|
||||
checked.getResultIRType() instanceof IRAddressType
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import cpp
|
||||
|
||||
pragma[inline]
|
||||
private predicate arithTypesMatch(Type arg, Type parm) {
|
||||
arg = parm
|
||||
or
|
||||
|
||||
@@ -6,29 +6,50 @@
|
||||
|
||||
import cpp
|
||||
|
||||
// True if function was ()-declared, but not (void)-declared or K&R-defined
|
||||
/**
|
||||
* Holds if `fde` has a parameter declaration that's clear on the minimum
|
||||
* number of parameters. This is essentially true for everything except
|
||||
* `()`-declarations.
|
||||
*/
|
||||
private predicate hasDefiniteNumberOfParameters(FunctionDeclarationEntry fde) {
|
||||
fde.hasVoidParamList()
|
||||
or
|
||||
fde.getNumberOfParameters() > 0
|
||||
or
|
||||
fde.isDefinition()
|
||||
}
|
||||
|
||||
/* Holds if function was ()-declared, but not (void)-declared or K&R-defined. */
|
||||
private predicate hasZeroParamDecl(Function f) {
|
||||
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
not fde.hasVoidParamList() and fde.getNumberOfParameters() = 0 and not fde.isDefinition()
|
||||
not hasDefiniteNumberOfParameters(fde)
|
||||
)
|
||||
}
|
||||
|
||||
// True if this file (or header) was compiled as a C file
|
||||
/* Holds if this file (or header) was compiled as a C file. */
|
||||
private predicate isCompiledAsC(File f) {
|
||||
f.compiledAsC()
|
||||
or
|
||||
exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
|
||||
}
|
||||
|
||||
/** Holds if `fc` is a call to `f` with too few arguments. */
|
||||
predicate tooFewArguments(FunctionCall fc, Function f) {
|
||||
f = fc.getTarget() and
|
||||
not f.isVarargs() and
|
||||
not f instanceof BuiltInFunction and
|
||||
// This query should only have results on C (not C++) functions that have a
|
||||
// `()` parameter list somewhere. If it has results on other functions, then
|
||||
// it's probably because the extractor only saw a partial compilation.
|
||||
hasZeroParamDecl(f) and
|
||||
isCompiledAsC(f.getFile()) and
|
||||
// There is an explicit declaration of the function whose parameter count is larger
|
||||
// than the number of call arguments
|
||||
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
// Produce an alert when all declarations that are authoritative on the
|
||||
// parameter count specify a parameter count larger than the number of call
|
||||
// arguments.
|
||||
forex(FunctionDeclarationEntry fde |
|
||||
fde = f.getADeclarationEntry() and
|
||||
hasDefiniteNumberOfParameters(fde)
|
||||
|
|
||||
fde.getNumberOfParameters() > fc.getNumberOfArguments()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* @name Churned lines per file
|
||||
* @description Number of churned lines per file, across the revision
|
||||
* history in the database.
|
||||
* @kind treemap
|
||||
* @id cpp/historical-churn
|
||||
* @treemap.warnOn highValues
|
||||
* @metricType file
|
||||
* @metricAggregate avg sum max
|
||||
* @tags external-data
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import external.VCS
|
||||
|
||||
from File f, int n
|
||||
where
|
||||
n =
|
||||
sum(Commit entry, int churn |
|
||||
churn = entry.getRecentChurnForFile(f) and
|
||||
not artificialChange(entry)
|
||||
|
|
||||
churn
|
||||
) and
|
||||
exists(f.getMetrics().getNumberOfLinesOfCode())
|
||||
select f, n order by n desc
|
||||
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* @name Added lines per file
|
||||
* @description Number of added lines per file, across the revision
|
||||
* history in the database.
|
||||
* @kind treemap
|
||||
* @id cpp/historical-lines-added
|
||||
* @treemap.warnOn highValues
|
||||
* @metricType file
|
||||
* @metricAggregate avg sum max
|
||||
* @tags external-data
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import external.VCS
|
||||
|
||||
from File f, int n
|
||||
where
|
||||
n =
|
||||
sum(Commit entry, int churn |
|
||||
churn = entry.getRecentAdditionsForFile(f) and
|
||||
not artificialChange(entry)
|
||||
|
|
||||
churn
|
||||
) and
|
||||
exists(f.getMetrics().getNumberOfLinesOfCode())
|
||||
select f, n order by n desc
|
||||
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* @name Deleted lines per file
|
||||
* @description Number of deleted lines per file, across the revision
|
||||
* history in the database.
|
||||
* @kind treemap
|
||||
* @id cpp/historical-lines-deleted
|
||||
* @treemap.warnOn highValues
|
||||
* @metricType file
|
||||
* @metricAggregate avg sum max
|
||||
* @tags external-data
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import external.VCS
|
||||
|
||||
from File f, int n
|
||||
where
|
||||
n =
|
||||
sum(Commit entry, int churn |
|
||||
churn = entry.getRecentDeletionsForFile(f) and
|
||||
not artificialChange(entry)
|
||||
|
|
||||
churn
|
||||
) and
|
||||
exists(f.getMetrics().getNumberOfLinesOfCode())
|
||||
select f, n order by n desc
|
||||
@@ -1,18 +0,0 @@
|
||||
/**
|
||||
* @name Number of authors
|
||||
* @description Number of distinct authors for each file.
|
||||
* @kind treemap
|
||||
* @id cpp/historical-number-of-authors
|
||||
* @treemap.warnOn highValues
|
||||
* @metricType file
|
||||
* @metricAggregate avg min max
|
||||
* @tags external-data
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import external.VCS
|
||||
|
||||
from File f
|
||||
where exists(f.getMetrics().getNumberOfLinesOfCode())
|
||||
select f, count(Author author | author.getAnEditedFile() = f)
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* @name Number of file-level changes
|
||||
* @description The number of file-level changes made (by version
|
||||
* control history).
|
||||
* @kind treemap
|
||||
* @id cpp/historical-number-of-changes
|
||||
* @treemap.warnOn highValues
|
||||
* @metricType file
|
||||
* @metricAggregate avg min max sum
|
||||
* @tags external-data
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import external.VCS
|
||||
|
||||
from File f
|
||||
where exists(f.getMetrics().getNumberOfLinesOfCode())
|
||||
select f, count(Commit svn | f = svn.getAnAffectedFile() and not artificialChange(svn))
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* @name Number of co-committed files
|
||||
* @description The average number of other files that are touched
|
||||
* whenever a file is affected by a commit.
|
||||
* @kind treemap
|
||||
* @id cpp/historical-number-of-co-commits
|
||||
* @treemap.warnOn highValues
|
||||
* @metricType file
|
||||
* @metricAggregate avg min max
|
||||
* @tags external-data
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import external.VCS
|
||||
|
||||
int committedFiles(Commit commit) { result = count(commit.getAnAffectedFile()) }
|
||||
|
||||
from File f
|
||||
where exists(f.getMetrics().getNumberOfLinesOfCode())
|
||||
select f, avg(Commit commit | commit.getAnAffectedFile() = f | committedFiles(commit) - 1)
|
||||
@@ -1,37 +0,0 @@
|
||||
/**
|
||||
* @name Number of re-commits for each file
|
||||
* @description A re-commit is taken to mean a commit to a file that
|
||||
* was touched less than five days ago.
|
||||
* @kind treemap
|
||||
* @id cpp/historical-number-of-re-commits
|
||||
* @treemap.warnOn highValues
|
||||
* @metricType file
|
||||
* @metricAggregate avg min max
|
||||
* @tags external-data
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import external.VCS
|
||||
|
||||
predicate inRange(Commit first, Commit second) {
|
||||
first.getAnAffectedFile() = second.getAnAffectedFile() and
|
||||
first != second and
|
||||
exists(int n |
|
||||
n = first.getDate().daysTo(second.getDate()) and
|
||||
n >= 0 and
|
||||
n < 5
|
||||
)
|
||||
}
|
||||
|
||||
int recommitsForFile(File f) {
|
||||
result =
|
||||
count(Commit recommit |
|
||||
f = recommit.getAnAffectedFile() and
|
||||
exists(Commit prev | inRange(prev, recommit))
|
||||
)
|
||||
}
|
||||
|
||||
from File f
|
||||
where exists(f.getMetrics().getNumberOfLinesOfCode())
|
||||
select f, recommitsForFile(f)
|
||||
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* @name Number of recent authors
|
||||
* @description Number of distinct authors that have recently made
|
||||
* changes.
|
||||
* @kind treemap
|
||||
* @id cpp/historical-number-of-recent-authors
|
||||
* @treemap.warnOn highValues
|
||||
* @metricType file
|
||||
* @metricAggregate avg min max
|
||||
* @tags external-data
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import external.VCS
|
||||
|
||||
from File f
|
||||
where exists(f.getMetrics().getNumberOfLinesOfCode())
|
||||
select f,
|
||||
count(Author author |
|
||||
exists(Commit e |
|
||||
e = author.getACommit() and
|
||||
f = e.getAnAffectedFile() and
|
||||
e.daysToNow() <= 180 and
|
||||
not artificialChange(e)
|
||||
)
|
||||
)
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* @name Recently changed files
|
||||
* @description Number of files recently edited.
|
||||
* @kind treemap
|
||||
* @id cpp/historical-number-of-recent-changed-files
|
||||
* @treemap.warnOn highValues
|
||||
* @metricType file
|
||||
* @metricAggregate avg min max sum
|
||||
* @tags external-data
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import external.VCS
|
||||
|
||||
from File f
|
||||
where
|
||||
exists(Commit e |
|
||||
e.getAnAffectedFile() = f and
|
||||
e.daysToNow() <= 180 and
|
||||
not artificialChange(e)
|
||||
) and
|
||||
exists(f.getMetrics().getNumberOfLinesOfCode())
|
||||
select f, 1
|
||||
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* @name Recent changes
|
||||
* @description Number of recent commits to this file.
|
||||
* @kind treemap
|
||||
* @id cpp/historical-number-of-recent-changes
|
||||
* @treemap.warnOn highValues
|
||||
* @metricType file
|
||||
* @metricAggregate avg min max sum
|
||||
* @tags external-data
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import external.VCS
|
||||
|
||||
from File f, int n
|
||||
where
|
||||
n =
|
||||
count(Commit e |
|
||||
e.getAnAffectedFile() = f and
|
||||
e.daysToNow() <= 180 and
|
||||
not artificialChange(e)
|
||||
) and
|
||||
exists(f.getMetrics().getNumberOfLinesOfCode())
|
||||
select f, n order by n desc
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* By default they fall back to the reasonable defaults provided in
|
||||
* `DefaultOptions.qll`, but by modifying this file, you can customize
|
||||
* the standard Semmle analyses to give better results for your project.
|
||||
* the standard analyses to give better results for your project.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -15,31 +15,31 @@ import cpp
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import TaintedWithPath
|
||||
|
||||
predicate taintedChild(Expr e, Expr tainted) {
|
||||
(
|
||||
isAllocationExpr(e)
|
||||
or
|
||||
any(MulExpr me | me.getAChild() instanceof SizeofOperator) = e
|
||||
) and
|
||||
tainted = e.getAChild() and
|
||||
/**
|
||||
* Holds if `alloc` is an allocation, and `tainted` is a child of it that is a
|
||||
* taint sink.
|
||||
*/
|
||||
predicate allocSink(Expr alloc, Expr tainted) {
|
||||
isAllocationExpr(alloc) and
|
||||
tainted = alloc.getAChild() and
|
||||
tainted.getUnspecifiedType() instanceof IntegralType
|
||||
}
|
||||
|
||||
class TaintedAllocationSizeConfiguration extends TaintTrackingConfiguration {
|
||||
override predicate isSink(Element tainted) { taintedChild(_, tainted) }
|
||||
override predicate isSink(Element tainted) { allocSink(_, tainted) }
|
||||
}
|
||||
|
||||
predicate taintedAllocSize(
|
||||
Expr e, Expr source, PathNode sourceNode, PathNode sinkNode, string taintCause
|
||||
Expr source, Expr alloc, PathNode sourceNode, PathNode sinkNode, string taintCause
|
||||
) {
|
||||
isUserInput(source, taintCause) and
|
||||
exists(Expr tainted |
|
||||
taintedChild(e, tainted) and
|
||||
allocSink(alloc, tainted) and
|
||||
taintedWithPath(source, tainted, sourceNode, sinkNode)
|
||||
)
|
||||
}
|
||||
|
||||
from Expr e, Expr source, PathNode sourceNode, PathNode sinkNode, string taintCause
|
||||
where taintedAllocSize(e, source, sourceNode, sinkNode, taintCause)
|
||||
select e, sourceNode, sinkNode, "This allocation size is derived from $@ and might overflow",
|
||||
from Expr source, Expr alloc, PathNode sourceNode, PathNode sinkNode, string taintCause
|
||||
where taintedAllocSize(source, alloc, sourceNode, sinkNode, taintCause)
|
||||
select alloc, sourceNode, sinkNode, "This allocation size is derived from $@ and might overflow",
|
||||
source, "user input (" + taintCause + ")"
|
||||
|
||||
@@ -198,12 +198,12 @@ class InitializationFunction extends Function {
|
||||
)
|
||||
or
|
||||
// If we have no definition, we look at SAL annotations
|
||||
not this.isDefined() and
|
||||
not this.hasDefinition() and
|
||||
this.getParameter(i).(SALParameter).isOut() and
|
||||
evidence = SuggestiveSALAnnotation()
|
||||
or
|
||||
// We have some external information that this function conditionally initializes
|
||||
not this.isDefined() and
|
||||
not this.hasDefinition() and
|
||||
any(ValidatedExternalCondInitFunction vc).isExternallyVerified(this, i) and
|
||||
evidence = ExternalEvidence()
|
||||
}
|
||||
@@ -406,7 +406,7 @@ class ConditionalInitializationFunction extends InitializationFunction {
|
||||
* Explicitly ignore pure virtual functions.
|
||||
*/
|
||||
|
||||
this.isDefined() and
|
||||
this.hasDefinition() and
|
||||
this.paramNotReassignedAt(this, i, c) and
|
||||
not this instanceof PureVirtualFunction
|
||||
)
|
||||
@@ -616,11 +616,11 @@ private predicate functionSignature(Function f, string qualifiedName, string typ
|
||||
* are never statically linked together.
|
||||
*/
|
||||
private Function getAPossibleDefinition(Function undefinedFunction) {
|
||||
not undefinedFunction.isDefined() and
|
||||
not undefinedFunction.hasDefinition() and
|
||||
exists(string qn, string typeSig |
|
||||
functionSignature(undefinedFunction, qn, typeSig) and functionSignature(result, qn, typeSig)
|
||||
) and
|
||||
result.isDefined()
|
||||
result.hasDefinition()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -631,7 +631,7 @@ private Function getAPossibleDefinition(Function undefinedFunction) {
|
||||
*/
|
||||
private Function getTarget1(Call c) {
|
||||
result = VirtualDispatch::getAViableTarget(c) and
|
||||
result.isDefined()
|
||||
result.hasDefinition()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,7 +21,7 @@ where
|
||||
destBase = baseType(destType) and
|
||||
destBase.getSize() != sourceBase.getSize() and
|
||||
not dest.isInMacroExpansion() and
|
||||
// If the source type is a char* or void* then don't
|
||||
// If the source type is a `char*` or `void*` then don't
|
||||
// produce a result, because it is likely to be a false
|
||||
// positive.
|
||||
not sourceBase instanceof CharType and
|
||||
|
||||
@@ -21,7 +21,7 @@ where
|
||||
destBase = baseType(destType) and
|
||||
destBase.getSize() != sourceBase.getSize() and
|
||||
not dest.isInMacroExpansion() and
|
||||
// If the source type is a char* or void* then don't
|
||||
// If the source type is a `char*` or `void*` then don't
|
||||
// produce a result, because it is likely to be a false
|
||||
// positive.
|
||||
not sourceBase instanceof CharType and
|
||||
|
||||
@@ -24,9 +24,9 @@ private predicate isCharSzPtrExpr(Expr e) {
|
||||
from Expr sizeofExpr, Expr e
|
||||
where
|
||||
// If we see an addWithSizeof then we expect the type of
|
||||
// the pointer expression to be char* or void*. Otherwise it
|
||||
// the pointer expression to be `char*` or `void*`. Otherwise it
|
||||
// is probably a mistake.
|
||||
addWithSizeof(e, sizeofExpr, _) and not isCharSzPtrExpr(e)
|
||||
select sizeofExpr,
|
||||
"Suspicious sizeof offset in a pointer arithmetic expression. " + "The type of the pointer is " +
|
||||
e.getFullyConverted().getType().toString() + "."
|
||||
"Suspicious sizeof offset in a pointer arithmetic expression. The type of the pointer is $@.",
|
||||
e.getFullyConverted().getType() as t, t.toString()
|
||||
|
||||
@@ -2,3 +2,5 @@
|
||||
- qlpack: codeql-cpp
|
||||
- apply: code-scanning-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
- apply: codeql-suites/exclude-slow-queries.yml
|
||||
from: codeql-cpp
|
||||
|
||||
@@ -2,13 +2,10 @@
|
||||
- qlpack: codeql-cpp
|
||||
- apply: lgtm-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
# These queries are infeasible to compute on large projects:
|
||||
- apply: codeql-suites/exclude-slow-queries.yml
|
||||
from: codeql-cpp
|
||||
# These are only for IDE use.
|
||||
- exclude:
|
||||
query path:
|
||||
- Security/CWE/CWE-497/ExposedSystemData.ql
|
||||
- Critical/DescriptorMayNotBeClosed.ql
|
||||
- Critical/DescriptorNeverClosed.ql
|
||||
- Critical/FileMayNotBeClosed.ql
|
||||
- Critical/FileNeverClosed.ql
|
||||
- Critical/MemoryMayNotBeFreed.ql
|
||||
- Critical/MemoryNeverFreed.ql
|
||||
tags contain:
|
||||
- ide-contextual-queries/local-definitions
|
||||
- ide-contextual-queries/local-references
|
||||
|
||||
6
cpp/ql/src/codeql-suites/cpp-security-and-quality.qls
Normal file
6
cpp/ql/src/codeql-suites/cpp-security-and-quality.qls
Normal file
@@ -0,0 +1,6 @@
|
||||
- description: Security-and-quality queries for C and C++
|
||||
- qlpack: codeql-cpp
|
||||
- apply: security-and-quality-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
- apply: codeql-suites/exclude-slow-queries.yml
|
||||
from: codeql-cpp
|
||||
6
cpp/ql/src/codeql-suites/cpp-security-extended.qls
Normal file
6
cpp/ql/src/codeql-suites/cpp-security-extended.qls
Normal file
@@ -0,0 +1,6 @@
|
||||
- description: Security-extended queries for C and C++
|
||||
- qlpack: codeql-cpp
|
||||
- apply: security-extended-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
- apply: codeql-suites/exclude-slow-queries.yml
|
||||
from: codeql-cpp
|
||||
11
cpp/ql/src/codeql-suites/exclude-slow-queries.yml
Normal file
11
cpp/ql/src/codeql-suites/exclude-slow-queries.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
- description: C/C++ queries which are infeasible to compute on large projects
|
||||
# These queries are infeasible to compute on large projects:
|
||||
- exclude:
|
||||
query path:
|
||||
- Security/CWE/CWE-497/ExposedSystemData.ql
|
||||
- Critical/DescriptorMayNotBeClosed.ql
|
||||
- Critical/DescriptorNeverClosed.ql
|
||||
- Critical/FileMayNotBeClosed.ql
|
||||
- Critical/FileNeverClosed.ql
|
||||
- Critical/MemoryMayNotBeFreed.ql
|
||||
- Critical/MemoryNeverFreed.ql
|
||||
@@ -32,6 +32,7 @@ import semmle.code.cpp.Enum
|
||||
import semmle.code.cpp.Member
|
||||
import semmle.code.cpp.Field
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.MemberFunction
|
||||
import semmle.code.cpp.Parameter
|
||||
import semmle.code.cpp.Variable
|
||||
import semmle.code.cpp.Initializer
|
||||
|
||||
@@ -1 +1,7 @@
|
||||
/**
|
||||
* DEPRECATED: use `import cpp` instead of `import default`.
|
||||
*
|
||||
* Provides classes and predicates for working with C/C++ code.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -132,6 +132,7 @@ private predicate constructorCallTypeMention(ConstructorCall cc, TypeMention tm)
|
||||
* - `"X"` for macro accesses
|
||||
* - `"I"` for import / include directives
|
||||
*/
|
||||
cached
|
||||
Top definitionOf(Top e, string kind) {
|
||||
(
|
||||
// call -> function called
|
||||
@@ -213,3 +214,11 @@ Top definitionOf(Top e, string kind) {
|
||||
// later on.
|
||||
strictcount(result.getLocation()) < 10
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an appropriately encoded version of a filename `name`
|
||||
* passed by the VS Code extension in order to coincide with the
|
||||
* output of `.getFile()` on locatable entities.
|
||||
*/
|
||||
cached
|
||||
File getEncodedFile(string name) { result.getAbsolutePath().replaceAll(":", "_") = name }
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* This library proves that a subset of pointer dereferences in a program are
|
||||
* safe, i.e. in-bounds.
|
||||
* It does so by first defining what a pointer dereference is (on the IR
|
||||
* `Instruction` level), and then using the array length analysis and the range
|
||||
* analysis together to prove that some of these pointer dereferences are safe.
|
||||
*
|
||||
* The analysis is soundy, i.e. it is sound if no undefined behaviour is present
|
||||
* in the program.
|
||||
* Furthermore, it crucially depends on the soundiness of the range analysis and
|
||||
* the array length analysis.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
private import experimental.semmle.code.cpp.rangeanalysis.ArrayLengthAnalysis
|
||||
private import semmle.code.cpp.rangeanalysis.RangeAnalysis
|
||||
|
||||
/**
|
||||
* Gets the instruction that computes the address of memory that `i` accesses.
|
||||
* Only holds if `i` dereferences a pointer, not when the computation of the
|
||||
* memory address is constant, or if the address of a local variable is loaded/stored to.
|
||||
*/
|
||||
private Instruction getMemoryAddressInstruction(Instruction i) {
|
||||
(
|
||||
result = i.(FieldAddressInstruction).getObjectAddress() or
|
||||
result = i.(LoadInstruction).getSourceAddress() or
|
||||
result = i.(StoreInstruction).getDestinationAddress()
|
||||
) and
|
||||
not result instanceof FieldAddressInstruction and
|
||||
not result instanceof VariableAddressInstruction and
|
||||
not result instanceof ConstantValueInstruction
|
||||
}
|
||||
|
||||
/**
|
||||
* All instructions that dereference a pointer.
|
||||
*/
|
||||
class PointerDereferenceInstruction extends Instruction {
|
||||
PointerDereferenceInstruction() { exists(getMemoryAddressInstruction(this)) }
|
||||
|
||||
Instruction getAddress() { result = getMemoryAddressInstruction(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ptrDeref` can be proven to always access allocated memory.
|
||||
*/
|
||||
predicate inBounds(PointerDereferenceInstruction ptrDeref) {
|
||||
exists(Length length, int lengthDelta, Offset offset, int offsetDelta |
|
||||
knownArrayLength(ptrDeref.getAddress(), length, lengthDelta, offset, offsetDelta) and
|
||||
// lower bound - note that we treat a pointer that accesses an array of
|
||||
// length 0 as on upper-bound violation, but not as a lower-bound violation
|
||||
(
|
||||
offset instanceof ZeroOffset and
|
||||
offsetDelta >= 0
|
||||
or
|
||||
offset instanceof OpOffset and
|
||||
exists(int lowerBoundDelta |
|
||||
boundedOperand(offset.(OpOffset).getOperand(), any(ZeroBound b), lowerBoundDelta,
|
||||
/*upper*/ false, _) and
|
||||
lowerBoundDelta + offsetDelta >= 0
|
||||
)
|
||||
) and
|
||||
// upper bound
|
||||
(
|
||||
// both offset and length are only integers
|
||||
length instanceof ZeroLength and
|
||||
offset instanceof ZeroOffset and
|
||||
offsetDelta < lengthDelta
|
||||
or
|
||||
exists(int lengthBound |
|
||||
// array length is variable+integer, and there's a fixed (integer-only)
|
||||
// lower bound on the variable, so we can guarantee this access is always in-bounds
|
||||
length instanceof VNLength and
|
||||
offset instanceof ZeroOffset and
|
||||
boundedInstruction(length.(VNLength).getInstruction(), any(ZeroBound b), lengthBound,
|
||||
/* upper*/ false, _) and
|
||||
offsetDelta < lengthBound + lengthDelta
|
||||
)
|
||||
or
|
||||
exists(int offsetBoundDelta |
|
||||
length instanceof ZeroLength and
|
||||
offset instanceof OpOffset and
|
||||
boundedOperand(offset.(OpOffset).getOperand(), any(ZeroBound b), offsetBoundDelta,
|
||||
/* upper */ true, _) and
|
||||
// offset <= offsetBoundDelta, so offset + offsetDelta <= offsetDelta + offsetBoundDelta
|
||||
// Thus, in-bounds if offsetDelta + offsetBoundDelta < lengthDelta
|
||||
// as we have length instanceof ZeroLength
|
||||
offsetDelta + offsetBoundDelta < lengthDelta
|
||||
)
|
||||
or
|
||||
exists(ValueNumberBound b, int offsetBoundDelta |
|
||||
length instanceof VNLength and
|
||||
offset instanceof OpOffset and
|
||||
b.getValueNumber() = length.(VNLength).getValueNumber() and
|
||||
// It holds that offset <= length + offsetBoundDelta
|
||||
boundedOperand(offset.(OpOffset).getOperand(), b, offsetBoundDelta, /*upper*/ true, _) and
|
||||
// it also holds that
|
||||
offsetDelta < lengthDelta - offsetBoundDelta
|
||||
// taking both inequalities together we get
|
||||
// offset <= length + offsetBoundDelta
|
||||
// => offset + offsetDelta <= length + offsetBoundDelta + offsetDelta < length + offsetBoundDelta + lengthDelta - offsetBoundDelta
|
||||
// as required
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
65
cpp/ql/src/external/CodeDuplication.qll
vendored
65
cpp/ql/src/external/CodeDuplication.qll
vendored
@@ -1,3 +1,5 @@
|
||||
/** Provides classes for detecting duplicate or similar code. */
|
||||
|
||||
import cpp
|
||||
|
||||
private string relativePath(File file) { result = file.getRelativePath().replaceAll("\\", "/") }
|
||||
@@ -8,9 +10,12 @@ private predicate tokenLocation(string path, int sl, int sc, int ec, int el, Cop
|
||||
tokens(copy, index, sl, sc, ec, el)
|
||||
}
|
||||
|
||||
/** A token block used for detection of duplicate and similar code. */
|
||||
class Copy extends @duplication_or_similarity {
|
||||
/** Gets the index of the last token in this block. */
|
||||
private int lastToken() { result = max(int i | tokens(this, i, _, _, _, _) | i) }
|
||||
|
||||
/** Gets the index of the token in this block starting at the location `loc`, if any. */
|
||||
int tokenStartingAt(Location loc) {
|
||||
exists(string filepath, int startline, int startcol |
|
||||
loc.hasLocationInfo(filepath, startline, startcol, _, _) and
|
||||
@@ -18,6 +23,7 @@ class Copy extends @duplication_or_similarity {
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the index of the token in this block ending at the location `loc`, if any. */
|
||||
int tokenEndingAt(Location loc) {
|
||||
exists(string filepath, int endline, int endcol |
|
||||
loc.hasLocationInfo(filepath, _, _, endline, endcol) and
|
||||
@@ -25,24 +31,38 @@ class Copy extends @duplication_or_similarity {
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the line on which the first token in this block starts. */
|
||||
int sourceStartLine() { tokens(this, 0, result, _, _, _) }
|
||||
|
||||
/** Gets the column on which the first token in this block starts. */
|
||||
int sourceStartColumn() { tokens(this, 0, _, result, _, _) }
|
||||
|
||||
/** Gets the line on which the last token in this block ends. */
|
||||
int sourceEndLine() { tokens(this, lastToken(), _, _, result, _) }
|
||||
|
||||
/** Gets the column on which the last token in this block ends. */
|
||||
int sourceEndColumn() { tokens(this, lastToken(), _, _, _, result) }
|
||||
|
||||
/** Gets the number of lines containing at least (part of) one token in this block. */
|
||||
int sourceLines() { result = this.sourceEndLine() + 1 - this.sourceStartLine() }
|
||||
|
||||
/** Gets an opaque identifier for the equivalence class of this block. */
|
||||
int getEquivalenceClass() { duplicateCode(this, _, result) or similarCode(this, _, result) }
|
||||
|
||||
/** Gets the source file in which this block appears. */
|
||||
File sourceFile() {
|
||||
exists(string name | duplicateCode(this, name, _) or similarCode(this, name, _) |
|
||||
name.replaceAll("\\", "/") = relativePath(result)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
@@ -53,25 +73,30 @@ class Copy extends @duplication_or_similarity {
|
||||
endcolumn = sourceEndColumn()
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
/** A block of duplicated code. */
|
||||
class DuplicateBlock extends Copy, @duplication {
|
||||
override string toString() { result = "Duplicate code: " + sourceLines() + " duplicated lines." }
|
||||
}
|
||||
|
||||
/** A block of similar code. */
|
||||
class SimilarBlock extends Copy, @similarity {
|
||||
override string toString() {
|
||||
result = "Similar code: " + sourceLines() + " almost duplicated lines."
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets a function with a body and a location. */
|
||||
FunctionDeclarationEntry sourceMethod() {
|
||||
result.isDefinition() and
|
||||
exists(result.getLocation()) and
|
||||
numlines(unresolveElement(result.getFunction()), _, _, _)
|
||||
}
|
||||
|
||||
/** Gets the number of member functions in `c` with a body and a location. */
|
||||
int numberOfSourceMethods(Class c) {
|
||||
result =
|
||||
count(FunctionDeclarationEntry m |
|
||||
@@ -108,6 +133,10 @@ private predicate duplicateStatement(
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `m1` is a function with `total` lines, and `m2` is a function
|
||||
* that has `duplicate` lines in common with `m1`.
|
||||
*/
|
||||
predicate duplicateStatements(
|
||||
FunctionDeclarationEntry m1, FunctionDeclarationEntry m2, int duplicate, int total
|
||||
) {
|
||||
@@ -115,13 +144,16 @@ predicate duplicateStatements(
|
||||
total = strictcount(statementInMethod(m1))
|
||||
}
|
||||
|
||||
/**
|
||||
* Find pairs of methods are identical
|
||||
*/
|
||||
/** Holds if `m` and other are identical functions. */
|
||||
predicate duplicateMethod(FunctionDeclarationEntry m, FunctionDeclarationEntry other) {
|
||||
exists(int total | duplicateStatements(m, other, total, total))
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* Holds if `line` in `f` is similar to a line somewhere else.
|
||||
*/
|
||||
predicate similarLines(File f, int line) {
|
||||
exists(SimilarBlock b | b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()])
|
||||
}
|
||||
@@ -152,6 +184,7 @@ private predicate similarLinesCoveredFiles(File f, File otherFile) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `coveredLines` lines of `f` are similar to lines in `otherFile`. */
|
||||
predicate similarLinesCovered(File f, int coveredLines, File otherFile) {
|
||||
exists(int numLines | numLines = f.getMetrics().getNumberOfLines() |
|
||||
similarLinesCoveredFiles(f, otherFile) and
|
||||
@@ -166,6 +199,11 @@ predicate similarLinesCovered(File f, int coveredLines, File otherFile) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* Holds if `line` in `f` is duplicated by a line somewhere else.
|
||||
*/
|
||||
predicate duplicateLines(File f, int line) {
|
||||
exists(DuplicateBlock b |
|
||||
b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()]
|
||||
@@ -182,6 +220,7 @@ private predicate duplicateLinesPerEquivalenceClass(int equivClass, int lines, F
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `coveredLines` lines of `f` are duplicates of lines in `otherFile`. */
|
||||
predicate duplicateLinesCovered(File f, int coveredLines, File otherFile) {
|
||||
exists(int numLines | numLines = f.getMetrics().getNumberOfLines() |
|
||||
exists(int coveredApprox |
|
||||
@@ -206,6 +245,7 @@ predicate duplicateLinesCovered(File f, int coveredLines, File otherFile) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if most of `f` (`percent`%) is similar to `other`. */
|
||||
predicate similarFiles(File f, File other, int percent) {
|
||||
exists(int covered, int total |
|
||||
similarLinesCovered(f, covered, other) and
|
||||
@@ -216,6 +256,7 @@ predicate similarFiles(File f, File other, int percent) {
|
||||
not duplicateFiles(f, other, _)
|
||||
}
|
||||
|
||||
/** Holds if most of `f` (`percent`%) is duplicated by `other`. */
|
||||
predicate duplicateFiles(File f, File other, int percent) {
|
||||
exists(int covered, int total |
|
||||
duplicateLinesCovered(f, covered, other) and
|
||||
@@ -225,6 +266,10 @@ predicate duplicateFiles(File f, File other, int percent) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if most member functions of `c` (`numDup` out of `total`) are
|
||||
* duplicates of member functions in `other`.
|
||||
*/
|
||||
predicate mostlyDuplicateClassBase(Class c, Class other, int numDup, int total) {
|
||||
numDup =
|
||||
strictcount(FunctionDeclarationEntry m1 |
|
||||
@@ -240,6 +285,11 @@ predicate mostlyDuplicateClassBase(Class c, Class other, int numDup, int total)
|
||||
(numDup * 100) / total > 80
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if most member functions of `c` are duplicates of member functions in
|
||||
* `other`. Provides the human-readable `message` to describe the amount of
|
||||
* duplication.
|
||||
*/
|
||||
predicate mostlyDuplicateClass(Class c, Class other, string message) {
|
||||
exists(int numDup, int total |
|
||||
mostlyDuplicateClassBase(c, other, numDup, total) and
|
||||
@@ -264,12 +314,21 @@ predicate mostlyDuplicateClass(Class c, Class other, string message) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `f` and `other` are similar or duplicates. */
|
||||
predicate fileLevelDuplication(File f, File other) {
|
||||
similarFiles(f, other, _) or duplicateFiles(f, other, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if most member functions of `c` are duplicates of member functions in
|
||||
* `other`.
|
||||
*/
|
||||
predicate classLevelDuplication(Class c, Class other) { mostlyDuplicateClass(c, other, _) }
|
||||
|
||||
/**
|
||||
* Holds if `line` in `f` should be allowed to be duplicated. This is the case
|
||||
* for `#include` directives.
|
||||
*/
|
||||
predicate whitelistedLineForDuplication(File f, int line) {
|
||||
exists(Include i | i.getFile() = f and i.getLocation().getStartLine() = line)
|
||||
}
|
||||
|
||||
21
cpp/ql/src/external/DefectFilter.qll
vendored
21
cpp/ql/src/external/DefectFilter.qll
vendored
@@ -1,31 +1,52 @@
|
||||
/** Provides a class for working with defect query results stored in dashboard databases. */
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Holds if `id` in the opaque identifier of a result reported by query `queryPath`,
|
||||
* such that `message` is the associated message and the location of the result spans
|
||||
* column `startcolumn` of line `startline` to column `endcolumn` of line `endline`
|
||||
* in file `filepath`.
|
||||
*
|
||||
* For more information, see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
external predicate defectResults(
|
||||
int id, string queryPath, string file, int startline, int startcol, int endline, int endcol,
|
||||
string message
|
||||
);
|
||||
|
||||
/**
|
||||
* A defect query result stored in a dashboard database.
|
||||
*/
|
||||
class DefectResult extends int {
|
||||
DefectResult() { defectResults(this, _, _, _, _, _, _, _) }
|
||||
|
||||
/** Gets the path of the query that reported the result. */
|
||||
string getQueryPath() { defectResults(this, result, _, _, _, _, _, _) }
|
||||
|
||||
/** Gets the file in which this query result was reported. */
|
||||
File getFile() {
|
||||
exists(string path |
|
||||
defectResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the line on which the location of this query result starts. */
|
||||
int getStartLine() { defectResults(this, _, _, result, _, _, _, _) }
|
||||
|
||||
/** Gets the column on which the location of this query result starts. */
|
||||
int getStartColumn() { defectResults(this, _, _, _, result, _, _, _) }
|
||||
|
||||
/** Gets the line on which the location of this query result ends. */
|
||||
int getEndLine() { defectResults(this, _, _, _, _, result, _, _) }
|
||||
|
||||
/** Gets the column on which the location of this query result ends. */
|
||||
int getEndColumn() { defectResults(this, _, _, _, _, _, result, _) }
|
||||
|
||||
/** Gets the message associated with this query result. */
|
||||
string getMessage() { defectResults(this, _, _, _, _, _, _, result) }
|
||||
|
||||
/** Gets the URL corresponding to the location of this query result. */
|
||||
string getURL() {
|
||||
result =
|
||||
"file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" +
|
||||
|
||||
35
cpp/ql/src/external/ExternalArtifact.qll
vendored
35
cpp/ql/src/external/ExternalArtifact.qll
vendored
@@ -1,26 +1,45 @@
|
||||
/**
|
||||
* Provides classes for working with external data.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* An external data item.
|
||||
*/
|
||||
class ExternalData extends @externalDataElement {
|
||||
/** Gets the path of the file this data was loaded from. */
|
||||
string getDataPath() { externalData(this, result, _, _) }
|
||||
|
||||
/**
|
||||
* Gets the path of the file this data was loaded from, with its
|
||||
* extension replaced by `.ql`.
|
||||
*/
|
||||
string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") }
|
||||
|
||||
/** Gets the number of fields in this data item. */
|
||||
int getNumFields() { result = 1 + max(int i | externalData(this, _, i, _) | i) }
|
||||
|
||||
string getField(int index) { externalData(this, _, index, result) }
|
||||
/** Gets the value of the `i`th field of this data item. */
|
||||
string getField(int i) { externalData(this, _, i, result) }
|
||||
|
||||
int getFieldAsInt(int index) { result = getField(index).toInt() }
|
||||
/** Gets the integer value of the `i`th field of this data item. */
|
||||
int getFieldAsInt(int i) { result = getField(i).toInt() }
|
||||
|
||||
float getFieldAsFloat(int index) { result = getField(index).toFloat() }
|
||||
/** Gets the floating-point value of the `i`th field of this data item. */
|
||||
float getFieldAsFloat(int i) { result = getField(i).toFloat() }
|
||||
|
||||
date getFieldAsDate(int index) { result = getField(index).toDate() }
|
||||
/** Gets the value of the `i`th field of this data item, interpreted as a date. */
|
||||
date getFieldAsDate(int i) { result = getField(i).toDate() }
|
||||
|
||||
/** Gets a textual representation of this data item. */
|
||||
string toString() { result = getQueryPath() + ": " + buildTupleString(0) }
|
||||
|
||||
private string buildTupleString(int start) {
|
||||
start = getNumFields() - 1 and result = getField(start)
|
||||
/** Gets a textual representation of this data item, starting with the `n`th field. */
|
||||
private string buildTupleString(int n) {
|
||||
n = getNumFields() - 1 and result = getField(n)
|
||||
or
|
||||
start < getNumFields() - 1 and result = getField(start) + "," + buildTupleString(start + 1)
|
||||
n < getNumFields() - 1 and result = getField(n) + "," + buildTupleString(n + 1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +52,9 @@ class DefectExternalData extends ExternalData {
|
||||
this.getNumFields() = 2
|
||||
}
|
||||
|
||||
/** Gets the URL associated with this data item. */
|
||||
string getURL() { result = getField(0) }
|
||||
|
||||
/** Gets the message associated with this data item. */
|
||||
string getMessage() { result = getField(1) }
|
||||
}
|
||||
|
||||
29
cpp/ql/src/external/MetricFilter.qll
vendored
29
cpp/ql/src/external/MetricFilter.qll
vendored
@@ -1,31 +1,58 @@
|
||||
/** Provides a class for working with metric query results stored in dashboard databases. */
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Holds if `id` in the opaque identifier of a result reported by query `queryPath`,
|
||||
* such that `value` is the reported metric value and the location of the result spans
|
||||
* column `startcolumn` of line `startline` to column `endcolumn` of line `endline`
|
||||
* in file `filepath`.
|
||||
*
|
||||
* For more information, see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
external predicate metricResults(
|
||||
int id, string queryPath, string file, int startline, int startcol, int endline, int endcol,
|
||||
float value
|
||||
);
|
||||
|
||||
/**
|
||||
* A metric query result stored in a dashboard database.
|
||||
*/
|
||||
class MetricResult extends int {
|
||||
MetricResult() { metricResults(this, _, _, _, _, _, _, _) }
|
||||
|
||||
/** Gets the path of the query that reported the result. */
|
||||
string getQueryPath() { metricResults(this, result, _, _, _, _, _, _) }
|
||||
|
||||
/** Gets the file in which this query result was reported. */
|
||||
File getFile() {
|
||||
exists(string path |
|
||||
metricResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the line on which the location of this query result starts. */
|
||||
int getStartLine() { metricResults(this, _, _, result, _, _, _, _) }
|
||||
|
||||
/** Gets the column on which the location of this query result starts. */
|
||||
int getStartColumn() { metricResults(this, _, _, _, result, _, _, _) }
|
||||
|
||||
/** Gets the line on which the location of this query result ends. */
|
||||
int getEndLine() { metricResults(this, _, _, _, _, result, _, _) }
|
||||
|
||||
/** Gets the column on which the location of this query result ends. */
|
||||
int getEndColumn() { metricResults(this, _, _, _, _, _, result, _) }
|
||||
|
||||
/**
|
||||
* Holds if there is a `Location` entity whose location is the same as
|
||||
* the location of this query result.
|
||||
*/
|
||||
predicate hasMatchingLocation() { exists(this.getMatchingLocation()) }
|
||||
|
||||
/**
|
||||
* Gets the `Location` entity whose location is the same as the location
|
||||
* of this query result.
|
||||
*/
|
||||
Location getMatchingLocation() {
|
||||
result.getFile() = this.getFile() and
|
||||
result.getStartLine() = this.getStartLine() and
|
||||
@@ -34,8 +61,10 @@ class MetricResult extends int {
|
||||
result.getEndColumn() = this.getEndColumn()
|
||||
}
|
||||
|
||||
/** Gets the value associated with this query result. */
|
||||
float getValue() { metricResults(this, _, _, _, _, _, _, result) }
|
||||
|
||||
/** Gets the URL corresponding to the location of this query result. */
|
||||
string getURL() {
|
||||
result =
|
||||
"file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" +
|
||||
|
||||
92
cpp/ql/src/external/VCS.qll
vendored
92
cpp/ql/src/external/VCS.qll
vendored
@@ -1,92 +0,0 @@
|
||||
import cpp
|
||||
|
||||
class Commit extends @svnentry {
|
||||
Commit() {
|
||||
svnaffectedfiles(this, _, _) and
|
||||
exists(date svnDate, date snapshotDate |
|
||||
svnentries(this, _, _, svnDate, _) and
|
||||
snapshotDate(snapshotDate) and
|
||||
svnDate <= snapshotDate
|
||||
)
|
||||
}
|
||||
|
||||
string toString() { result = this.getRevisionName() }
|
||||
|
||||
string getRevisionName() { svnentries(this, result, _, _, _) }
|
||||
|
||||
string getAuthor() { svnentries(this, _, result, _, _) }
|
||||
|
||||
date getDate() { svnentries(this, _, _, result, _) }
|
||||
|
||||
int getChangeSize() { svnentries(this, _, _, _, result) }
|
||||
|
||||
string getMessage() { svnentrymsg(this, result) }
|
||||
|
||||
string getAnAffectedFilePath(string action) {
|
||||
exists(File rawFile | svnaffectedfiles(this, unresolveElement(rawFile), action) |
|
||||
result = rawFile.getAbsolutePath()
|
||||
)
|
||||
}
|
||||
|
||||
string getAnAffectedFilePath() { result = getAnAffectedFilePath(_) }
|
||||
|
||||
File getAnAffectedFile(string action) {
|
||||
// Workaround for incorrect keys in SVN data
|
||||
exists(File svnFile | svnFile.getAbsolutePath() = result.getAbsolutePath() |
|
||||
svnaffectedfiles(this, unresolveElement(svnFile), action)
|
||||
) and
|
||||
exists(result.getMetrics().getNumberOfLinesOfCode())
|
||||
}
|
||||
|
||||
File getAnAffectedFile() { exists(string action | result = this.getAnAffectedFile(action)) }
|
||||
|
||||
private predicate churnForFile(File f, int added, int deleted) {
|
||||
// Workaround for incorrect keys in SVN data
|
||||
exists(File svnFile | svnFile.getAbsolutePath() = f.getAbsolutePath() |
|
||||
svnchurn(this, unresolveElement(svnFile), added, deleted)
|
||||
) and
|
||||
exists(f.getMetrics().getNumberOfLinesOfCode())
|
||||
}
|
||||
|
||||
int getRecentChurnForFile(File f) {
|
||||
exists(int added, int deleted | churnForFile(f, added, deleted) and result = added + deleted)
|
||||
}
|
||||
|
||||
int getRecentAdditionsForFile(File f) { churnForFile(f, result, _) }
|
||||
|
||||
int getRecentDeletionsForFile(File f) { churnForFile(f, _, result) }
|
||||
|
||||
predicate isRecent() { recentCommit(this) }
|
||||
|
||||
int daysToNow() {
|
||||
exists(date now | snapshotDate(now) | result = getDate().daysTo(now) and result >= 0)
|
||||
}
|
||||
}
|
||||
|
||||
class Author extends string {
|
||||
Author() { exists(Commit e | this = e.getAuthor()) }
|
||||
|
||||
Commit getACommit() { result.getAuthor() = this }
|
||||
|
||||
File getAnEditedFile() { result = this.getACommit().getAnAffectedFile() }
|
||||
}
|
||||
|
||||
predicate recentCommit(Commit e) {
|
||||
exists(date snapshotDate, date commitDate, int days |
|
||||
snapshotDate(snapshotDate) and
|
||||
e.getDate() = commitDate and
|
||||
days = commitDate.daysTo(snapshotDate) and
|
||||
days >= 0 and
|
||||
days <= 60
|
||||
)
|
||||
}
|
||||
|
||||
date firstChange(File f) {
|
||||
result = min(Commit e, date toMin | f = e.getAnAffectedFile() and toMin = e.getDate() | toMin)
|
||||
}
|
||||
|
||||
predicate firstCommit(Commit e) {
|
||||
not exists(File f | f = e.getAnAffectedFile() | firstChange(f) < e.getDate())
|
||||
}
|
||||
|
||||
predicate artificialChange(Commit e) { firstCommit(e) or e.getChangeSize() >= 50000 }
|
||||
20
cpp/ql/src/external/tests/DefectFromSVN.ql
vendored
20
cpp/ql/src/external/tests/DefectFromSVN.ql
vendored
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* @name Defect from SVN
|
||||
* @description A test case for creating a defect from SVN data.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @tags external-data
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import external.ExternalArtifact
|
||||
import external.VCS
|
||||
|
||||
predicate numCommits(File f, int i) { i = count(Commit e | e.getAnAffectedFile() = f) }
|
||||
|
||||
predicate maxCommits(int i) { i = max(File f, int j | numCommits(f, j) | j) }
|
||||
|
||||
from File f, int i
|
||||
where numCommits(f, i) and maxCommits(i)
|
||||
select f, "This file has " + i + " commits."
|
||||
17
cpp/ql/src/external/tests/MetricFromSVN.ql
vendored
17
cpp/ql/src/external/tests/MetricFromSVN.ql
vendored
@@ -1,17 +0,0 @@
|
||||
/**
|
||||
* @name Metric from SVN
|
||||
* @description Find number of commits for a file
|
||||
* @treemap.warnOn lowValues
|
||||
* @metricType file
|
||||
* @tags external-data
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import external.VCS
|
||||
|
||||
predicate numCommits(File f, int i) { i = count(Commit e | e.getAnAffectedFile() = f) }
|
||||
|
||||
from File f, int i
|
||||
where numCommits(f, i)
|
||||
select f, i
|
||||
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* @name Filter: exclude results from files that have not recently been
|
||||
* edited
|
||||
* @description Use this filter to return results only if they are
|
||||
* located in files that have been modified in the 60 days
|
||||
* before the date of the snapshot.
|
||||
* @kind problem
|
||||
* @id cpp/recent-defects-filter
|
||||
* @tags filter
|
||||
* external-data
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import external.DefectFilter
|
||||
import external.VCS
|
||||
|
||||
pragma[noopt]
|
||||
private predicate recent(File file) {
|
||||
exists(Commit e | file = e.getAnAffectedFile() | e.isRecent() and not artificialChange(e))
|
||||
}
|
||||
|
||||
from DefectResult res
|
||||
where recent(res.getFile())
|
||||
select res, res.getMessage()
|
||||
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* @name Metric filter: exclude results from files that have not
|
||||
* recently been edited
|
||||
* @description Use this filter to return results only if they are
|
||||
* located in files that have been modified in the 60 days
|
||||
* before the snapshot.
|
||||
* @kind treemap
|
||||
* @id cpp/recent-defects-for-metric-filter
|
||||
* @tags filter
|
||||
* external-data
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import external.MetricFilter
|
||||
import external.VCS
|
||||
|
||||
pragma[noopt]
|
||||
private predicate recent(File file) {
|
||||
exists(Commit e | file = e.getAnAffectedFile() | e.isRecent() and not artificialChange(e))
|
||||
}
|
||||
|
||||
from MetricResult res
|
||||
where recent(res.getFile())
|
||||
select res, res.getValue()
|
||||
16
cpp/ql/src/localDefinitions.ql
Normal file
16
cpp/ql/src/localDefinitions.ql
Normal 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
|
||||
16
cpp/ql/src/localReferences.ql
Normal file
16
cpp/ql/src/localReferences.ql
Normal 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
|
||||
@@ -1 +1,7 @@
|
||||
/**
|
||||
* DEPRECATED: Objective C is no longer supported.
|
||||
*
|
||||
* Import `cpp` instead of `objc`.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
9
cpp/ql/src/semmle/code/cpp/ASTConsistency.ql
Normal file
9
cpp/ql/src/semmle/code/cpp/ASTConsistency.ql
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* @name AST Consistency Check
|
||||
* @description Performs consistency checks on the Abstract Syntax Tree. This query should have no results.
|
||||
* @kind table
|
||||
* @id cpp/ast-consistency-check
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import CastConsistency
|
||||
@@ -1,9 +0,0 @@
|
||||
/**
|
||||
* @name AST Sanity Check
|
||||
* @description Performs sanity checks on the Abstract Syntax Tree. This query should have no results.
|
||||
* @kind table
|
||||
* @id cpp/ast-sanity-check
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import CastSanity
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* Provides a class and predicate for recognizing files that are likely to have been generated
|
||||
* automatically.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Comments
|
||||
import semmle.code.cpp.File
|
||||
import semmle.code.cpp.Preprocessor
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Provides classes representing C++ classes, including structs, unions, and template classes.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Type
|
||||
import semmle.code.cpp.UserType
|
||||
import semmle.code.cpp.metrics.MetricClass
|
||||
@@ -458,6 +462,15 @@ class Class extends UserType {
|
||||
exists(ClassDerivation d | d.getDerivedClass() = this and d = result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets class derivation number `index` of this class/struct, for example the
|
||||
* `public B` is derivation 1 in the following code:
|
||||
* ```
|
||||
* class D : public A, public B, public C {
|
||||
* ...
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
ClassDerivation getDerivation(int index) {
|
||||
exists(ClassDerivation d | d.getDerivedClass() = this and d.getIndex() = index and d = result)
|
||||
}
|
||||
@@ -900,6 +913,22 @@ class AbstractClass extends Class {
|
||||
class TemplateClass extends Class {
|
||||
TemplateClass() { usertypes(underlyingElement(this), _, 6) }
|
||||
|
||||
/**
|
||||
* Gets a class instantiated from this template.
|
||||
*
|
||||
* For example for `MyTemplateClass<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() {
|
||||
result.isConstructedFrom(this) and
|
||||
exists(result.getATemplateArgument())
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Provides classes representing C and C++ comments.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Location
|
||||
import semmle.code.cpp.Element
|
||||
|
||||
@@ -13,8 +17,20 @@ class Comment extends Locatable, @comment {
|
||||
|
||||
override Location getLocation() { comments(underlyingElement(this), _, result) }
|
||||
|
||||
/**
|
||||
* Gets the text of this comment, including the opening `//` or `/*`, and the closing `*``/` if
|
||||
* present.
|
||||
*/
|
||||
string getContents() { comments(underlyingElement(this), result, _) }
|
||||
|
||||
/**
|
||||
* Gets the AST element this comment is associated with. For example, the comment in the
|
||||
* following code is associated with the declaration of `j`.
|
||||
* ```
|
||||
* int i;
|
||||
* int j; // Comment on j
|
||||
* ```
|
||||
*/
|
||||
Element getCommentedElement() {
|
||||
commentbinding(underlyingElement(this), unresolveElement(result))
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Provides a class representing individual compiler invocations that occurred during the build.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.File
|
||||
|
||||
/*
|
||||
@@ -21,9 +25,9 @@ private predicate idOf(@compilation x, int y) = equivalenceRelation(id/2)(x, y)
|
||||
* Three things happen to each file during a compilation:
|
||||
*
|
||||
* 1. The file is compiled by a real compiler, such as gcc or VC.
|
||||
* 2. The file is parsed by Semmle's C++ front-end.
|
||||
* 2. The file is parsed by the CodeQL C++ front-end.
|
||||
* 3. The parsed representation is converted to database tables by
|
||||
* Semmle's extractor.
|
||||
* the CodeQL extractor.
|
||||
*
|
||||
* This class provides CPU and elapsed time information for steps 2 and 3,
|
||||
* but not for step 1.
|
||||
@@ -40,6 +44,7 @@ class Compilation extends @compilation {
|
||||
/** Gets a file compiled during this invocation. */
|
||||
File getAFileCompiled() { result = getFileCompiled(_) }
|
||||
|
||||
/** Gets the `i`th file compiled during this invocation */
|
||||
File getFileCompiled(int i) { compilation_compiling_files(this, i, unresolveElement(result)) }
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Provides classes for working with C and C++ declarations.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Element
|
||||
import semmle.code.cpp.Specifier
|
||||
import semmle.code.cpp.Namespace
|
||||
@@ -25,7 +29,7 @@ private import semmle.code.cpp.internal.QualifiedName as Q
|
||||
* `DeclarationEntry`, because they always have a unique source location.
|
||||
* `EnumConstant` and `FriendDecl` are both examples of this.
|
||||
*/
|
||||
abstract class Declaration extends Locatable, @declaration {
|
||||
class Declaration extends Locatable, @declaration {
|
||||
/**
|
||||
* Gets the innermost namespace which contains this declaration.
|
||||
*
|
||||
@@ -98,7 +102,12 @@ abstract class Declaration extends Locatable, @declaration {
|
||||
this.hasQualifiedName(namespaceQualifier, "", baseName)
|
||||
}
|
||||
|
||||
override string toString() { result = this.getName() }
|
||||
/**
|
||||
* Gets a description of this `Declaration` for display purposes.
|
||||
*/
|
||||
string getDescription() { result = this.getName() }
|
||||
|
||||
final override string toString() { result = this.getDescription() }
|
||||
|
||||
/**
|
||||
* Gets the name of this declaration.
|
||||
@@ -161,6 +170,7 @@ abstract class Declaration extends Locatable, @declaration {
|
||||
/** Holds if the declaration has a definition. */
|
||||
predicate hasDefinition() { exists(this.getDefinition()) }
|
||||
|
||||
/** DEPRECATED: Use `hasDefinition` instead. */
|
||||
predicate isDefined() { hasDefinition() }
|
||||
|
||||
/** Gets the preferred location of this declaration, if any. */
|
||||
@@ -303,7 +313,7 @@ abstract class DeclarationEntry extends Locatable {
|
||||
* available), or the name declared by this entry otherwise.
|
||||
*/
|
||||
string getCanonicalName() {
|
||||
if getDeclaration().isDefined()
|
||||
if getDeclaration().hasDefinition()
|
||||
then result = getDeclaration().getDefinition().getName()
|
||||
else result = getName()
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Provides classes representing warnings generated during compilation.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Location
|
||||
|
||||
/** A compiler-generated error, warning or remark. */
|
||||
@@ -11,6 +15,7 @@ class Diagnostic extends Locatable, @diagnostic {
|
||||
/** Gets the error code for this compiler message. */
|
||||
string getTag() { diagnostics(underlyingElement(this), _, result, _, _, _) }
|
||||
|
||||
/** Holds if `s` is the error code for this compiler message. */
|
||||
predicate hasTag(string s) { this.getTag() = s }
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* Provides the `Element` class, which is the base class for all classes representing C or C++
|
||||
* program elements.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Location
|
||||
private import semmle.code.cpp.Enclosing
|
||||
private import semmle.code.cpp.internal.ResolveClass
|
||||
@@ -261,8 +266,14 @@ private predicate isFromUninstantiatedTemplateRec(Element e, Element template) {
|
||||
class StaticAssert extends Locatable, @static_assert {
|
||||
override string toString() { result = "static_assert(..., \"" + getMessage() + "\")" }
|
||||
|
||||
/**
|
||||
* Gets the expression which this static assertion ensures is true.
|
||||
*/
|
||||
Expr getCondition() { static_asserts(underlyingElement(this), unresolveElement(result), _, _) }
|
||||
|
||||
/**
|
||||
* Gets the message which will be reported by the compiler if this static assertion fails.
|
||||
*/
|
||||
string getMessage() { static_asserts(underlyingElement(this), _, result, _) }
|
||||
|
||||
override Location getLocation() { static_asserts(underlyingElement(this), _, _, result) }
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Provides predicates for finding the smallest element that encloses an expression or statement.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Provides classes representing C/C++ enums and enum constants.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Type
|
||||
private import semmle.code.cpp.internal.ResolveClass
|
||||
|
||||
@@ -19,6 +23,17 @@ class Enum extends UserType, IntegralOrEnumType {
|
||||
/** Gets an enumerator of this enumeration. */
|
||||
EnumConstant getAnEnumConstant() { result.getDeclaringEnum() = this }
|
||||
|
||||
/**
|
||||
* Gets the enumerator of this enumeration that was declared at the zero-based position `index`.
|
||||
* For example, `zero` is at index 2 in the following declaration:
|
||||
* ```
|
||||
* enum ReversedOrder {
|
||||
* two = 2,
|
||||
* one = 1,
|
||||
* zero = 0
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
EnumConstant getEnumConstant(int index) {
|
||||
enumconstants(unresolveElement(result), underlyingElement(this), index, _, _, _)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Provides classes representing C structure members and C++ non-static member variables.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Variable
|
||||
import semmle.code.cpp.Enum
|
||||
import semmle.code.cpp.exprs.Access
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
/**
|
||||
* Provides classes representing files and folders.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Element
|
||||
import semmle.code.cpp.Declaration
|
||||
import semmle.code.cpp.metrics.MetricFile
|
||||
|
||||
/** A file or folder. */
|
||||
abstract class Container extends Locatable, @container {
|
||||
class Container extends Locatable, @container {
|
||||
/**
|
||||
* Gets the absolute, canonical path of this container, using forward slashes
|
||||
* as path separator.
|
||||
@@ -28,7 +32,7 @@ abstract class Container extends Locatable, @container {
|
||||
* a bare root prefix, that is, the path has no path segments. A container
|
||||
* whose absolute path has no segments is always a `Folder`, not a `File`.
|
||||
*/
|
||||
abstract string getAbsolutePath();
|
||||
string getAbsolutePath() { none() } // overridden by subclasses
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getLocation` instead.
|
||||
@@ -36,7 +40,7 @@ abstract class Container extends Locatable, @container {
|
||||
*
|
||||
* For more information see [Providing URLs](https://help.semmle.com/QL/learn-ql/ql/locations.html#providing-urls).
|
||||
*/
|
||||
abstract deprecated string getURL();
|
||||
deprecated string getURL() { none() } // overridden by subclasses
|
||||
|
||||
/**
|
||||
* Gets the relative path of this file or folder from the root folder of the
|
||||
@@ -261,18 +265,6 @@ class File extends Container, @file {
|
||||
/** Holds if this file was compiled as C++ (at any point). */
|
||||
predicate compiledAsCpp() { fileannotations(underlyingElement(this), 1, "compiled as c++", "1") }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Objective-C is no longer supported.
|
||||
* Holds if this file was compiled as Objective C (at any point).
|
||||
*/
|
||||
deprecated predicate compiledAsObjC() { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Objective-C is no longer supported.
|
||||
* Holds if this file was compiled as Objective C++ (at any point).
|
||||
*/
|
||||
deprecated predicate compiledAsObjCpp() { none() }
|
||||
|
||||
/**
|
||||
* Holds if this file was compiled by a Microsoft compiler (at any point).
|
||||
*
|
||||
@@ -316,14 +308,6 @@ class File extends Container, @file {
|
||||
exists(Include i | i.getFile() = this and i.getIncludedFile() = result)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getParentContainer` instead.
|
||||
* Gets the folder which contains this file.
|
||||
*/
|
||||
deprecated Folder getParent() {
|
||||
containerparent(unresolveElement(result), underlyingElement(this))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this file may be from source. This predicate holds for all files
|
||||
* except the dummy file, whose name is the empty string, which contains
|
||||
@@ -341,28 +325,6 @@ class File extends Container, @file {
|
||||
/** Gets the metric file. */
|
||||
MetricFile getMetrics() { result = this }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getAbsolutePath` instead.
|
||||
* Gets the full name of this file, for example:
|
||||
* "/usr/home/me/myprogram.c".
|
||||
*/
|
||||
deprecated string getName() { files(underlyingElement(this), result, _, _, _) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getAbsolutePath` instead.
|
||||
* Holds if this file has the specified full name.
|
||||
*
|
||||
* Example usage: `f.hasName("/usr/home/me/myprogram.c")`.
|
||||
*/
|
||||
deprecated predicate hasName(string name) { name = this.getName() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getAbsolutePath` instead.
|
||||
* Gets the full name of this file, for example
|
||||
* "/usr/home/me/myprogram.c".
|
||||
*/
|
||||
deprecated string getFullName() { result = this.getName() }
|
||||
|
||||
/**
|
||||
* Gets the remainder of the base name after the first dot character. Note
|
||||
* that the name of this predicate is in plural form, unlike `getExtension`,
|
||||
@@ -377,22 +339,6 @@ class File extends Container, @file {
|
||||
*/
|
||||
string getExtensions() { files(underlyingElement(this), _, _, result, _) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getBaseName` instead.
|
||||
* Gets the name and extension(s), but not path, of a file. For example,
|
||||
* if the full name is "/path/to/filename.a.bcd" then the filename is
|
||||
* "filename.a.bcd".
|
||||
*/
|
||||
deprecated string getFileName() {
|
||||
// [a/b.c/d/]fileName
|
||||
// ^ beginAfter
|
||||
exists(string fullName, int beginAfter |
|
||||
fullName = this.getName() and
|
||||
beginAfter = max(int i | i = -1 or fullName.charAt(i) = "/" | i) and
|
||||
result = fullName.suffix(beginAfter + 1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the short name of this file, that is, the prefix of its base name up
|
||||
* to (but not including) the first dot character if there is one, or the
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Provides a class representing C++ `friend` declarations.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Declaration
|
||||
private import semmle.code.cpp.internal.ResolveClass
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Provides classes for working with functions, including template functions.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Location
|
||||
import semmle.code.cpp.Member
|
||||
import semmle.code.cpp.Class
|
||||
@@ -103,6 +107,9 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
|
||||
/**
|
||||
* Holds if this function is declared to be `constexpr`.
|
||||
*
|
||||
* Note that this does not hold if the function has been declared
|
||||
* `consteval`.
|
||||
*/
|
||||
predicate isDeclaredConstexpr() { this.hasSpecifier("declared_constexpr") }
|
||||
|
||||
@@ -115,9 +122,16 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
* template <typename T> constexpr int g(T x) { return f(x); }
|
||||
* ```
|
||||
* `g<int>` is declared constexpr, but is not constexpr.
|
||||
*
|
||||
* Will also hold if this function is `consteval`.
|
||||
*/
|
||||
predicate isConstexpr() { this.hasSpecifier("is_constexpr") }
|
||||
|
||||
/**
|
||||
* Holds if this function is declared to be `consteval`.
|
||||
*/
|
||||
predicate isConsteval() { this.hasSpecifier("is_consteval") }
|
||||
|
||||
/**
|
||||
* Holds if this function is declared with `__attribute__((naked))` or
|
||||
* `__declspec(naked)`.
|
||||
@@ -174,17 +188,8 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
* For example: for a function `int Foo(int p1, int p2)` this would
|
||||
* return `int p1, int p2`.
|
||||
*/
|
||||
string getParameterString() { result = getParameterStringFrom(0) }
|
||||
|
||||
private string getParameterStringFrom(int index) {
|
||||
index = getNumberOfParameters() and
|
||||
result = ""
|
||||
or
|
||||
index = getNumberOfParameters() - 1 and
|
||||
result = getParameter(index).getTypedName()
|
||||
or
|
||||
index < getNumberOfParameters() - 1 and
|
||||
result = getParameter(index).getTypedName() + ", " + getParameterStringFrom(index + 1)
|
||||
string getParameterString() {
|
||||
result = concat(int i | | min(getParameter(i).getTypedName()), ", " order by i)
|
||||
}
|
||||
|
||||
/** Gets a call to this function. */
|
||||
@@ -606,18 +611,8 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
|
||||
* For example: for a function 'int Foo(int p1, int p2)' this would
|
||||
* return 'int p1, int p2'.
|
||||
*/
|
||||
string getParameterString() { result = getParameterStringFrom(0) }
|
||||
|
||||
private string getParameterStringFrom(int index) {
|
||||
index = getNumberOfParameters() and
|
||||
result = ""
|
||||
or
|
||||
index = getNumberOfParameters() - 1 and
|
||||
result = getParameterDeclarationEntry(index).getTypedName()
|
||||
or
|
||||
index < getNumberOfParameters() - 1 and
|
||||
result =
|
||||
getParameterDeclarationEntry(index).getTypedName() + ", " + getParameterStringFrom(index + 1)
|
||||
string getParameterString() {
|
||||
result = concat(int i | | min(getParameterDeclarationEntry(i).getTypedName()), ", " order by i)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -707,427 +702,6 @@ class TopLevelFunction extends Function {
|
||||
override string getCanonicalQLClass() { result = "TopLevelFunction" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ function declared as a member of a class [N4140 9.3]. This includes
|
||||
* static member functions. For example the functions `MyStaticMemberFunction`
|
||||
* and `MyMemberFunction` in:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* void MyMemberFunction() {
|
||||
* DoSomething();
|
||||
* }
|
||||
*
|
||||
* static void MyStaticMemberFunction() {
|
||||
* DoSomething();
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class MemberFunction extends Function {
|
||||
MemberFunction() { this.isMember() }
|
||||
|
||||
override string getCanonicalQLClass() {
|
||||
not this instanceof CopyAssignmentOperator and
|
||||
not this instanceof MoveAssignmentOperator and
|
||||
result = "MemberFunction"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of parameters of this function, including any implicit
|
||||
* `this` parameter.
|
||||
*/
|
||||
override int getEffectiveNumberOfParameters() {
|
||||
if isStatic() then result = getNumberOfParameters() else result = getNumberOfParameters() + 1
|
||||
}
|
||||
|
||||
/** Holds if this member is private. */
|
||||
predicate isPrivate() { this.hasSpecifier("private") }
|
||||
|
||||
/** Holds if this member is protected. */
|
||||
predicate isProtected() { this.hasSpecifier("protected") }
|
||||
|
||||
/** Holds if this member is public. */
|
||||
predicate isPublic() { this.hasSpecifier("public") }
|
||||
|
||||
/** Holds if this function overrides that function. */
|
||||
predicate overrides(MemberFunction that) {
|
||||
overrides(underlyingElement(this), unresolveElement(that))
|
||||
}
|
||||
|
||||
/** Gets a directly overridden function. */
|
||||
MemberFunction getAnOverriddenFunction() { this.overrides(result) }
|
||||
|
||||
/** Gets a directly overriding function. */
|
||||
MemberFunction getAnOverridingFunction() { result.overrides(this) }
|
||||
|
||||
/**
|
||||
* Gets the declaration entry for this member function that is within the
|
||||
* class body.
|
||||
*/
|
||||
FunctionDeclarationEntry getClassBodyDeclarationEntry() {
|
||||
if strictcount(getADeclarationEntry()) = 1
|
||||
then result = getDefinition()
|
||||
else (
|
||||
result = getADeclarationEntry() and result != getDefinition()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ virtual function. For example the two functions called
|
||||
* `myVirtualFunction` in the following code are each a
|
||||
* `VirtualFunction`:
|
||||
* ```
|
||||
* class A {
|
||||
* public:
|
||||
* virtual void myVirtualFunction() = 0;
|
||||
* };
|
||||
*
|
||||
* class B: public A {
|
||||
* public:
|
||||
* virtual void myVirtualFunction() {
|
||||
* doSomething();
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class VirtualFunction extends MemberFunction {
|
||||
VirtualFunction() { this.hasSpecifier("virtual") or purefunctions(underlyingElement(this)) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "VirtualFunction" }
|
||||
|
||||
/** Holds if this virtual function is pure. */
|
||||
predicate isPure() { this instanceof PureVirtualFunction }
|
||||
|
||||
/**
|
||||
* Holds if this function was declared with the `override` specifier
|
||||
* [N4140 10.3].
|
||||
*/
|
||||
predicate isOverrideExplicit() { this.hasSpecifier("override") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ pure virtual function [N4140 10.4]. For example the first function
|
||||
* called `myVirtualFunction` in the following code:
|
||||
* ```
|
||||
* class A {
|
||||
* public:
|
||||
* virtual void myVirtualFunction() = 0;
|
||||
* };
|
||||
*
|
||||
* class B: public A {
|
||||
* public:
|
||||
* virtual void myVirtualFunction() {
|
||||
* doSomething();
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class PureVirtualFunction extends VirtualFunction {
|
||||
PureVirtualFunction() { purefunctions(underlyingElement(this)) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "PureVirtualFunction" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A const C++ member function [N4140 9.3.1/4]. A const function has the
|
||||
* `const` specifier and does not modify the state of its class. For example
|
||||
* the member function `day` in the following code:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* ...
|
||||
*
|
||||
* int day() const {
|
||||
* return d;
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class ConstMemberFunction extends MemberFunction {
|
||||
ConstMemberFunction() { this.hasSpecifier("const") }
|
||||
|
||||
override string getCanonicalQLClass() { result = "ConstMemberFunction" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ constructor [N4140 12.1]. For example the function `MyClass` in the
|
||||
* following code is a constructor:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass() {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class Constructor extends MemberFunction {
|
||||
Constructor() { functions(underlyingElement(this), _, 2) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "Constructor" }
|
||||
|
||||
/**
|
||||
* Holds if this constructor serves as a default constructor.
|
||||
*
|
||||
* This holds for constructors with zero formal parameters. It also holds
|
||||
* for constructors which have a non-zero number of formal parameters,
|
||||
* provided that every parameter has a default value.
|
||||
*/
|
||||
predicate isDefault() { forall(Parameter p | p = this.getAParameter() | p.hasInitializer()) }
|
||||
|
||||
/**
|
||||
* Gets an entry in the constructor's initializer list, or a
|
||||
* compiler-generated action which initializes a base class or member
|
||||
* variable.
|
||||
*/
|
||||
ConstructorInit getAnInitializer() { result = getInitializer(_) }
|
||||
|
||||
/**
|
||||
* Gets an entry in the constructor's initializer list, or a
|
||||
* compiler-generated action which initializes a base class or member
|
||||
* variable. The index specifies the order in which the initializer is
|
||||
* to be evaluated.
|
||||
*/
|
||||
ConstructorInit getInitializer(int i) {
|
||||
exprparents(unresolveElement(result), i, underlyingElement(this))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that defines an implicit conversion.
|
||||
*/
|
||||
abstract class ImplicitConversionFunction extends MemberFunction {
|
||||
abstract Type getSourceType();
|
||||
|
||||
abstract Type getDestType();
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ constructor that also defines an implicit conversion. For example the
|
||||
* function `MyClass` in the following code is a `ConversionConstructor`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass(const MyOtherClass &from) {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class ConversionConstructor extends Constructor, ImplicitConversionFunction {
|
||||
ConversionConstructor() {
|
||||
strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
|
||||
not hasSpecifier("explicit") and
|
||||
not this instanceof CopyConstructor
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() {
|
||||
not this instanceof MoveConstructor and result = "ConversionConstructor"
|
||||
}
|
||||
|
||||
/** Gets the type this `ConversionConstructor` takes as input. */
|
||||
override Type getSourceType() { result = this.getParameter(0).getType() }
|
||||
|
||||
/** Gets the type this `ConversionConstructor` is a constructor of. */
|
||||
override Type getDestType() { result = this.getDeclaringType() }
|
||||
}
|
||||
|
||||
private predicate hasCopySignature(MemberFunction f) {
|
||||
f.getParameter(0).getUnspecifiedType().(LValueReferenceType).getBaseType() = f.getDeclaringType()
|
||||
}
|
||||
|
||||
private predicate hasMoveSignature(MemberFunction f) {
|
||||
f.getParameter(0).getUnspecifiedType().(RValueReferenceType).getBaseType() = f.getDeclaringType()
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ copy constructor [N4140 12.8]. For example the function `MyClass` in
|
||||
* the following code is a `CopyConstructor`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass(const MyClass &from) {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* As per the standard, a copy constructor of class `T` is a non-template
|
||||
* constructor whose first parameter has type `T&`, `const T&`, `volatile
|
||||
* T&`, or `const volatile T&`, and either there are no other parameters,
|
||||
* or the rest of the parameters all have default values.
|
||||
*
|
||||
* For template classes, it can generally not be determined until instantiation
|
||||
* whether a constructor is a copy constructor. For such classes, `CopyConstructor`
|
||||
* over-approximates the set of copy constructors; if an under-approximation is
|
||||
* desired instead, see the member predicate
|
||||
* `mayNotBeCopyConstructorInInstantiation`.
|
||||
*/
|
||||
class CopyConstructor extends Constructor {
|
||||
CopyConstructor() {
|
||||
hasCopySignature(this) and
|
||||
(
|
||||
// The rest of the parameters all have default values
|
||||
forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer())
|
||||
or
|
||||
// or this is a template class, in which case the default values have
|
||||
// not been extracted even if they exist. In that case, we assume that
|
||||
// there are default values present since that is the most common case
|
||||
// in real-world code.
|
||||
getDeclaringType() instanceof TemplateClass
|
||||
) and
|
||||
not exists(getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() { result = "CopyConstructor" }
|
||||
|
||||
/**
|
||||
* Holds if we cannot determine that this constructor will become a copy
|
||||
* constructor in all instantiations. Depending on template parameters of the
|
||||
* enclosing class, this may become an ordinary constructor or a copy
|
||||
* constructor.
|
||||
*/
|
||||
predicate mayNotBeCopyConstructorInInstantiation() {
|
||||
// In general, default arguments of template classes can only be
|
||||
// type-checked for each template instantiation; if an argument in an
|
||||
// instantiation fails to type-check then the corresponding parameter has
|
||||
// no default argument in the instantiation.
|
||||
getDeclaringType() instanceof TemplateClass and
|
||||
getNumberOfParameters() > 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ move constructor [N4140 12.8]. For example the function `MyClass` in
|
||||
* the following code is a `MoveConstructor`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass(MyClass &&from) {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* As per the standard, a move constructor of class `T` is a non-template
|
||||
* constructor whose first parameter is `T&&`, `const T&&`, `volatile T&&`,
|
||||
* or `const volatile T&&`, and either there are no other parameters, or
|
||||
* the rest of the parameters all have default values.
|
||||
*
|
||||
* For template classes, it can generally not be determined until instantiation
|
||||
* whether a constructor is a move constructor. For such classes, `MoveConstructor`
|
||||
* over-approximates the set of move constructors; if an under-approximation is
|
||||
* desired instead, see the member predicate
|
||||
* `mayNotBeMoveConstructorInInstantiation`.
|
||||
*/
|
||||
class MoveConstructor extends Constructor {
|
||||
MoveConstructor() {
|
||||
hasMoveSignature(this) and
|
||||
(
|
||||
// The rest of the parameters all have default values
|
||||
forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer())
|
||||
or
|
||||
// or this is a template class, in which case the default values have
|
||||
// not been extracted even if they exist. In that case, we assume that
|
||||
// there are default values present since that is the most common case
|
||||
// in real-world code.
|
||||
getDeclaringType() instanceof TemplateClass
|
||||
) and
|
||||
not exists(getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() { result = "MoveConstructor" }
|
||||
|
||||
/**
|
||||
* Holds if we cannot determine that this constructor will become a move
|
||||
* constructor in all instantiations. Depending on template parameters of the
|
||||
* enclosing class, this may become an ordinary constructor or a move
|
||||
* constructor.
|
||||
*/
|
||||
predicate mayNotBeMoveConstructorInInstantiation() {
|
||||
// In general, default arguments of template classes can only be
|
||||
// type-checked for each template instantiation; if an argument in an
|
||||
// instantiation fails to type-check then the corresponding parameter has
|
||||
// no default argument in the instantiation.
|
||||
getDeclaringType() instanceof TemplateClass and
|
||||
getNumberOfParameters() > 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ constructor that takes no arguments ('default' constructor). This
|
||||
* is the constructor that is invoked when no initializer is given. For
|
||||
* example the function `MyClass` in the following code is a
|
||||
* `NoArgConstructor`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass() {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class NoArgConstructor extends Constructor {
|
||||
NoArgConstructor() { this.getNumberOfParameters() = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ destructor [N4140 12.4]. For example the function `~MyClass` in the
|
||||
* following code is a destructor:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* ~MyClass() {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class Destructor extends MemberFunction {
|
||||
Destructor() { functions(underlyingElement(this), _, 3) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "Destructor" }
|
||||
|
||||
/**
|
||||
* Gets a compiler-generated action which destructs a base class or member
|
||||
* variable.
|
||||
*/
|
||||
DestructorDestruction getADestruction() { result = getDestruction(_) }
|
||||
|
||||
/**
|
||||
* Gets a compiler-generated action which destructs a base class or member
|
||||
* variable. The index specifies the order in which the destruction should
|
||||
* be evaluated.
|
||||
*/
|
||||
DestructorDestruction getDestruction(int i) {
|
||||
exprparents(unresolveElement(result), i, underlyingElement(this))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ conversion operator [N4140 12.3.2]. For example the function
|
||||
* `operator int` in the following code is a `ConversionOperator`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* operator int();
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class ConversionOperator extends MemberFunction, ImplicitConversionFunction {
|
||||
ConversionOperator() { functions(underlyingElement(this), _, 4) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "ConversionOperator" }
|
||||
|
||||
override Type getSourceType() { result = this.getDeclaringType() }
|
||||
|
||||
override Type getDestType() { result = this.getType() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ user-defined operator [N4140 13.5].
|
||||
*/
|
||||
@@ -1139,64 +713,6 @@ class Operator extends Function {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ copy assignment operator [N4140 12.8]. For example the function
|
||||
* `operator=` in the following code is a `CopyAssignmentOperator`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass &operator=(const MyClass &other);
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* As per the standard, a copy assignment operator of class `T` is a
|
||||
* non-template non-static member function with the name `operator=` that
|
||||
* takes exactly one parameter of type `T`, `T&`, `const T&`, `volatile
|
||||
* T&`, or `const volatile T&`.
|
||||
*/
|
||||
class CopyAssignmentOperator extends Operator {
|
||||
CopyAssignmentOperator() {
|
||||
hasName("operator=") and
|
||||
(
|
||||
hasCopySignature(this)
|
||||
or
|
||||
// Unlike CopyConstructor, this member allows a non-reference
|
||||
// parameter.
|
||||
getParameter(0).getUnspecifiedType() = getDeclaringType()
|
||||
) and
|
||||
not exists(this.getParameter(1)) and
|
||||
not exists(getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() { result = "CopyAssignmentOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ move assignment operator [N4140 12.8]. For example the function
|
||||
* `operator=` in the following code is a `MoveAssignmentOperator`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass &operator=(MyClass &&other);
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* As per the standard, a move assignment operator of class `T` is a
|
||||
* non-template non-static member function with the name `operator=` that
|
||||
* takes exactly one parameter of type `T&&`, `const T&&`, `volatile T&&`,
|
||||
* or `const volatile T&&`.
|
||||
*/
|
||||
class MoveAssignmentOperator extends Operator {
|
||||
MoveAssignmentOperator() {
|
||||
hasName("operator=") and
|
||||
hasMoveSignature(this) and
|
||||
not exists(this.getParameter(1)) and
|
||||
not exists(getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() { result = "MoveAssignmentOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ function which has a non-empty template argument list. For example
|
||||
* the function `myTemplateFunction` in the following code:
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* Provides classes representing C/C++ `#include`, `#include_next`, and `#import` preprocessor
|
||||
* directives.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Preprocessor
|
||||
|
||||
/**
|
||||
|
||||
487
cpp/ql/src/semmle/code/cpp/MemberFunction.qll
Normal file
487
cpp/ql/src/semmle/code/cpp/MemberFunction.qll
Normal file
@@ -0,0 +1,487 @@
|
||||
/**
|
||||
* Provides classes for working with C++ member functions, constructors, destructors,
|
||||
* and user-defined operators.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* A C++ function declared as a member of a class [N4140 9.3]. This includes
|
||||
* static member functions. For example the functions `MyStaticMemberFunction`
|
||||
* and `MyMemberFunction` in:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* void MyMemberFunction() {
|
||||
* DoSomething();
|
||||
* }
|
||||
*
|
||||
* static void MyStaticMemberFunction() {
|
||||
* DoSomething();
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class MemberFunction extends Function {
|
||||
MemberFunction() { this.isMember() }
|
||||
|
||||
override string getCanonicalQLClass() {
|
||||
not this instanceof CopyAssignmentOperator and
|
||||
not this instanceof MoveAssignmentOperator and
|
||||
result = "MemberFunction"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of parameters of this function, including any implicit
|
||||
* `this` parameter.
|
||||
*/
|
||||
override int getEffectiveNumberOfParameters() {
|
||||
if isStatic() then result = getNumberOfParameters() else result = getNumberOfParameters() + 1
|
||||
}
|
||||
|
||||
/** Holds if this member is private. */
|
||||
predicate isPrivate() { this.hasSpecifier("private") }
|
||||
|
||||
/** Holds if this member is protected. */
|
||||
predicate isProtected() { this.hasSpecifier("protected") }
|
||||
|
||||
/** Holds if this member is public. */
|
||||
predicate isPublic() { this.hasSpecifier("public") }
|
||||
|
||||
/** Holds if this function overrides that function. */
|
||||
predicate overrides(MemberFunction that) {
|
||||
overrides(underlyingElement(this), unresolveElement(that))
|
||||
}
|
||||
|
||||
/** Gets a directly overridden function. */
|
||||
MemberFunction getAnOverriddenFunction() { this.overrides(result) }
|
||||
|
||||
/** Gets a directly overriding function. */
|
||||
MemberFunction getAnOverridingFunction() { result.overrides(this) }
|
||||
|
||||
/**
|
||||
* Gets the declaration entry for this member function that is within the
|
||||
* class body.
|
||||
*/
|
||||
FunctionDeclarationEntry getClassBodyDeclarationEntry() {
|
||||
if strictcount(getADeclarationEntry()) = 1
|
||||
then result = getDefinition()
|
||||
else (
|
||||
result = getADeclarationEntry() and result != getDefinition()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ virtual function. For example the two functions called
|
||||
* `myVirtualFunction` in the following code are each a
|
||||
* `VirtualFunction`:
|
||||
* ```
|
||||
* class A {
|
||||
* public:
|
||||
* virtual void myVirtualFunction() = 0;
|
||||
* };
|
||||
*
|
||||
* class B: public A {
|
||||
* public:
|
||||
* virtual void myVirtualFunction() {
|
||||
* doSomething();
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class VirtualFunction extends MemberFunction {
|
||||
VirtualFunction() { this.hasSpecifier("virtual") or purefunctions(underlyingElement(this)) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "VirtualFunction" }
|
||||
|
||||
/** Holds if this virtual function is pure. */
|
||||
predicate isPure() { this instanceof PureVirtualFunction }
|
||||
|
||||
/**
|
||||
* Holds if this function was declared with the `override` specifier
|
||||
* [N4140 10.3].
|
||||
*/
|
||||
predicate isOverrideExplicit() { this.hasSpecifier("override") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ pure virtual function [N4140 10.4]. For example the first function
|
||||
* called `myVirtualFunction` in the following code:
|
||||
* ```
|
||||
* class A {
|
||||
* public:
|
||||
* virtual void myVirtualFunction() = 0;
|
||||
* };
|
||||
*
|
||||
* class B: public A {
|
||||
* public:
|
||||
* virtual void myVirtualFunction() {
|
||||
* doSomething();
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class PureVirtualFunction extends VirtualFunction {
|
||||
PureVirtualFunction() { purefunctions(underlyingElement(this)) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "PureVirtualFunction" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A const C++ member function [N4140 9.3.1/4]. A const function has the
|
||||
* `const` specifier and does not modify the state of its class. For example
|
||||
* the member function `day` in the following code:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* ...
|
||||
*
|
||||
* int day() const {
|
||||
* return d;
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class ConstMemberFunction extends MemberFunction {
|
||||
ConstMemberFunction() { this.hasSpecifier("const") }
|
||||
|
||||
override string getCanonicalQLClass() { result = "ConstMemberFunction" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ constructor [N4140 12.1]. For example the function `MyClass` in the
|
||||
* following code is a constructor:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass() {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class Constructor extends MemberFunction {
|
||||
Constructor() { functions(underlyingElement(this), _, 2) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "Constructor" }
|
||||
|
||||
/**
|
||||
* Holds if this constructor serves as a default constructor.
|
||||
*
|
||||
* This holds for constructors with zero formal parameters. It also holds
|
||||
* for constructors which have a non-zero number of formal parameters,
|
||||
* provided that every parameter has a default value.
|
||||
*/
|
||||
predicate isDefault() { forall(Parameter p | p = this.getAParameter() | p.hasInitializer()) }
|
||||
|
||||
/**
|
||||
* Gets an entry in the constructor's initializer list, or a
|
||||
* compiler-generated action which initializes a base class or member
|
||||
* variable.
|
||||
*/
|
||||
ConstructorInit getAnInitializer() { result = getInitializer(_) }
|
||||
|
||||
/**
|
||||
* Gets an entry in the constructor's initializer list, or a
|
||||
* compiler-generated action which initializes a base class or member
|
||||
* variable. The index specifies the order in which the initializer is
|
||||
* to be evaluated.
|
||||
*/
|
||||
ConstructorInit getInitializer(int i) {
|
||||
exprparents(unresolveElement(result), i, underlyingElement(this))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that defines an implicit conversion.
|
||||
*/
|
||||
abstract class ImplicitConversionFunction extends MemberFunction {
|
||||
/** Gets the type this `ImplicitConversionFunction` takes as input. */
|
||||
abstract Type getSourceType();
|
||||
|
||||
/** Gets the type this `ImplicitConversionFunction` converts to. */
|
||||
abstract Type getDestType();
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ constructor that also defines an implicit conversion. For example the
|
||||
* function `MyClass` in the following code is a `ConversionConstructor`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass(const MyOtherClass &from) {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class ConversionConstructor extends Constructor, ImplicitConversionFunction {
|
||||
ConversionConstructor() {
|
||||
strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
|
||||
not hasSpecifier("explicit") and
|
||||
not this instanceof CopyConstructor
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() {
|
||||
not this instanceof MoveConstructor and result = "ConversionConstructor"
|
||||
}
|
||||
|
||||
/** Gets the type this `ConversionConstructor` takes as input. */
|
||||
override Type getSourceType() { result = this.getParameter(0).getType() }
|
||||
|
||||
/** Gets the type this `ConversionConstructor` is a constructor of. */
|
||||
override Type getDestType() { result = this.getDeclaringType() }
|
||||
}
|
||||
|
||||
private predicate hasCopySignature(MemberFunction f) {
|
||||
f.getParameter(0).getUnspecifiedType().(LValueReferenceType).getBaseType() = f.getDeclaringType()
|
||||
}
|
||||
|
||||
private predicate hasMoveSignature(MemberFunction f) {
|
||||
f.getParameter(0).getUnspecifiedType().(RValueReferenceType).getBaseType() = f.getDeclaringType()
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ copy constructor [N4140 12.8]. For example the function `MyClass` in
|
||||
* the following code is a `CopyConstructor`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass(const MyClass &from) {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* As per the standard, a copy constructor of class `T` is a non-template
|
||||
* constructor whose first parameter has type `T&`, `const T&`, `volatile
|
||||
* T&`, or `const volatile T&`, and either there are no other parameters,
|
||||
* or the rest of the parameters all have default values.
|
||||
*
|
||||
* For template classes, it can generally not be determined until instantiation
|
||||
* whether a constructor is a copy constructor. For such classes, `CopyConstructor`
|
||||
* over-approximates the set of copy constructors; if an under-approximation is
|
||||
* desired instead, see the member predicate
|
||||
* `mayNotBeCopyConstructorInInstantiation`.
|
||||
*/
|
||||
class CopyConstructor extends Constructor {
|
||||
CopyConstructor() {
|
||||
hasCopySignature(this) and
|
||||
(
|
||||
// The rest of the parameters all have default values
|
||||
forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer())
|
||||
or
|
||||
// or this is a template class, in which case the default values have
|
||||
// not been extracted even if they exist. In that case, we assume that
|
||||
// there are default values present since that is the most common case
|
||||
// in real-world code.
|
||||
getDeclaringType() instanceof TemplateClass
|
||||
) and
|
||||
not exists(getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() { result = "CopyConstructor" }
|
||||
|
||||
/**
|
||||
* Holds if we cannot determine that this constructor will become a copy
|
||||
* constructor in all instantiations. Depending on template parameters of the
|
||||
* enclosing class, this may become an ordinary constructor or a copy
|
||||
* constructor.
|
||||
*/
|
||||
predicate mayNotBeCopyConstructorInInstantiation() {
|
||||
// In general, default arguments of template classes can only be
|
||||
// type-checked for each template instantiation; if an argument in an
|
||||
// instantiation fails to type-check then the corresponding parameter has
|
||||
// no default argument in the instantiation.
|
||||
getDeclaringType() instanceof TemplateClass and
|
||||
getNumberOfParameters() > 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ move constructor [N4140 12.8]. For example the function `MyClass` in
|
||||
* the following code is a `MoveConstructor`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass(MyClass &&from) {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* As per the standard, a move constructor of class `T` is a non-template
|
||||
* constructor whose first parameter is `T&&`, `const T&&`, `volatile T&&`,
|
||||
* or `const volatile T&&`, and either there are no other parameters, or
|
||||
* the rest of the parameters all have default values.
|
||||
*
|
||||
* For template classes, it can generally not be determined until instantiation
|
||||
* whether a constructor is a move constructor. For such classes, `MoveConstructor`
|
||||
* over-approximates the set of move constructors; if an under-approximation is
|
||||
* desired instead, see the member predicate
|
||||
* `mayNotBeMoveConstructorInInstantiation`.
|
||||
*/
|
||||
class MoveConstructor extends Constructor {
|
||||
MoveConstructor() {
|
||||
hasMoveSignature(this) and
|
||||
(
|
||||
// The rest of the parameters all have default values
|
||||
forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer())
|
||||
or
|
||||
// or this is a template class, in which case the default values have
|
||||
// not been extracted even if they exist. In that case, we assume that
|
||||
// there are default values present since that is the most common case
|
||||
// in real-world code.
|
||||
getDeclaringType() instanceof TemplateClass
|
||||
) and
|
||||
not exists(getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() { result = "MoveConstructor" }
|
||||
|
||||
/**
|
||||
* Holds if we cannot determine that this constructor will become a move
|
||||
* constructor in all instantiations. Depending on template parameters of the
|
||||
* enclosing class, this may become an ordinary constructor or a move
|
||||
* constructor.
|
||||
*/
|
||||
predicate mayNotBeMoveConstructorInInstantiation() {
|
||||
// In general, default arguments of template classes can only be
|
||||
// type-checked for each template instantiation; if an argument in an
|
||||
// instantiation fails to type-check then the corresponding parameter has
|
||||
// no default argument in the instantiation.
|
||||
getDeclaringType() instanceof TemplateClass and
|
||||
getNumberOfParameters() > 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ constructor that takes no arguments ('default' constructor). This
|
||||
* is the constructor that is invoked when no initializer is given. For
|
||||
* example the function `MyClass` in the following code is a
|
||||
* `NoArgConstructor`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass() {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class NoArgConstructor extends Constructor {
|
||||
NoArgConstructor() { this.getNumberOfParameters() = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ destructor [N4140 12.4]. For example the function `~MyClass` in the
|
||||
* following code is a destructor:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* ~MyClass() {
|
||||
* ...
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class Destructor extends MemberFunction {
|
||||
Destructor() { functions(underlyingElement(this), _, 3) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "Destructor" }
|
||||
|
||||
/**
|
||||
* Gets a compiler-generated action which destructs a base class or member
|
||||
* variable.
|
||||
*/
|
||||
DestructorDestruction getADestruction() { result = getDestruction(_) }
|
||||
|
||||
/**
|
||||
* Gets a compiler-generated action which destructs a base class or member
|
||||
* variable. The index specifies the order in which the destruction should
|
||||
* be evaluated.
|
||||
*/
|
||||
DestructorDestruction getDestruction(int i) {
|
||||
exprparents(unresolveElement(result), i, underlyingElement(this))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ conversion operator [N4140 12.3.2]. For example the function
|
||||
* `operator int` in the following code is a `ConversionOperator`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* operator int();
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class ConversionOperator extends MemberFunction, ImplicitConversionFunction {
|
||||
ConversionOperator() { functions(underlyingElement(this), _, 4) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "ConversionOperator" }
|
||||
|
||||
override Type getSourceType() { result = this.getDeclaringType() }
|
||||
|
||||
override Type getDestType() { result = this.getType() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ copy assignment operator [N4140 12.8]. For example the function
|
||||
* `operator=` in the following code is a `CopyAssignmentOperator`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass &operator=(const MyClass &other);
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* As per the standard, a copy assignment operator of class `T` is a
|
||||
* non-template non-static member function with the name `operator=` that
|
||||
* takes exactly one parameter of type `T`, `T&`, `const T&`, `volatile
|
||||
* T&`, or `const volatile T&`.
|
||||
*/
|
||||
class CopyAssignmentOperator extends Operator {
|
||||
CopyAssignmentOperator() {
|
||||
hasName("operator=") and
|
||||
(
|
||||
hasCopySignature(this)
|
||||
or
|
||||
// Unlike CopyConstructor, this member allows a non-reference
|
||||
// parameter.
|
||||
getParameter(0).getUnspecifiedType() = getDeclaringType()
|
||||
) and
|
||||
not exists(this.getParameter(1)) and
|
||||
not exists(getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() { result = "CopyAssignmentOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ move assignment operator [N4140 12.8]. For example the function
|
||||
* `operator=` in the following code is a `MoveAssignmentOperator`:
|
||||
* ```
|
||||
* class MyClass {
|
||||
* public:
|
||||
* MyClass &operator=(MyClass &&other);
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* As per the standard, a move assignment operator of class `T` is a
|
||||
* non-template non-static member function with the name `operator=` that
|
||||
* takes exactly one parameter of type `T&&`, `const T&&`, `volatile T&&`,
|
||||
* or `const volatile T&&`.
|
||||
*/
|
||||
class MoveAssignmentOperator extends Operator {
|
||||
MoveAssignmentOperator() {
|
||||
hasName("operator=") and
|
||||
hasMoveSignature(this) and
|
||||
not exists(this.getParameter(1)) and
|
||||
not exists(getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() { result = "MoveAssignmentOperator" }
|
||||
}
|
||||
@@ -79,7 +79,10 @@ class Namespace extends NameQualifyingElement, @namespace {
|
||||
/** Gets the metric namespace. */
|
||||
MetricNamespace getMetrics() { result = this }
|
||||
|
||||
override string toString() { result = this.getQualifiedName() }
|
||||
/** Gets a version of the `QualifiedName` that is more suitable for display purposes. */
|
||||
string getFriendlyName() { result = this.getQualifiedName() }
|
||||
|
||||
final override string toString() { result = getFriendlyName() }
|
||||
|
||||
/** Gets a declaration of (part of) this namespace. */
|
||||
NamespaceDeclarationEntry getADeclarationEntry() { result.getNamespace() = this }
|
||||
@@ -104,7 +107,7 @@ class NamespaceDeclarationEntry extends Locatable, @namespace_decl {
|
||||
namespace_decls(underlyingElement(this), unresolveElement(result), _, _)
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNamespace().toString() }
|
||||
override string toString() { result = this.getNamespace().getFriendlyName() }
|
||||
|
||||
/**
|
||||
* Gets the location of the token preceding the namespace declaration
|
||||
@@ -130,7 +133,7 @@ class NamespaceDeclarationEntry extends Locatable, @namespace_decl {
|
||||
/**
|
||||
* A C++ `using` directive or `using` declaration.
|
||||
*/
|
||||
abstract class UsingEntry extends Locatable, @using {
|
||||
class UsingEntry extends Locatable, @using {
|
||||
override Location getLocation() { usings(underlyingElement(this), _, result) }
|
||||
}
|
||||
|
||||
@@ -150,7 +153,7 @@ class UsingDeclarationEntry extends UsingEntry {
|
||||
*/
|
||||
Declaration getDeclaration() { usings(underlyingElement(this), unresolveElement(result), _) }
|
||||
|
||||
override string toString() { result = "using " + this.getDeclaration().toString() }
|
||||
override string toString() { result = "using " + this.getDeclaration().getDescription() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,7 +172,7 @@ class UsingDirectiveEntry extends UsingEntry {
|
||||
*/
|
||||
Namespace getNamespace() { usings(underlyingElement(this), unresolveElement(result), _) }
|
||||
|
||||
override string toString() { result = "using namespace " + this.getNamespace().toString() }
|
||||
override string toString() { result = "using namespace " + this.getNamespace().getFriendlyName() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,7 +207,7 @@ class GlobalNamespace extends Namespace {
|
||||
*/
|
||||
deprecated string getFullName() { result = this.getName() }
|
||||
|
||||
override string toString() { result = "(global namespace)" }
|
||||
override string getFriendlyName() { result = "(global namespace)" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -165,6 +165,7 @@ class Parameter extends LocalScopeVariable, @parameter {
|
||||
class ParameterIndex extends int {
|
||||
ParameterIndex() {
|
||||
exists(Parameter p | this = p.getIndex()) or
|
||||
exists(Call c | exists(c.getArgument(this))) // permit indexing varargs
|
||||
exists(Call c | exists(c.getArgument(this))) or // permit indexing varargs
|
||||
this = -1 // used for `this`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,6 +376,8 @@ private predicate isIntegralType(@builtintype type, int kind) {
|
||||
kind = 43
|
||||
or
|
||||
kind = 44
|
||||
or
|
||||
kind = 51
|
||||
)
|
||||
}
|
||||
|
||||
@@ -463,6 +465,8 @@ private predicate integralTypeMapping(int original, int canonical, int unsigned,
|
||||
original = 43 and canonical = 43 and unsigned = -1 and signed = -1 // char16_t
|
||||
or
|
||||
original = 44 and canonical = 44 and unsigned = -1 and signed = -1 // char32_t
|
||||
or
|
||||
original = 51 and canonical = 51 and unsigned = -1 and signed = -1 // char8_t
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -697,28 +701,188 @@ class Int128Type extends IntegralType {
|
||||
override string getCanonicalQLClass() { result = "Int128Type" }
|
||||
}
|
||||
|
||||
private newtype TTypeDomain =
|
||||
TRealDomain() or
|
||||
TComplexDomain() or
|
||||
TImaginaryDomain()
|
||||
|
||||
/**
|
||||
* The C/C++ floating point types. See 4.5. This includes `float`,
|
||||
* `double` and `long double` types.
|
||||
* ```
|
||||
* float f;
|
||||
* double d;
|
||||
* long double ld;
|
||||
* ```
|
||||
* The type domain of a floating-point type. One of `RealDomain`, `ComplexDomain`, or
|
||||
* `ImaginaryDomain`.
|
||||
*/
|
||||
class TypeDomain extends TTypeDomain {
|
||||
/** Gets a textual representation of this type domain. */
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The type domain of a floating-point type that represents a real number.
|
||||
*/
|
||||
class RealDomain extends TypeDomain, TRealDomain {
|
||||
final override string toString() { result = "real" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The type domain of a floating-point type that represents a complex number.
|
||||
*/
|
||||
class ComplexDomain extends TypeDomain, TComplexDomain {
|
||||
final override string toString() { result = "complex" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The type domain of a floating-point type that represents an imaginary number.
|
||||
*/
|
||||
class ImaginaryDomain extends TypeDomain, TImaginaryDomain {
|
||||
final override string toString() { result = "imaginary" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Data for floating-point types.
|
||||
*
|
||||
* kind: The original type kind. Can be any floating-point type kind.
|
||||
* base: The numeric base of the number's representation. Can be 2 (binary) or 10 (decimal).
|
||||
* domain: The type domain of the type. Can be `RealDomain`, `ComplexDomain`, or `ImaginaryDomain`.
|
||||
* realKind: The type kind of the corresponding real type. For example, the corresponding real type
|
||||
* of `_Complex double` is `double`.
|
||||
* extended: `true` if the number is an extended-precision floating-point number, such as
|
||||
* `_Float32x`.
|
||||
*/
|
||||
private predicate floatingPointTypeMapping(
|
||||
int kind, int base, TTypeDomain domain, int realKind, boolean extended
|
||||
) {
|
||||
// float
|
||||
kind = 24 and base = 2 and domain = TRealDomain() and realKind = 24 and extended = false
|
||||
or
|
||||
// double
|
||||
kind = 25 and base = 2 and domain = TRealDomain() and realKind = 25 and extended = false
|
||||
or
|
||||
// long double
|
||||
kind = 26 and base = 2 and domain = TRealDomain() and realKind = 26 and extended = false
|
||||
or
|
||||
// _Complex float
|
||||
kind = 27 and base = 2 and domain = TComplexDomain() and realKind = 24 and extended = false
|
||||
or
|
||||
// _Complex double
|
||||
kind = 28 and base = 2 and domain = TComplexDomain() and realKind = 25 and extended = false
|
||||
or
|
||||
// _Complex long double
|
||||
kind = 29 and base = 2 and domain = TComplexDomain() and realKind = 26 and extended = false
|
||||
or
|
||||
// _Imaginary float
|
||||
kind = 30 and base = 2 and domain = TImaginaryDomain() and realKind = 24 and extended = false
|
||||
or
|
||||
// _Imaginary double
|
||||
kind = 31 and base = 2 and domain = TImaginaryDomain() and realKind = 25 and extended = false
|
||||
or
|
||||
// _Imaginary long double
|
||||
kind = 32 and base = 2 and domain = TImaginaryDomain() and realKind = 26 and extended = false
|
||||
or
|
||||
// __float128
|
||||
kind = 38 and base = 2 and domain = TRealDomain() and realKind = 38 and extended = false
|
||||
or
|
||||
// _Complex __float128
|
||||
kind = 39 and base = 2 and domain = TComplexDomain() and realKind = 38 and extended = false
|
||||
or
|
||||
// _Decimal32
|
||||
kind = 40 and base = 10 and domain = TRealDomain() and realKind = 40 and extended = false
|
||||
or
|
||||
// _Decimal64
|
||||
kind = 41 and base = 10 and domain = TRealDomain() and realKind = 41 and extended = false
|
||||
or
|
||||
// _Decimal128
|
||||
kind = 42 and base = 10 and domain = TRealDomain() and realKind = 42 and extended = false
|
||||
or
|
||||
// _Float32
|
||||
kind = 45 and base = 2 and domain = TRealDomain() and realKind = 45 and extended = false
|
||||
or
|
||||
// _Float32x
|
||||
kind = 46 and base = 2 and domain = TRealDomain() and realKind = 46 and extended = true
|
||||
or
|
||||
// _Float64
|
||||
kind = 47 and base = 2 and domain = TRealDomain() and realKind = 47 and extended = false
|
||||
or
|
||||
// _Float64x
|
||||
kind = 48 and base = 2 and domain = TRealDomain() and realKind = 48 and extended = true
|
||||
or
|
||||
// _Float128
|
||||
kind = 49 and base = 2 and domain = TRealDomain() and realKind = 49 and extended = false
|
||||
or
|
||||
// _Float128x
|
||||
kind = 50 and base = 2 and domain = TRealDomain() and realKind = 50 and extended = true
|
||||
}
|
||||
|
||||
/**
|
||||
* The C/C++ floating point types. See 4.5. This includes `float`, `double` and `long double`, the
|
||||
* fixed-size floating-point types like `_Float32`, the extended-precision floating-point types like
|
||||
* `_Float64x`, and the decimal floating-point types like `_Decimal32`. It also includes the complex
|
||||
* and imaginary versions of all of these types.
|
||||
*/
|
||||
class FloatingPointType extends ArithmeticType {
|
||||
final int base;
|
||||
final TypeDomain domain;
|
||||
final int realKind;
|
||||
final boolean extended;
|
||||
|
||||
FloatingPointType() {
|
||||
exists(int kind |
|
||||
builtintypes(underlyingElement(this), _, kind, _, _, _) and
|
||||
(
|
||||
kind >= 24 and kind <= 32
|
||||
or
|
||||
kind >= 38 and kind <= 42
|
||||
or
|
||||
kind >= 45 and kind <= 50
|
||||
)
|
||||
floatingPointTypeMapping(kind, base, domain, realKind, extended)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the numeric base of this type's representation: 2 (binary) or 10 (decimal). */
|
||||
final int getBase() { result = base }
|
||||
|
||||
/**
|
||||
* Gets the type domain of this type. Can be `RealDomain`, `ComplexDomain`, or `ImaginaryDomain`.
|
||||
*/
|
||||
final TypeDomain getDomain() { result = domain }
|
||||
|
||||
/**
|
||||
* Gets the corresponding real type of this type. For example, the corresponding real type of
|
||||
* `_Complex double` is `double`.
|
||||
*/
|
||||
final RealNumberType getRealType() {
|
||||
builtintypes(unresolveElement(result), _, realKind, _, _, _)
|
||||
}
|
||||
|
||||
/** Holds if this type is an extended precision floating-point type, such as `_Float32x`. */
|
||||
final predicate isExtendedPrecision() { extended = true }
|
||||
}
|
||||
|
||||
/**
|
||||
* A floating-point type representing a real number.
|
||||
*/
|
||||
class RealNumberType extends FloatingPointType {
|
||||
RealNumberType() { domain instanceof RealDomain }
|
||||
}
|
||||
|
||||
/**
|
||||
* A floating-point type representing a complex number.
|
||||
*/
|
||||
class ComplexNumberType extends FloatingPointType {
|
||||
ComplexNumberType() { domain instanceof ComplexDomain }
|
||||
}
|
||||
|
||||
/**
|
||||
* A floating-point type representing an imaginary number.
|
||||
*/
|
||||
class ImaginaryNumberType extends FloatingPointType {
|
||||
ImaginaryNumberType() { domain instanceof ImaginaryDomain }
|
||||
}
|
||||
|
||||
/**
|
||||
* A floating-point type whose representation is base 2.
|
||||
*/
|
||||
class BinaryFloatingPointType extends FloatingPointType {
|
||||
BinaryFloatingPointType() { base = 2 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A floating-point type whose representation is base 10.
|
||||
*/
|
||||
class DecimalFloatingPointType extends FloatingPointType {
|
||||
DecimalFloatingPointType() { base = 10 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -727,7 +891,7 @@ class FloatingPointType extends ArithmeticType {
|
||||
* float f;
|
||||
* ```
|
||||
*/
|
||||
class FloatType extends FloatingPointType {
|
||||
class FloatType extends RealNumberType, BinaryFloatingPointType {
|
||||
FloatType() { builtintypes(underlyingElement(this), _, 24, _, _, _) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "FloatType" }
|
||||
@@ -739,7 +903,7 @@ class FloatType extends FloatingPointType {
|
||||
* double d;
|
||||
* ```
|
||||
*/
|
||||
class DoubleType extends FloatingPointType {
|
||||
class DoubleType extends RealNumberType, BinaryFloatingPointType {
|
||||
DoubleType() { builtintypes(underlyingElement(this), _, 25, _, _, _) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "DoubleType" }
|
||||
@@ -751,7 +915,7 @@ class DoubleType extends FloatingPointType {
|
||||
* long double ld;
|
||||
* ```
|
||||
*/
|
||||
class LongDoubleType extends FloatingPointType {
|
||||
class LongDoubleType extends RealNumberType, BinaryFloatingPointType {
|
||||
LongDoubleType() { builtintypes(underlyingElement(this), _, 26, _, _, _) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "LongDoubleType" }
|
||||
@@ -763,7 +927,7 @@ class LongDoubleType extends FloatingPointType {
|
||||
* __float128 f128;
|
||||
* ```
|
||||
*/
|
||||
class Float128Type extends FloatingPointType {
|
||||
class Float128Type extends RealNumberType, BinaryFloatingPointType {
|
||||
Float128Type() { builtintypes(underlyingElement(this), _, 38, _, _, _) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "Float128Type" }
|
||||
@@ -775,7 +939,7 @@ class Float128Type extends FloatingPointType {
|
||||
* _Decimal32 d32;
|
||||
* ```
|
||||
*/
|
||||
class Decimal32Type extends FloatingPointType {
|
||||
class Decimal32Type extends RealNumberType, DecimalFloatingPointType {
|
||||
Decimal32Type() { builtintypes(underlyingElement(this), _, 40, _, _, _) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "Decimal32Type" }
|
||||
@@ -787,7 +951,7 @@ class Decimal32Type extends FloatingPointType {
|
||||
* _Decimal64 d64;
|
||||
* ```
|
||||
*/
|
||||
class Decimal64Type extends FloatingPointType {
|
||||
class Decimal64Type extends RealNumberType, DecimalFloatingPointType {
|
||||
Decimal64Type() { builtintypes(underlyingElement(this), _, 41, _, _, _) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "Decimal64Type" }
|
||||
@@ -799,7 +963,7 @@ class Decimal64Type extends FloatingPointType {
|
||||
* _Decimal128 d128;
|
||||
* ```
|
||||
*/
|
||||
class Decimal128Type extends FloatingPointType {
|
||||
class Decimal128Type extends RealNumberType, DecimalFloatingPointType {
|
||||
Decimal128Type() { builtintypes(underlyingElement(this), _, 42, _, _, _) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "Decimal128Type" }
|
||||
@@ -833,6 +997,18 @@ class WideCharType extends IntegralType {
|
||||
override string getCanonicalQLClass() { result = "WideCharType" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The C/C++ `char8_t` type. This is available starting with C++20.
|
||||
* ```
|
||||
* char8_t c8;
|
||||
* ```
|
||||
*/
|
||||
class Char8Type extends IntegralType {
|
||||
Char8Type() { builtintypes(underlyingElement(this), _, 51, _, _, _) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "Char8Type" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The C/C++ `char16_t` type. This is available starting with C11 and C++11.
|
||||
* ```
|
||||
|
||||
@@ -38,7 +38,7 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @
|
||||
override Specifier getASpecifier() { result = Type.super.getASpecifier() }
|
||||
|
||||
override Location getLocation() {
|
||||
if isDefined()
|
||||
if hasDefinition()
|
||||
then result = this.getDefinitionLocation()
|
||||
else result = this.getADeclarationLocation()
|
||||
}
|
||||
|
||||
@@ -126,10 +126,7 @@ class Variable extends Declaration, @variable {
|
||||
or
|
||||
exists(AssignExpr ae | ae.getLValue().(Access).getTarget() = this and result = ae.getRValue())
|
||||
or
|
||||
exists(AggregateLiteral l |
|
||||
this.getDeclaringType() = l.getType() and
|
||||
result = l.getChild(this.(Field).getInitializationOrder())
|
||||
)
|
||||
exists(ClassAggregateLiteral l | result = l.getFieldExpr(this))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -263,24 +260,33 @@ class ParameterDeclarationEntry extends VariableDeclarationEntry {
|
||||
*/
|
||||
int getIndex() { param_decl_bind(underlyingElement(this), result, _) }
|
||||
|
||||
private string getAnonymousParameterDescription() {
|
||||
not exists(getName()) and
|
||||
exists(string idx |
|
||||
idx =
|
||||
((getIndex() + 1).toString() + "th")
|
||||
.replaceAll("1th", "1st")
|
||||
.replaceAll("2th", "2nd")
|
||||
.replaceAll("3th", "3rd")
|
||||
.replaceAll("11st", "11th")
|
||||
.replaceAll("12nd", "12th")
|
||||
.replaceAll("13rd", "13th") and
|
||||
if exists(getCanonicalName())
|
||||
then result = "declaration of " + getCanonicalName() + " as anonymous " + idx + " parameter"
|
||||
else result = "declaration of " + idx + " parameter"
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
if exists(getName())
|
||||
then result = super.toString()
|
||||
else
|
||||
exists(string idx |
|
||||
idx =
|
||||
((getIndex() + 1).toString() + "th")
|
||||
.replaceAll("1th", "1st")
|
||||
.replaceAll("2th", "2nd")
|
||||
.replaceAll("3th", "3rd")
|
||||
.replaceAll("11st", "11th")
|
||||
.replaceAll("12nd", "12th")
|
||||
.replaceAll("13rd", "13th")
|
||||
|
|
||||
if exists(getCanonicalName())
|
||||
then result = "declaration of " + getCanonicalName() + " as anonymous " + idx + " parameter"
|
||||
else result = "declaration of " + idx + " parameter"
|
||||
)
|
||||
isDefinition() and
|
||||
result = "definition of " + getName()
|
||||
or
|
||||
not isDefinition() and
|
||||
if getName() = getCanonicalName()
|
||||
then result = "declaration of " + getName()
|
||||
else result = "declaration of " + getCanonicalName() + " as " + getName()
|
||||
or
|
||||
result = getAnonymousParameterDescription()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -116,7 +116,7 @@ class XMLFile extends XMLParent, File {
|
||||
XMLFile() { xmlEncoding(this, _) }
|
||||
|
||||
/** Gets a printable representation of this XML file. */
|
||||
override string toString() { result = XMLParent.super.toString() }
|
||||
override string toString() { result = getName() }
|
||||
|
||||
/** Gets the name of this XML file. */
|
||||
override string getName() { result = File.super.getAbsolutePath() }
|
||||
@@ -236,7 +236,7 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
|
||||
string getAttributeValue(string name) { result = this.getAttribute(name).getValue() }
|
||||
|
||||
/** Gets a printable representation of this XML element. */
|
||||
override string toString() { result = XMLParent.super.toString() }
|
||||
override string toString() { result = getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,12 +12,12 @@ abstract class Assertion extends Locatable {
|
||||
}
|
||||
|
||||
/**
|
||||
* A libc assert, as defined in assert.h. A macro with the head
|
||||
* "assert(expr)" that expands to a conditional expression which
|
||||
* may terminate the program.
|
||||
* A libc assert, as defined in assert.h. A macro with a head
|
||||
* that matches the prefix "assert(", and expands to a conditional
|
||||
* expression which may terminate the program.
|
||||
*/
|
||||
class LibcAssert extends MacroInvocation, Assertion {
|
||||
LibcAssert() { this.getMacro().getHead() = "assert(expr)" }
|
||||
LibcAssert() { this.getMacro().getHead().matches("assert(%") }
|
||||
|
||||
override Expr getAsserted() {
|
||||
exists(ConditionalExpr ce | this.getAGeneratedElement() = ce | result = ce.getCondition())
|
||||
|
||||
@@ -92,13 +92,7 @@ int getBufferSize(Expr bufferExpr, Element why) {
|
||||
// dataflow (all sources must be the same size)
|
||||
bufferExprNode = DataFlow::exprNode(bufferExpr) and
|
||||
result =
|
||||
min(Expr def |
|
||||
DataFlow::localFlowStep(DataFlow::exprNode(def), bufferExprNode)
|
||||
|
|
||||
getBufferSize(def, _)
|
||||
) and
|
||||
result =
|
||||
max(Expr def |
|
||||
unique(Expr def |
|
||||
DataFlow::localFlowStep(DataFlow::exprNode(def), bufferExprNode)
|
||||
|
|
||||
getBufferSize(def, _)
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Gets the number corresponding to the contents of `input` in base-8.
|
||||
* Note: the first character of `input` must be `0`. For example:
|
||||
* `parseOctal("012345") = 5349`.
|
||||
*/
|
||||
bindingset[input]
|
||||
int parseOctal(string input) {
|
||||
input.charAt(0) = "0" and
|
||||
@@ -15,44 +20,77 @@ int parseOctal(string input) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the number corresponding to the "set-user-ID on execute bit" in Unix. */
|
||||
int s_isuid() { result = parseOctal("04000") }
|
||||
|
||||
/** Gets the number corresponding to the "set-group-ID on execute bit" in Unix. */
|
||||
int s_isgid() { result = parseOctal("02000") }
|
||||
|
||||
/** Gets the number corresponding to the sticky bit in Unix. */
|
||||
int s_isvtx() { result = parseOctal("01000") }
|
||||
|
||||
/** Gets the number corresponding to the read permission bit for owner of the file in Unix. */
|
||||
int s_irusr() { result = parseOctal("0400") }
|
||||
|
||||
/** Gets the number corresponding to the write permission bit for owner of the file in Unix. */
|
||||
int s_iwusr() { result = parseOctal("0200") }
|
||||
|
||||
/** Gets the number corresponding to the execute permission bit for owner of the file in Unix. */
|
||||
int s_ixusr() { result = parseOctal("0100") }
|
||||
|
||||
/** Gets the number corresponding to the permissions `S_IRUSR | S_IWUSR | S_IXUSR` in Unix. */
|
||||
int s_irwxu() { result = s_irusr().bitOr(s_iwusr()).bitOr(s_ixusr()) }
|
||||
|
||||
/**
|
||||
* Gets the number corresponding to the read permission bit for the group
|
||||
* owner of the file in Unix.
|
||||
*/
|
||||
int s_irgrp() { result = s_irusr().bitShiftRight(3) }
|
||||
|
||||
/**
|
||||
* Gets the number corresponding to the write permission bit for the group
|
||||
* owner of the file in Unix.
|
||||
*/
|
||||
int s_iwgrp() { result = s_iwusr().bitShiftRight(3) }
|
||||
|
||||
/**
|
||||
* Gets the number corresponding to the execute permission bit for the group
|
||||
* owner of the file in Unix.
|
||||
*/
|
||||
int s_ixgrp() { result = s_ixusr().bitShiftRight(3) }
|
||||
|
||||
/** Gets the number corresponding to the permissions `S_IRGRP | S_IWGRP | S_IXGRP` in Unix. */
|
||||
int s_irwxg() { result = s_irwxu().bitShiftRight(3) }
|
||||
|
||||
/** Gets the number corresponding to the read permission bit for other users in Unix. */
|
||||
int s_iroth() { result = s_irgrp().bitShiftRight(3) }
|
||||
|
||||
/** Gets the number corresponding to the write permission bit for other users in Unix. */
|
||||
int s_iwoth() { result = s_iwgrp().bitShiftRight(3) }
|
||||
|
||||
/** Gets the number corresponding to the execute-or-search permission bit for other users in Unix. */
|
||||
int s_ixoth() { result = s_ixgrp().bitShiftRight(3) }
|
||||
|
||||
/** Gets the number corresponding to the permissions `S_IROTH | S_IWOTH | S_IXOTH` in Unix. */
|
||||
int s_irwxo() { result = s_irwxg().bitShiftRight(3) }
|
||||
|
||||
/**
|
||||
* Gets the number that can be used in a bitwise and with the file status flag
|
||||
* to produce a number representing the file access mode.
|
||||
*/
|
||||
int o_accmode() { result = parseOctal("0003") }
|
||||
|
||||
/** Gets the number corresponding to the read-only file access mode. */
|
||||
int o_rdonly() { result = parseOctal("00") }
|
||||
|
||||
/** Gets the number corresponding to the write-only file access mode. */
|
||||
int o_wronly() { result = parseOctal("01") }
|
||||
|
||||
/** Gets the number corresponding to the read-and-write file access mode. */
|
||||
int o_rdwr() { result = parseOctal("02") }
|
||||
|
||||
/** Gets the number corresponding to the file creation flag O_CREAT on Linux. */
|
||||
int o_creat() { result = parseOctal("0100") }
|
||||
|
||||
/** Gets the number corresponding to the file creation flag O_EXCL on Linux. */
|
||||
int o_excl() { result = parseOctal("0200") }
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* Provides a library for reasoning about control flow at the granularity of basic blocks.
|
||||
* This is usually much more efficient than reasoning directly at the level of `ControlFlowNode`s.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
private import internal.PrimitiveBasicBlocks
|
||||
private import internal.ConstantExprs
|
||||
@@ -148,22 +153,37 @@ predicate bb_successor = bb_successor_cached/2;
|
||||
class BasicBlock extends ControlFlowNodeBase {
|
||||
BasicBlock() { basic_block_entry_node(this) }
|
||||
|
||||
/** Holds if this basic block contains `node`. */
|
||||
predicate contains(ControlFlowNode node) { basic_block_member(node, this, _) }
|
||||
|
||||
/** Gets the `ControlFlowNode` at position `pos` in this basic block. */
|
||||
ControlFlowNode getNode(int pos) { basic_block_member(result, this, pos) }
|
||||
|
||||
/** Gets a `ControlFlowNode` in this basic block. */
|
||||
ControlFlowNode getANode() { basic_block_member(result, this, _) }
|
||||
|
||||
/** Gets a `BasicBlock` that is a direct successor of this basic block. */
|
||||
BasicBlock getASuccessor() { bb_successor(this, result) }
|
||||
|
||||
/** Gets a `BasicBlock` that is a direct predecessor of this basic block. */
|
||||
BasicBlock getAPredecessor() { bb_successor(result, this) }
|
||||
|
||||
/**
|
||||
* Gets a `BasicBlock` such that the control-flow edge `(this, result)` may be taken
|
||||
* when the outgoing edge of this basic block is an expression that is true.
|
||||
*/
|
||||
BasicBlock getATrueSuccessor() { result.getStart() = this.getEnd().getATrueSuccessor() }
|
||||
|
||||
/**
|
||||
* Gets a `BasicBlock` such that the control-flow edge `(this, result)` may be taken
|
||||
* when the outgoing edge of this basic block is an expression that is false.
|
||||
*/
|
||||
BasicBlock getAFalseSuccessor() { result.getStart() = this.getEnd().getAFalseSuccessor() }
|
||||
|
||||
/** Gets the final `ControlFlowNode` of this basic block. */
|
||||
ControlFlowNode getEnd() { basic_block_member(result, this, bb_length(this) - 1) }
|
||||
|
||||
/** Gets the first `ControlFlowNode` of this basic block. */
|
||||
ControlFlowNode getStart() { result = this }
|
||||
|
||||
/** Gets the number of `ControlFlowNode`s in this basic block. */
|
||||
@@ -192,6 +212,7 @@ class BasicBlock extends ControlFlowNodeBase {
|
||||
this.getEnd().getLocation().hasLocationInfo(endf, _, _, endl, endc)
|
||||
}
|
||||
|
||||
/** Gets the function containing this basic block. */
|
||||
Function getEnclosingFunction() { result = this.getStart().getControlFlowScope() }
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* Provides a library for reasoning about control flow at the granularity of
|
||||
* individual nodes in the control-flow graph.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import BasicBlocks
|
||||
private import semmle.code.cpp.controlflow.internal.ConstantExprs
|
||||
@@ -29,8 +34,10 @@ private import semmle.code.cpp.controlflow.internal.CFG
|
||||
* `Handler`. There are no edges from function calls to `Handler`s.
|
||||
*/
|
||||
class ControlFlowNode extends Locatable, ControlFlowNodeBase {
|
||||
/** Gets a direct successor of this control-flow node, if any. */
|
||||
ControlFlowNode getASuccessor() { successors_adapted(this, result) }
|
||||
|
||||
/** Gets a direct predecessor of this control-flow node, if any. */
|
||||
ControlFlowNode getAPredecessor() { this = result.getASuccessor() }
|
||||
|
||||
/** Gets the function containing this control-flow node. */
|
||||
@@ -71,6 +78,7 @@ class ControlFlowNode extends Locatable, ControlFlowNodeBase {
|
||||
result = getASuccessor()
|
||||
}
|
||||
|
||||
/** Gets the `BasicBlock` containing this control-flow node. */
|
||||
BasicBlock getBasicBlock() { result.getANode() = this }
|
||||
}
|
||||
|
||||
@@ -86,10 +94,18 @@ import ControlFlowGraphPublic
|
||||
*/
|
||||
class ControlFlowNodeBase extends ElementBase, @cfgnode { }
|
||||
|
||||
/**
|
||||
* Holds when `n2` is a control-flow node such that the control-flow
|
||||
* edge `(n1, n2)` may be taken when `n1` is an expression that is true.
|
||||
*/
|
||||
predicate truecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
|
||||
qlCFGTrueSuccessor(n1, n2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds when `n2` is a control-flow node such that the control-flow
|
||||
* edge `(n1, n2)` may be taken when `n1` is an expression that is false.
|
||||
*/
|
||||
predicate falsecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
|
||||
qlCFGFalseSuccessor(n1, n2)
|
||||
}
|
||||
|
||||
@@ -15,14 +15,25 @@ import Dereferenced
|
||||
abstract class DataflowAnnotation extends string {
|
||||
DataflowAnnotation() { this = "pointer-null" or this = "pointer-valid" }
|
||||
|
||||
/** Holds if this annotation is the default annotation. */
|
||||
abstract predicate isDefault();
|
||||
|
||||
/** Holds if this annotation is generated when analyzing expression `e`. */
|
||||
abstract predicate generatedOn(Expr e);
|
||||
|
||||
/**
|
||||
* Holds if this annotation is generated for the variable `v` when
|
||||
* the control-flow edge `(src, dest)` is taken.
|
||||
*/
|
||||
abstract predicate generatedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest);
|
||||
|
||||
/**
|
||||
* Holds if this annotation is removed for the variable `v` when
|
||||
* the control-flow edge `(src, dest)` is taken.
|
||||
*/
|
||||
abstract predicate killedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest);
|
||||
|
||||
/** Holds if expression `e` is given this annotation. */
|
||||
predicate marks(Expr e) {
|
||||
this.generatedOn(e) and reachable(e)
|
||||
or
|
||||
@@ -31,6 +42,7 @@ abstract class DataflowAnnotation extends string {
|
||||
exists(LocalScopeVariable v | this.marks(v, e) and e = v.getAnAccess())
|
||||
}
|
||||
|
||||
/** Holds if the variable `v` accessed in control-flow node `n` is given this annotation. */
|
||||
predicate marks(LocalScopeVariable v, ControlFlowNode n) {
|
||||
v.getAnAccess().getEnclosingFunction().getBlock() = n and
|
||||
this.isDefault()
|
||||
@@ -57,6 +69,10 @@ abstract class DataflowAnnotation extends string {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the variable `v` preserves this annotation when the control-flow
|
||||
* edge `(src, dest)` is taken.
|
||||
*/
|
||||
predicate preservedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest) {
|
||||
this.marks(v, src) and
|
||||
src.getASuccessor() = dest and
|
||||
@@ -64,6 +80,10 @@ abstract class DataflowAnnotation extends string {
|
||||
not v.getAnAssignment() = src
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the variable `v` is assigned this annotation when `src` is an assignment
|
||||
* expression that assigns to `v` and the control-flow edge `(src, dest)` is taken.
|
||||
*/
|
||||
predicate assignedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest) {
|
||||
this.marks(src.(AssignExpr).getRValue()) and
|
||||
src = v.getAnAssignment() and
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user