Merge branch 'master' into alloc-size

This commit is contained in:
Geoffrey White
2020-04-07 17:12:35 +01:00
1279 changed files with 50678 additions and 38960 deletions

6
.gitignore vendored
View File

@@ -14,7 +14,11 @@
.vs/*
!.vs/VSWorkspaceSettings.json
# Byte-compiled python files
*.pyc
# It's useful (though not required) to be able to unpack codeql in the ql checkout itself
/codeql/
.vscode/settings.json
csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json
.vscode

View File

@@ -1,94 +1,66 @@
# Contributing to CodeQL
We welcome contributions to our standard library and standard checks. Got an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request!
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).
Before we accept your pull request, we require that you have agreed to our Contributor License Agreement, this is not something that you need to do before you submit your pull request, but until you've done so, we will be unable to accept your contribution.
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).
## Adding a new query
If you have an idea for a query that you would like to share with other CodeQL users, please open a pull request to add it to this repository.
Follow the steps below to help other users understand what your query does, and to ensure that your query is consistent with the other CodeQL queries.
## Submitting a new experimental query
1. **Consult the documentation for query writers**
If you have an idea for a query that you would like to share with other CodeQL users, please open a pull request to add it to this repository. New queries start out in a `<language>/ql/src/experimental` directory, to which they can be merged when they meet the following requirements.
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).
1. **Directory structure**
2. **Format your code correctly**
There are five language-specific query directories in this repository:
All CodeQL standard queries and libraries are uniformly formatted for clarity and consistency, so we strongly recommend that all contributions follow the same formatting guidelines. If you use CodeQL for VS Code, you can autoformat your query in the [Editor](https://help.semmle.com/codeql/codeql-for-vscode/reference/editor.html#autoformatting). For more information, see the [CodeQL style guide](https://github.com/Semmle/ql/blob/master/docs/ql-style-guide.md).
* C/C++: `cpp/ql/src`
* C#: `csharp/ql/src`
* Java: `java/ql/src`
* JavaScript: `javascript/ql/src`
* Python: `python/ql/src`
3. **Make sure your query has the correct metadata**
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`.
- 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.
Query metadata is used to identify your query and make sure the query results are displayed properly.
The most important metadata to include are the `@name`, `@description`, and the `@kind`.
Other metadata properties (`@precision`, `@severity`, and `@tags`) are usually added after the query has been reviewed by GitHub staff.
For more information on writing query metadata, see the [Query metadata style guide](https://github.com/Semmle/ql/blob/master/docs/query-metadata-style-guide.md).
2. **Query metadata**
4. **Make sure the `select` statement is compatible with the query type**
- The query `@id` must conform to all the requirements in the [guide on query metadata](docs/query-metadata-style-guide.md#query-id-id). In particular, it must not clash with any other queries in the repository, and it must start with the appropriate language-specific prefix.
- The query must have a `@name` and `@description` to explain its purpose.
- The query must have a `@kind` and `@problem.severity` as required by CodeQL tools.
The `select` statement of your query must be compatible with the query type (determined by the `@kind` metadata property) for alert or path results to be displayed correctly in LGTM and CodeQL for VS Code.
For more information on `select` statement format, see [Introduction to query files](https://help.semmle.com/QL/learn-ql/writing-queries/introduction-to-queries.html#select-clause) on help.semmle.com.
For details, see the [guide on query metadata](docs/query-metadata-style-guide.md).
5. **Save your query in a `.ql` file in the correct language directory in this repository**
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.
There are five language-specific directories in this repository:
* C/C++: `ql/cpp/ql/src`
* C#: `ql/csharp/ql/src`
* Java: `ql/java/ql/src`
* JavaScript: `ql/javascript/ql/src`
* Python: `ql/python/ql/src`
3. **Formatting**
Each language-specific directory contains further subdirectories that group queries based on their `@tags` properties or purpose. Select the appropriate subdirectory for your new query, or create a new one if necessary.
- The queries and libraries must be [autoformatted](https://help.semmle.com/codeql/codeql-for-vscode/reference/editor.html#autoformatting).
6. **Write a query help file**
4. **Compilation**
Query help files explain the purpose of your query to other users. Write your query help in a `.qhelp` file and save it in the same directory as your new query.
For more information on writing query help, see the [Query help style guide](https://github.com/Semmle/ql/blob/master/docs/query-help-style-guide.md).
- Compilation of the query and any associated libraries and tests must be resilient to future development of the [supported](docs/supported-queries.md) libraries. This means that the functionality cannot use internal libraries, cannot depend on the output of `getAQlClass`, and cannot make use of regexp matching on `toString`.
- The query and any associated libraries and tests must not cause any compiler warnings to be emitted (such as use of deprecated functionality or missing `override` annotations).
7. **Maintain backwards compatibility**
5. **Results**
The standard CodeQL libraries must evolve in a backwards compatible manner. If any backwards incompatible changes need to be made, the existing API must first be marked as deprecated. This is done by adding a `deprecated` annotation along with a QLDoc reference to the replacement API. Only after at least one full release cycle has elapsed may the old API be removed.
- The query must have at least one true positive result on some revision of a real project.
In addition to contributions to our standard queries and libraries, we also welcome contributions of a more experimental nature, which do not need to fulfill all the requirements listed above. See the guidelines for [experimental queries and libraries](docs/experimental.md) for details.
Experimental queries and libraries may not be actively maintained as the [supported](docs/supported-queries.md) libraries evolve. They may also be changed in backwards-incompatible ways or may be removed entirely in the future without deprecation warnings.
After the experimental query is merged, we welcome pull requests to improve it. Before a query can be moved out of the `experimental` subdirectory, it must satisfy [the requirements for being a supported query](docs/supported-queries.md).
## 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 might be made public. We might also use this information
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@semmle.com) if you have any questions about
Please do get in touch (privacy@github.com) if you have any questions about
this or our data protection policies.
## Contributor License Agreement
This Contributor License Agreement (“Agreement”) is entered into between Semmle Limited (“Semmle,” “we” or “us” etc.), and You (as defined and further identified below).
Accordingly, You hereby agree to the following terms for Your present and future Contributions submitted to Semmle:
1. **Definitions**.
* "You" (or "Your") shall mean the Contribution copyright owner (whether an individual or organization) or legal entity authorized by the copyright owner that is making this Agreement with Semmle. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
* "Contribution(s)" shall mean the code, documentation or other original works of authorship, including any modifications or additions to an existing work, submitted by You to Semmle for inclusion in, or documentation of, any of the products or projects owned or managed by Semmle (the "Work(s)"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to Semmle or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Semmle for the purpose of discussing and/or improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
2. **Grant of Copyright License**. You hereby grant to Semmle and to recipients of software distributed by Semmle a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works.
3. **Grant of Patent License**. You hereby grant to Semmle and to recipients of software distributed by Semmle a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that Your Contribution, or the Work to which You have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.
4. **Ownership**. Except as set out above, You keep all right, title, and interest in Your Contribution. The rights that You grant to us under this Agreement are effective on the date You first submitted a Contribution to us, even if Your submission took place before the date You entered this Agreement.
5. **Representations**. You represent and warrant that: (i) the Contributions are an original work and that You can legally grant the rights set out in this Agreement; (ii) the Contributions and Semmles exercise of any license rights granted hereunder, does not and will not, infringe the rights of any third party; (iii) You are not aware of any pending or threatened claims, suits, actions, or charges pertaining to the Contributions, including without limitation any claims or allegations that any or all of the Contributions infringes, violates, or misappropriate the intellectual property rights of any third party (You further agree that You will notify Semmle immediately if You become aware of any such actual or potential claims, suits, actions, allegations or charges).
6. **Employer**. If Your employer(s) has rights to intellectual property that You create that includes Your Contributions, You represent and warrant that Your employer has waived such rights for Your Contributions to Semmle, or that You have received permission to make Contributions on behalf of that employer and that You are authorized to execute this Agreement on behalf of Your employer.
7. **Inclusion of Code**. We determine the code that is in our Works. You understand that the decision to include the Contribution in any project or source repository is entirely that of Semmle, and this agreement does not guarantee that the Contributions will be included in any product.
8. **Disclaimer**. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Except as set forth herein, and unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND.
9. **General**. The failure of either party to enforce its rights under this Agreement for any period shall not be construed as a waiver of such rights. No changes or modifications or waivers to this Agreement will be effective unless in writing and signed by both parties. In the event that any provision of this Agreement shall be determined to be illegal or unenforceable, that provision will be limited or eliminated to the minimum extent necessary so that this Agreement shall otherwise remain in full force and effect and enforceable. This Agreement shall be governed by and construed in accordance with the laws of the State of California in the United States without regard to the conflicts of laws provisions thereof. In any action or proceeding to enforce rights under this Agreement, the prevailing party will be entitled to recover costs and attorneys fees.

View File

@@ -1,13 +0,0 @@
Copyright (c) Semmle Inc and other contributors. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.

189
LICENSE
View File

@@ -1,176 +1,21 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
MIT License
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Copyright (c) 2006-2020 GitHub, Inc.
1. Definitions.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -18,13 +18,16 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
| 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. |
| 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. |
| 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. |
| 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
@@ -43,6 +46,8 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
* 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.

View File

@@ -21,6 +21,9 @@ The following changes in version 1.24 affect C# analysis in all applications.
| 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
@@ -41,5 +44,6 @@ The following changes in version 1.24 affect C# analysis in all applications.
* [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

View File

@@ -6,16 +6,18 @@
* Alert suppression can now be done with single-line block comments (`/* ... */`) as well as line comments (`// ...`).
* Imports with the `.js` extension can now be resolved to a TypeScript file,
* 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 that rely on path-mappings from a `tsconfig.json` file can now be resolved.
* 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.
* Export declarations of the form `export * as ns from "x"` are now analyzed more precisely.
* The analysis of sanitizer guards has improved, leading to fewer false-positive results from the security queries.
* The call graph construction has been improved, leading to more results from the security queries:
* Call graph construction has been improved, leading to more results from the security queries:
- Calls can now be resolved to indirectly-defined class members in more cases.
- Calls through partial invocations such as `.bind` can now be resolved in more cases.
@@ -23,7 +25,9 @@
* 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/)
@@ -32,11 +36,23 @@
- [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)
## New queries
@@ -69,8 +85,15 @@
| 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. |
## Changes to libraries
* The predicates `RegExpTerm.getSuccessor` and `RegExpTerm.getPredecessor` have been changed to reflect textual, not operational, matching order. This only makes a difference in lookbehind assertions, which are operationally matched backwards. Previously, `getSuccessor` would mimick this, so in an assertion `(?<=ab)` the term `b` would be considered the predecessor, not the successor, of `a`. Textually, however, `a` is still matched before `b`, and this is the order we now follow.
* 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.

View File

@@ -4,6 +4,8 @@ The following changes in version 1.24 affect Python analysis in all applications
## General improvements
Support for Django version 2.x and 3.x
## New queries
| **Query** | **Tags** | **Purpose** |
@@ -13,6 +15,7 @@ The following changes in version 1.24 affect Python analysis in all applications
| **Query** | **Expected impact** | **Change** |
|----------------------------|------------------------|------------------------------------------------------------------|
| Uncontrolled command line (`py/command-line-injection`) | More results | We now model the `fabric` and `invoke` pacakges for command execution. |
### Web framework support

View File

@@ -39,6 +39,12 @@
"java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
"java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll"
],
"DataFlow Java/C++/C# Consistency checks": [
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll",
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll",
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll",
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll"
],
"C++ SubBasicBlocks": [
"cpp/ql/src/semmle/code/cpp/controlflow/SubBasicBlocks.qll",
"cpp/ql/src/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll"
@@ -236,6 +242,13 @@
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/ValueNumbering.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll"
],
"C++ IR 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"
],
"C++ IR ConstantAnalysis": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/constant/ConstantAnalysis.qll",

140
config/sync-files.py Normal file
View File

@@ -0,0 +1,140 @@
#!/usr/bin/env python3
# Due to various technical limitations, we sometimes have files that need to be
# kept identical in the repository. This script loads a database of such
# files and can perform two functions: check whether they are still identical,
# and overwrite the others with a master copy if needed.
import hashlib
import shutil
import os
import sys
import json
import re
path = os.path
file_groups = {}
def add_prefix(prefix, relative):
result = path.join(prefix, relative)
if path.commonprefix((path.realpath(result), path.realpath(prefix))) != \
path.realpath(prefix):
raise Exception("Path {} is not below {}".format(
result, prefix))
return result
def load_if_exists(prefix, json_file_relative):
json_file_name = path.join(prefix, json_file_relative)
if path.isfile(json_file_name):
print("Loading file groups from", json_file_name)
with open(json_file_name, 'r', encoding='utf-8') as fp:
raw_groups = json.load(fp)
prefixed_groups = {
name: [
add_prefix(prefix, relative)
for relative in relatives
]
for name, relatives in raw_groups.items()
}
file_groups.update(prefixed_groups)
# Generates a list of C# test files that should be in sync
def csharp_test_files():
test_file_re = re.compile('.*(Bad|Good)[0-9]*\\.cs$')
csharp_doc_files = {
file:os.path.join(root, file)
for root, dirs, files in os.walk("csharp/ql/src")
for file in files
if test_file_re.match(file)
}
return {
"C# test '" + file + "'" : [os.path.join(root, file), csharp_doc_files[file]]
for root, dirs, files in os.walk("csharp/ql/test")
for file in files
if file in csharp_doc_files
}
def file_checksum(filename):
with open(filename, 'rb') as file_handle:
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:
return
master_file = master_file_picker(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 "
"group of files from the most recently modified file in the group.")
for filename in files:
emit_error(__file__, 0, " " + filename)
else:
print(" Syncing others from", master_file)
for filename in files:
if filename == master_file:
continue
print(" " + filename)
os.replace(filename, filename + '~')
shutil.copy(master_file, filename)
print(" Backups written with '~' appended to file names")
def chdir_repo_root():
root_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')
os.chdir(root_path)
def choose_master_file(master_file, files):
if master_file in files:
return master_file
else:
return None
def choose_latest_file(files):
latest_time = None
latest_file = None
for filename in files:
file_time = os.path.getmtime(filename)
if (latest_time is None) or (latest_time < file_time):
latest_time = file_time
latest_file = filename
return latest_file
local_error_count = 0
def emit_local_error(path, line, error):
print('ERROR: ' + path + ':' + line + " - " + error)
global local_error_count
local_error_count += 1
# This function is invoked directly by a CI script, which passes a different error-handling
# callback.
def sync_identical_files(emit_error):
if len(sys.argv) == 1:
master_file_picker = lambda files: None
elif len(sys.argv) == 2:
if sys.argv[1] == "--latest":
master_file_picker = choose_latest_file
elif os.path.isfile(sys.argv[1]):
master_file_picker = lambda files: choose_master_file(sys.argv[1], files)
else:
raise Exception("File not found")
else:
raise Exception("Bad command line or file not found")
chdir_repo_root()
load_if_exists('.', 'config/identical-files.json')
file_groups.update(csharp_test_files())
for group_name, files in file_groups.items():
check_group(group_name, files, master_file_picker, emit_error)
def main():
sync_identical_files(emit_local_error)
if local_error_count > 0:
exit(1)
if __name__ == "__main__":
main()

View File

@@ -12,6 +12,7 @@ import semmle.code.cpp.dataflow.DataFlow
*/
predicate allocExpr(Expr alloc, string kind) {
isAllocationExpr(alloc) and
not alloc.isFromUninstantiatedTemplate(_) and
(
alloc instanceof FunctionCall and
kind = "malloc"

View File

@@ -19,15 +19,19 @@ class ConstantZero extends Expr {
* Holds if `candidate` is an expression such that if it's unsigned then we
* want an alert at `ge`.
*/
private predicate lookForUnsignedAt(GEExpr ge, Expr candidate) {
// Base case: `candidate >= 0`
ge.getRightOperand() instanceof ConstantZero and
candidate = ge.getLeftOperand().getFullyConverted() and
// left operand was a signed or unsigned IntegralType before conversions
private predicate lookForUnsignedAt(RelationalOperation ge, Expr candidate) {
// Base case: `candidate >= 0` (or `0 <= candidate`)
(
ge instanceof GEExpr or
ge instanceof LEExpr
) and
ge.getLesserOperand() instanceof ConstantZero and
candidate = ge.getGreaterOperand().getFullyConverted() and
// left/greater operand was a signed or unsigned IntegralType before conversions
// (not a pointer, checking a pointer >= 0 is an entirely different mistake)
// (not an enum, as the fully converted type of an enum is compiler dependent
// so checking an enum >= 0 is always reasonable)
ge.getLeftOperand().getUnderlyingType() instanceof IntegralType
ge.getGreaterOperand().getUnderlyingType() instanceof IntegralType
or
// Recursive case: `...(largerType)candidate >= 0`
exists(Conversion conversion |
@@ -37,7 +41,7 @@ private predicate lookForUnsignedAt(GEExpr ge, Expr candidate) {
)
}
class UnsignedGEZero extends GEExpr {
class UnsignedGEZero extends ComparisonOperation {
UnsignedGEZero() {
exists(Expr ue |
lookForUnsignedAt(this, ue) and

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,7 @@
* @description Using externally controlled strings in a process
* operation can allow an attacker to execute malicious
* commands.
* @kind problem
* @kind path-problem
* @problem.severity warning
* @precision medium
* @id cpp/uncontrolled-process-operation
@@ -14,13 +14,24 @@
import cpp
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking
import TaintedWithPath
from string processOperation, int processOperationArg, FunctionCall call, Expr arg, Element source
predicate isProcessOperationExplanation(Expr arg, string processOperation) {
exists(int processOperationArg, FunctionCall call |
isProcessOperationArgument(processOperation, processOperationArg) and
call.getTarget().getName() = processOperation and
call.getArgument(processOperationArg) = arg
)
}
class Configuration extends TaintTrackingConfiguration {
override predicate isSink(Element arg) { isProcessOperationExplanation(arg, _) }
}
from string processOperation, Expr arg, Expr source, PathNode sourceNode, PathNode sinkNode
where
isProcessOperationArgument(processOperation, processOperationArg) and
call.getTarget().getName() = processOperation and
call.getArgument(processOperationArg) = arg and
tainted(source, arg)
select arg,
isProcessOperationExplanation(arg, processOperation) and
taintedWithPath(source, arg, sourceNode, sinkNode)
select arg, sourceNode, sinkNode,
"The value of this argument may come from $@ and is being passed to " + processOperation, source,
source.toString()

View File

@@ -2,7 +2,7 @@
* @name Unbounded write
* @description Buffer write operations that do not control the length
* of data written may overflow.
* @kind problem
* @kind path-problem
* @problem.severity error
* @precision medium
* @id cpp/unbounded-write
@@ -16,6 +16,7 @@
import semmle.code.cpp.security.BufferWrite
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking
import TaintedWithPath
/*
* --- Summary of CWE-120 alerts ---
@@ -54,32 +55,48 @@ predicate isUnboundedWrite(BufferWrite bw) {
* }
*/
/**
* Holds if `e` is a source buffer going into an unbounded write `bw` or a
* qualifier of (a qualifier of ...) such a source.
*/
predicate unboundedWriteSource(Expr e, BufferWrite bw) {
isUnboundedWrite(bw) and e = bw.getASource()
or
exists(FieldAccess fa | unboundedWriteSource(fa, bw) and e = fa.getQualifier())
}
/*
* --- user input reach ---
*/
/**
* Identifies expressions that are potentially tainted with user
* input. Most of the work for this is actually done by the
* TaintTracking library.
*/
predicate tainted2(Expr expr, Expr inputSource, string inputCause) {
taintedIncludingGlobalVars(inputSource, expr, _) and
inputCause = inputSource.toString()
or
exists(Expr e | tainted2(e, inputSource, inputCause) |
// field accesses of a tainted struct are tainted
e = expr.(FieldAccess).getQualifier()
)
class Configuration extends TaintTrackingConfiguration {
override predicate isSink(Element tainted) { unboundedWriteSource(tainted, _) }
override predicate taintThroughGlobals() { any() }
}
/*
* --- put it together ---
*/
from BufferWrite bw, Expr inputSource, string inputCause
/*
* An unbounded write is, for example `strcpy(..., tainted)`. We're looking
* for a tainted source buffer of an unbounded write, where this source buffer
* is a sink in the taint-tracking analysis.
*
* In the case of `gets` and `scanf`, where the source buffer is implicit, the
* `BufferWrite` library reports the source buffer to be the same as the
* destination buffer. Since those destination-buffer arguments are also
* modeled in the taint-tracking library as being _sources_ of taint, they are
* in practice reported as being tainted because the `security.TaintTracking`
* library does not distinguish between taint going into an argument and out of
* an argument. Thus, we get the desired alerts.
*/
from BufferWrite bw, Expr inputSource, Expr tainted, PathNode sourceNode, PathNode sinkNode
where
isUnboundedWrite(bw) and
tainted2(bw.getASource(), inputSource, inputCause)
select bw, "This '" + bw.getBWDesc() + "' with input from $@ may overflow the destination.",
inputSource, inputCause
taintedWithPath(inputSource, tainted, sourceNode, sinkNode) and
unboundedWriteSource(tainted, bw)
select bw, sourceNode, sinkNode,
"This '" + bw.getBWDesc() + "' with input from $@ may overflow the destination.", inputSource,
inputSource.toString()

View File

@@ -3,7 +3,7 @@
* @description Using externally-controlled format strings in
* printf-style functions can lead to buffer overflows
* or data representation problems.
* @kind problem
* @kind path-problem
* @problem.severity warning
* @precision medium
* @id cpp/tainted-format-string
@@ -16,12 +16,21 @@ import cpp
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.FunctionWithWrappers
import semmle.code.cpp.security.TaintTracking
import TaintedWithPath
from PrintfLikeFunction printf, Expr arg, string printfFunction, Expr userValue, string cause
class Configuration extends TaintTrackingConfiguration {
override predicate isSink(Element tainted) {
exists(PrintfLikeFunction printf | printf.outermostWrapperFunctionCall(tainted, _))
}
}
from
PrintfLikeFunction printf, Expr arg, PathNode sourceNode, PathNode sinkNode,
string printfFunction, Expr userValue, string cause
where
printf.outermostWrapperFunctionCall(arg, printfFunction) and
tainted(userValue, arg) and
taintedWithPath(userValue, arg, sourceNode, sinkNode) and
isUserInput(userValue, cause)
select arg,
select arg, sourceNode, sinkNode,
"The value of this argument may come from $@ and is being used as a formatting argument to " +
printfFunction, userValue, cause

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,7 @@
* @name Cleartext storage of sensitive information in buffer
* @description Storing sensitive information in cleartext can expose it
* to an attacker.
* @kind problem
* @kind path-problem
* @problem.severity warning
* @precision medium
* @id cpp/cleartext-storage-buffer
@@ -14,12 +14,20 @@ import cpp
import semmle.code.cpp.security.BufferWrite
import semmle.code.cpp.security.TaintTracking
import semmle.code.cpp.security.SensitiveExprs
import TaintedWithPath
from BufferWrite w, Expr taintedArg, Expr taintSource, string taintCause, SensitiveExpr dest
class Configuration extends TaintTrackingConfiguration {
override predicate isSink(Element tainted) { exists(BufferWrite w | w.getASource() = tainted) }
}
from
BufferWrite w, Expr taintedArg, Expr taintSource, PathNode sourceNode, PathNode sinkNode,
string taintCause, SensitiveExpr dest
where
tainted(taintSource, taintedArg) and
taintedWithPath(taintSource, taintedArg, sourceNode, sinkNode) and
isUserInput(taintSource, taintCause) and
w.getASource() = taintedArg and
dest = w.getDest()
select w, "This write into buffer '" + dest.toString() + "' may contain unencrypted data from $@",
select w, sourceNode, sinkNode,
"This write into buffer '" + dest.toString() + "' may contain unencrypted data from $@",
taintSource, "user input (" + taintCause + ")"

View File

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

View File

@@ -3,7 +3,7 @@
* @description Using untrusted inputs in a statement that makes a
* security decision makes code vulnerable to
* attack.
* @kind problem
* @kind path-problem
* @problem.severity warning
* @precision medium
* @id cpp/tainted-permissions-check
@@ -12,14 +12,9 @@
*/
import semmle.code.cpp.security.TaintTracking
import TaintedWithPath
/**
* Holds if there is an 'if' statement whose condition `condition`
* is influenced by tainted data `source`, and the body contains
* `raise` which escalates privilege.
*/
predicate cwe807violation(Expr source, Expr condition, Expr raise) {
tainted(source, condition) and
predicate sensitiveCondition(Expr condition, Expr raise) {
raisesPrivilege(raise) and
exists(IfStmt ifstmt |
ifstmt.getCondition() = condition and
@@ -27,7 +22,19 @@ predicate cwe807violation(Expr source, Expr condition, Expr raise) {
)
}
from Expr source, Expr condition, Expr raise
where cwe807violation(source, condition, raise)
select condition, "Reliance on untrusted input $@ to raise privilege at $@", source,
source.toString(), raise, raise.toString()
class Configuration extends TaintTrackingConfiguration {
override predicate isSink(Element tainted) { sensitiveCondition(tainted, _) }
}
/*
* Produce an alert if there is an 'if' statement whose condition `condition`
* is influenced by tainted data `source`, and the body contains
* `raise` which escalates privilege.
*/
from Expr source, Expr condition, Expr raise, PathNode sourceNode, PathNode sinkNode
where
taintedWithPath(source, condition, sourceNode, sinkNode) and
sensitiveCondition(condition, raise)
select condition, sourceNode, sinkNode, "Reliance on untrusted input $@ to raise privilege at $@",
source, source.toString(), raise, raise.toString()

View File

@@ -0,0 +1,4 @@
- description: Standard Code Scanning queries for C and C++
- qlpack: codeql-cpp
- apply: code-scanning-selectors.yml
from: codeql-suite-helpers

View File

@@ -0,0 +1,138 @@
#include <stdlib.h>
#include <sys/param.h>
#include <unistd.h>
#include <pwd.h>
void callSetuidAndCheck(int uid) {
if (setuid(uid) != 0) {
exit(1);
}
}
void callSetgidAndCheck(int gid) {
if (setgid(gid) != 0) {
exit(1);
}
}
/// Correct ways to drop priv.
void correctDropPrivInline() {
if (setgroups(0, NULL)) {
exit(1);
}
if (setgid(-2) != 0) {
exit(1);
}
if (setuid(-2) != 0) {
exit(1);
}
}
void correctDropPrivInScope() {
{
if (setgroups(0, NULL)) {
exit(1);
}
}
{
if (setgid(-2) != 0) {
exit(1);
}
}
{
if (setuid(-2) != 0) {
exit(1);
}
}
}
void correctOrderForInitgroups() {
struct passwd *pw = getpwuid(0);
if (pw) {
if (initgroups(pw->pw_name, -2)) {
exit(1);
}
} else {
// Unhandled.
}
int rc = setuid(-2);
if (rc) {
exit(1);
}
}
void correctDropPrivInScopeParent() {
{
callSetgidAndCheck(-2);
}
correctOrderForInitgroups();
}
void incorrectNoReturnCodeCheck() {
int user = -2;
if (user) {
if (user) {
int rc = setgid(user);
(void)rc;
initgroups("nobody", user);
}
if (user) {
setuid(user);
}
}
}
void correctDropPrivInFunctionCall() {
if (setgroups(0, NULL)) {
exit(1);
}
callSetgidAndCheck(-2);
callSetuidAndCheck(-2);
}
/// Incorrect, out of order gid and uid.
/// Calling uid before gid will fail.
void incorrectDropPrivOutOfOrderInline() {
if (setuid(-2) != 0) {
exit(1);
}
if (setgid(-2) != 0) {
exit(1);
}
}
void incorrectDropPrivOutOfOrderInScope() {
{
if (setuid(-2) != 0) {
exit(1);
}
}
setgid(-2);
}
void incorrectDropPrivOutOfOrderWithFunction() {
callSetuidAndCheck(-2);
if (setgid(-2) != 0) {
exit(1);
}
}
void incorrectDropPrivOutOfOrderWithFunction2() {
callSetuidAndCheck(-2);
callSetgidAndCheck(-2);
}
void incorrectDropPrivNoCheck() {
setgid(-2);
setuid(-2);
}

View File

@@ -0,0 +1,35 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>The code attempts to drop privilege in an incorrect order by
erroneous dropping user privilege before groups. This has security
impact if the return codes are not checked.</p>
<p>False positives include code performing negative checks, making
sure that setgid or setgroups does not work, meaning permissions are
dropped. Additionally, other forms of sandboxing may be present removing
any residual risk, for example a dedicated user namespace.</p>
</overview>
<recommendation>
<p>Set the new group ID, then set the target user's intended groups by
dropping previous supplemental source groups and initializing target
groups, and finally set the target user.</p>
</recommendation>
<example>
<p>The following example demonstrates out of order calls.</p>
<sample src="PrivilegeDroppingOutoforder.c" />
</example>
<references>
<li>CERT C Coding Standard:
<a href="https://wiki.sei.cmu.edu/confluence/display/c/POS37-C.+Ensure+that+privilege+relinquishment+is+successful">POS37-C. Ensure that privilege relinquishment is successful</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,101 @@
/**
* @name LinuxPrivilegeDroppingOutoforder
* @description A syscall commonly associated with privilege dropping is being called out of order.
* Normally a process drops group ID and sets supplimental groups for the target user
* before setting the target user ID. This can have security impact if the return code
* from these methods is not checked.
* @kind problem
* @problem.severity recommendation
* @id cpp/drop-linux-privileges-outoforder
* @tags security
* external/cwe/cwe-273
* @precision medium
*/
import cpp
predicate argumentMayBeRoot(Expr e) {
e.getValue() = "0" or
e.(VariableAccess).getTarget().getName().toLowerCase().matches("%root%")
}
class SetuidLikeFunctionCall extends FunctionCall {
SetuidLikeFunctionCall() {
(getTarget().hasGlobalName("setuid") or getTarget().hasGlobalName("setresuid")) and
// setuid/setresuid with the root user are false positives.
not argumentMayBeRoot(getArgument(0))
}
}
class SetuidLikeWrapperCall extends FunctionCall {
SetuidLikeFunctionCall baseCall;
SetuidLikeWrapperCall() {
this = baseCall
or
exists(SetuidLikeWrapperCall fc |
this.getTarget() = fc.getEnclosingFunction() and
baseCall = fc.getBaseCall()
)
}
SetuidLikeFunctionCall getBaseCall() { result = baseCall }
}
class CallBeforeSetuidFunctionCall extends FunctionCall {
CallBeforeSetuidFunctionCall() {
(
getTarget().hasGlobalName("setgid") or
getTarget().hasGlobalName("setresgid") or
// Compatibility may require skipping initgroups and setgroups return checks.
// A stricter best practice is to check the result and errnor for EPERM.
getTarget().hasGlobalName("initgroups") or
getTarget().hasGlobalName("setgroups")
) and
// setgid/setresgid/etc with the root group are false positives.
not argumentMayBeRoot(getArgument(0))
}
}
class CallBeforeSetuidWrapperCall extends FunctionCall {
CallBeforeSetuidFunctionCall baseCall;
CallBeforeSetuidWrapperCall() {
this = baseCall
or
exists(CallBeforeSetuidWrapperCall fc |
this.getTarget() = fc.getEnclosingFunction() and
baseCall = fc.getBaseCall()
)
}
CallBeforeSetuidFunctionCall getBaseCall() { result = baseCall }
}
predicate setuidBeforeSetgid(
SetuidLikeWrapperCall setuidWrapper, CallBeforeSetuidWrapperCall setgidWrapper
) {
setgidWrapper.getAPredecessor+() = setuidWrapper
}
predicate isAccessed(FunctionCall fc) {
exists(Variable v | v.getAnAssignedValue() = fc)
or
exists(Operation c | fc = c.getAChild() | c.isCondition())
or
// ignore pattern where result is intentionally ignored by a cast to void.
fc.hasExplicitConversion()
}
from Function func, CallBeforeSetuidFunctionCall fc, SetuidLikeFunctionCall setuid
where
setuidBeforeSetgid(setuid, fc) and
// Require the call return code to be used in a condition or assigned.
// This introduces false negatives where the return is checked but then
// errno == EPERM allows execution to continue.
not isAccessed(fc) and
func = fc.getEnclosingFunction()
select fc,
"This function is called within " + func + ", and potentially after " +
"$@, and may not succeed. Be sure to check the return code and errno, otherwise permissions " +
"may not be dropped.", setuid, setuid.getTarget().getName()

View File

@@ -30,7 +30,13 @@ predicate functionsMissingReturnStmt(Function f, ControlFlowNode blame) {
) and
exists(ReturnStmt s |
f.getAPredecessor() = s and
blame = s.getAPredecessor()
(
blame = s.getAPredecessor() and
count(blame.getASuccessor()) = 1
or
blame = s and
exists(ControlFlowNode pred | pred = s.getAPredecessor() | count(pred.getASuccessor()) != 1)
)
)
}

View File

@@ -133,10 +133,16 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
*/
Type getUnspecifiedType() { result = getType().getUnspecifiedType() }
/** Gets the nth parameter of this function. */
/**
* Gets the nth parameter of this function. There is no result for the
* implicit `this` parameter, and there is no `...` varargs pseudo-parameter.
*/
Parameter getParameter(int n) { params(unresolveElement(result), underlyingElement(this), n, _) }
/** Gets a parameter of this function. */
/**
* Gets a parameter of this function. There is no result for the implicit
* `this` parameter, and there is no `...` varargs pseudo-parameter.
*/
Parameter getAParameter() { params(unresolveElement(result), underlyingElement(this), _, _) }
/**

View File

@@ -381,10 +381,10 @@ class StaticStorageDurationVariable extends Variable {
}
/**
* Holds if the initializer for this variable is evaluated at compile time.
* Holds if the initializer for this variable is evaluated at runtime.
*/
predicate hasConstantInitialization() {
not runtimeExprInStaticInitializer(this.getInitializer().getExpr())
predicate hasDynamicInitialization() {
runtimeExprInStaticInitializer(this.getInitializer().getExpr())
}
}
@@ -397,18 +397,28 @@ class StaticStorageDurationVariable extends Variable {
*/
private predicate runtimeExprInStaticInitializer(Expr e) {
inStaticInitializer(e) and
if e instanceof AggregateLiteral
if e instanceof AggregateLiteral // in sync with the cast in `inStaticInitializer`
then runtimeExprInStaticInitializer(e.getAChild())
else not e.getFullyConverted().isConstant()
}
/** Holds if `e` is part of the initializer of a `StaticStorageDurationVariable`. */
/**
* Holds if `e` is the initializer of a `StaticStorageDurationVariable`, either
* directly or below some top-level `AggregateLiteral`s.
*/
private predicate inStaticInitializer(Expr e) {
exists(StaticStorageDurationVariable var | e = var.getInitializer().getExpr())
or
inStaticInitializer(e.getParent())
// The cast to `AggregateLiteral` ensures we only compute what'll later be
// needed by `runtimeExprInStaticInitializer`.
inStaticInitializer(e.getParent().(AggregateLiteral))
}
/**
* A C++ local variable declared as `static`.
*/
class StaticLocalVariable extends LocalVariable, StaticStorageDurationVariable { }
/**
* A C/C++ variable which has global scope or namespace scope. For example the
* variables `a` and `b` in the following code:

View File

@@ -441,9 +441,9 @@ private Node getControlOrderChildSparse(Node n, int i) {
* thus should not have control flow computed.
*/
private predicate skipInitializer(Initializer init) {
exists(LocalVariable local |
exists(StaticLocalVariable local |
init = local.getInitializer() and
local.(StaticStorageDurationVariable).hasConstantInitialization()
not local.hasDynamicInitialization()
)
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -243,7 +243,7 @@ private module Cached {
* - Types are checked using the `compatibleTypes()` relation.
*/
cached
module Final {
private module Final {
/**
* Holds if `p` can flow to `node` in the same callable using only
* value-preserving steps, not taking call contexts into account.

View File

@@ -0,0 +1,175 @@
/**
* Provides consistency queries for checking invariants in the language-specific
* data-flow classes and predicates.
*/
private import DataFlowImplSpecific::Private
private import DataFlowImplSpecific::Public
private import tainttracking1.TaintTrackingParameter::Private
private import tainttracking1.TaintTrackingParameter::Public
module Consistency {
private class RelevantNode extends Node {
RelevantNode() {
this instanceof ArgumentNode or
this instanceof ParameterNode or
this instanceof ReturnNode or
this = getAnOutNode(_, _) or
simpleLocalFlowStep(this, _) or
simpleLocalFlowStep(_, this) or
jumpStep(this, _) or
jumpStep(_, this) or
storeStep(this, _, _) or
storeStep(_, _, this) or
readStep(this, _, _) or
readStep(_, _, this) or
defaultAdditionalTaintStep(this, _) or
defaultAdditionalTaintStep(_, this)
}
}
query predicate uniqueEnclosingCallable(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
c = count(n.getEnclosingCallable()) and
c != 1 and
msg = "Node should have one enclosing callable but has " + c + "."
)
}
query predicate uniqueTypeBound(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
c = count(n.getTypeBound()) and
c != 1 and
msg = "Node should have one type bound but has " + c + "."
)
}
query predicate uniqueTypeRepr(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
c = count(getErasedRepr(n.getTypeBound())) and
c != 1 and
msg = "Node should have one type representation but has " + c + "."
)
}
query predicate uniqueNodeLocation(Node n, string msg) {
exists(int c |
c =
count(string filepath, int startline, int startcolumn, int endline, int endcolumn |
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
) and
c != 1 and
msg = "Node should have one location but has " + c + "."
)
}
query predicate missingLocation(string msg) {
exists(int c |
c =
strictcount(Node n |
not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
)
) and
msg = "Nodes without location: " + c
)
}
query predicate uniqueNodeToString(Node n, string msg) {
exists(int c |
c = count(n.toString()) and
c != 1 and
msg = "Node should have one toString but has " + c + "."
)
}
query predicate missingToString(string msg) {
exists(int c |
c = strictcount(Node n | not exists(n.toString())) and
msg = "Nodes without toString: " + c
)
}
query predicate parameterCallable(ParameterNode p, string msg) {
exists(DataFlowCallable c | p.isParameterOf(c, _) and c != p.getEnclosingCallable()) and
msg = "Callable mismatch for parameter."
}
query predicate localFlowIsLocal(Node n1, Node n2, string msg) {
simpleLocalFlowStep(n1, n2) and
n1.getEnclosingCallable() != n2.getEnclosingCallable() and
msg = "Local flow step does not preserve enclosing callable."
}
private DataFlowType typeRepr() { result = getErasedRepr(any(Node n).getTypeBound()) }
query predicate compatibleTypesReflexive(DataFlowType t, string msg) {
t = typeRepr() and
not compatibleTypes(t, t) and
msg = "Type compatibility predicate is not reflexive."
}
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
isUnreachableInCall(n, call) and
exists(DataFlowCallable c |
c = n.getEnclosingCallable() and
not viableCallable(call) = c
) and
msg = "Call context for isUnreachableInCall is inconsistent with call graph."
}
query predicate localCallNodes(DataFlowCall call, Node n, string msg) {
(
n = getAnOutNode(call, _) and
msg = "OutNode and call does not share enclosing callable."
or
n.(ArgumentNode).argumentOf(call, _) and
msg = "ArgumentNode and call does not share enclosing callable."
) and
n.getEnclosingCallable() != call.getEnclosingCallable()
}
query predicate postIsNotPre(PostUpdateNode n, string msg) {
n.getPreUpdateNode() = n and msg = "PostUpdateNode should not equal its pre-update node."
}
query predicate postHasUniquePre(PostUpdateNode n, string msg) {
exists(int c |
c = count(n.getPreUpdateNode()) and
c != 1 and
msg = "PostUpdateNode should have one pre-update node but has " + c + "."
)
}
query predicate uniquePostUpdate(Node n, string msg) {
1 < strictcount(PostUpdateNode post | post.getPreUpdateNode() = n) and
msg = "Node has multiple PostUpdateNodes."
}
query predicate postIsInSameCallable(PostUpdateNode n, string msg) {
n.getEnclosingCallable() != n.getPreUpdateNode().getEnclosingCallable() and
msg = "PostUpdateNode does not share callable with its pre-update node."
}
private predicate hasPost(Node n) { exists(PostUpdateNode post | post.getPreUpdateNode() = n) }
query predicate reverseRead(Node n, string msg) {
exists(Node n2 | readStep(n, _, n2) and hasPost(n2) and not hasPost(n)) and
msg = "Origin of readStep is missing a PostUpdateNode."
}
query predicate storeIsPostUpdate(Node n, string msg) {
storeStep(_, _, n) and
not n instanceof PostUpdateNode and
msg = "Store targets should be PostUpdateNodes."
}
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
not hasPost(n) and
not isImmutableOrUnobservable(n) and
msg = "ArgumentNode is missing PostUpdateNode."
}
}

View File

@@ -293,3 +293,27 @@ class DataFlowCall extends Expr {
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
int accessPathLimit() { result = 5 }
/**
* Holds if `n` does not require a `PostUpdateNode` as it either cannot be
* modified or its modification cannot be observed, for example if it is a
* freshly created object that is not saved in a variable.
*
* This predicate is only used for consistency checks.
*/
predicate isImmutableOrUnobservable(Node n) {
// Is the null pointer (or something that's not really a pointer)
exists(n.asExpr().getValue())
or
// Isn't a pointer or is a pointer to const
forall(DerivedType dt | dt = n.asExpr().getActualType() |
dt.getBaseType().isConst()
or
dt.getBaseType() instanceof RoutineType
)
or
// Isn't something we can track
n.asExpr() instanceof Call
// The above list of cases isn't exhaustive, but it narrows down the
// consistency alerts enough that most of them are interesting.
}

View File

@@ -1,3 +1,13 @@
/**
* Provides an implementation of global (interprocedural) taint tracking.
* This file re-exports the local (intraprocedural) taint-tracking analysis
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
* exposed through the `Configuration` class. For some languages, this file
* exists in several identical copies, allowing queries to use multiple
* `Configuration` classes that depend on each other without introducing
* mutual recursion among those configurations.
*/
import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private

View File

@@ -1,3 +1,13 @@
/**
* Provides an implementation of global (interprocedural) taint tracking.
* This file re-exports the local (intraprocedural) taint-tracking analysis
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
* exposed through the `Configuration` class. For some languages, this file
* exists in several identical copies, allowing queries to use multiple
* `Configuration` classes that depend on each other without introducing
* mutual recursion among those configurations.
*/
import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private

View File

@@ -16,7 +16,7 @@ class UnaryMinusExpr extends UnaryArithmeticOperation, @arithnegexpr {
override string getCanonicalQLClass() { result = "UnaryMinusExpr" }
override int getPrecedence() { result = 15 }
override int getPrecedence() { result = 16 }
}
/**
@@ -30,7 +30,7 @@ class UnaryPlusExpr extends UnaryArithmeticOperation, @unaryplusexpr {
override string getCanonicalQLClass() { result = "UnaryPlusExpr" }
override int getPrecedence() { result = 15 }
override int getPrecedence() { result = 16 }
}
/**
@@ -109,7 +109,7 @@ class PrefixIncrExpr extends IncrementOperation, PrefixCrementOperation, @preinc
override string getCanonicalQLClass() { result = "PrefixIncrExpr" }
override int getPrecedence() { result = 15 }
override int getPrecedence() { result = 16 }
}
/**
@@ -125,7 +125,7 @@ class PrefixDecrExpr extends DecrementOperation, PrefixCrementOperation, @predec
override string getCanonicalQLClass() { result = "PrefixDecrExpr" }
override int getPrecedence() { result = 15 }
override int getPrecedence() { result = 16 }
}
/**
@@ -141,7 +141,7 @@ class PostfixIncrExpr extends IncrementOperation, PostfixCrementOperation, @post
override string getCanonicalQLClass() { result = "PostfixIncrExpr" }
override int getPrecedence() { result = 16 }
override int getPrecedence() { result = 17 }
override string toString() { result = "... " + getOperator() }
}
@@ -159,7 +159,7 @@ class PostfixDecrExpr extends DecrementOperation, PostfixCrementOperation, @post
override string getCanonicalQLClass() { result = "PostfixDecrExpr" }
override int getPrecedence() { result = 16 }
override int getPrecedence() { result = 17 }
override string toString() { result = "... " + getOperator() }
}
@@ -210,7 +210,7 @@ class AddExpr extends BinaryArithmeticOperation, @addexpr {
override string getCanonicalQLClass() { result = "AddExpr" }
override int getPrecedence() { result = 12 }
override int getPrecedence() { result = 13 }
}
/**
@@ -224,7 +224,7 @@ class SubExpr extends BinaryArithmeticOperation, @subexpr {
override string getCanonicalQLClass() { result = "SubExpr" }
override int getPrecedence() { result = 12 }
override int getPrecedence() { result = 13 }
}
/**
@@ -238,7 +238,7 @@ class MulExpr extends BinaryArithmeticOperation, @mulexpr {
override string getCanonicalQLClass() { result = "MulExpr" }
override int getPrecedence() { result = 13 }
override int getPrecedence() { result = 14 }
}
/**
@@ -252,7 +252,7 @@ class DivExpr extends BinaryArithmeticOperation, @divexpr {
override string getCanonicalQLClass() { result = "DivExpr" }
override int getPrecedence() { result = 13 }
override int getPrecedence() { result = 14 }
}
/**
@@ -266,7 +266,7 @@ class RemExpr extends BinaryArithmeticOperation, @remexpr {
override string getCanonicalQLClass() { result = "RemExpr" }
override int getPrecedence() { result = 13 }
override int getPrecedence() { result = 14 }
}
/**
@@ -283,7 +283,7 @@ class ImaginaryMulExpr extends BinaryArithmeticOperation, @jmulexpr {
override string getCanonicalQLClass() { result = "ImaginaryMulExpr" }
override int getPrecedence() { result = 13 }
override int getPrecedence() { result = 14 }
}
/**
@@ -300,7 +300,7 @@ class ImaginaryDivExpr extends BinaryArithmeticOperation, @jdivexpr {
override string getCanonicalQLClass() { result = "ImaginaryDivExpr" }
override int getPrecedence() { result = 13 }
override int getPrecedence() { result = 14 }
}
/**
@@ -318,7 +318,7 @@ class RealImaginaryAddExpr extends BinaryArithmeticOperation, @fjaddexpr {
override string getCanonicalQLClass() { result = "RealImaginaryAddExpr" }
override int getPrecedence() { result = 12 }
override int getPrecedence() { result = 13 }
}
/**
@@ -336,7 +336,7 @@ class ImaginaryRealAddExpr extends BinaryArithmeticOperation, @jfaddexpr {
override string getCanonicalQLClass() { result = "ImaginaryRealAddExpr" }
override int getPrecedence() { result = 12 }
override int getPrecedence() { result = 13 }
}
/**
@@ -354,7 +354,7 @@ class RealImaginarySubExpr extends BinaryArithmeticOperation, @fjsubexpr {
override string getCanonicalQLClass() { result = "RealImaginarySubExpr" }
override int getPrecedence() { result = 12 }
override int getPrecedence() { result = 13 }
}
/**
@@ -372,7 +372,7 @@ class ImaginaryRealSubExpr extends BinaryArithmeticOperation, @jfsubexpr {
override string getCanonicalQLClass() { result = "ImaginaryRealSubExpr" }
override int getPrecedence() { result = 12 }
override int getPrecedence() { result = 13 }
}
/**
@@ -416,7 +416,7 @@ class PointerAddExpr extends PointerArithmeticOperation, @paddexpr {
override string getCanonicalQLClass() { result = "PointerAddExpr" }
override int getPrecedence() { result = 12 }
override int getPrecedence() { result = 13 }
}
/**
@@ -431,7 +431,7 @@ class PointerSubExpr extends PointerArithmeticOperation, @psubexpr {
override string getCanonicalQLClass() { result = "PointerSubExpr" }
override int getPrecedence() { result = 12 }
override int getPrecedence() { result = 13 }
}
/**
@@ -446,5 +446,5 @@ class PointerDiffExpr extends PointerArithmeticOperation, @pdiffexpr {
override string getCanonicalQLClass() { result = "PointerDiffExpr" }
override int getPrecedence() { result = 12 }
override int getPrecedence() { result = 13 }
}

View File

@@ -14,7 +14,7 @@ class UnaryBitwiseOperation extends UnaryOperation, @un_bitwise_op_expr { }
class ComplementExpr extends UnaryBitwiseOperation, @complementexpr {
override string getOperator() { result = "~" }
override int getPrecedence() { result = 15 }
override int getPrecedence() { result = 16 }
override string getCanonicalQLClass() { result = "ComplementExpr" }
}
@@ -33,7 +33,7 @@ class BinaryBitwiseOperation extends BinaryOperation, @bin_bitwise_op_expr { }
class LShiftExpr extends BinaryBitwiseOperation, @lshiftexpr {
override string getOperator() { result = "<<" }
override int getPrecedence() { result = 11 }
override int getPrecedence() { result = 12 }
override string getCanonicalQLClass() { result = "LShiftExpr" }
}
@@ -47,7 +47,7 @@ class LShiftExpr extends BinaryBitwiseOperation, @lshiftexpr {
class RShiftExpr extends BinaryBitwiseOperation, @rshiftexpr {
override string getOperator() { result = ">>" }
override int getPrecedence() { result = 11 }
override int getPrecedence() { result = 12 }
override string getCanonicalQLClass() { result = "RShiftExpr" }
}

View File

@@ -8,6 +8,22 @@ abstract class BuiltInOperation extends Expr {
override string getCanonicalQLClass() { result = "BuiltInOperation" }
}
/**
* A C/C++ built-in operation that is used to support functions with variable numbers of arguments.
* This includes `va_start`, `va_end`, `va_copy`, and `va_arg`.
*/
class VarArgsExpr extends BuiltInOperation {
VarArgsExpr() {
this instanceof BuiltInVarArgsStart
or
this instanceof BuiltInVarArgsEnd
or
this instanceof BuiltInVarArg
or
this instanceof BuiltInVarArgCopy
}
}
/**
* A C/C++ `__builtin_va_start` built-in operation (used by some
* implementations of `va_start`).
@@ -20,6 +36,16 @@ class BuiltInVarArgsStart extends BuiltInOperation, @vastartexpr {
override string toString() { result = "__builtin_va_start" }
override string getCanonicalQLClass() { result = "BuiltInVarArgsStart" }
/**
* Gets the `va_list` argument.
*/
final Expr getVAList() { result = getChild(0) }
/**
* Gets the argument that specifies the last named parameter before the ellipsis.
*/
final VariableAccess getLastNamedParameter() { result = getChild(1) }
}
/**
@@ -35,6 +61,11 @@ class BuiltInVarArgsEnd extends BuiltInOperation, @vaendexpr {
override string toString() { result = "__builtin_va_end" }
override string getCanonicalQLClass() { result = "BuiltInVarArgsEnd" }
/**
* Gets the `va_list` argument.
*/
final Expr getVAList() { result = getChild(0) }
}
/**
@@ -48,6 +79,11 @@ class BuiltInVarArg extends BuiltInOperation, @vaargexpr {
override string toString() { result = "__builtin_va_arg" }
override string getCanonicalQLClass() { result = "BuiltInVarArg" }
/**
* Gets the `va_list` argument.
*/
final Expr getVAList() { result = getChild(0) }
}
/**
@@ -63,6 +99,16 @@ class BuiltInVarArgCopy extends BuiltInOperation, @vacopyexpr {
override string toString() { result = "__builtin_va_copy" }
override string getCanonicalQLClass() { result = "BuiltInVarArgCopy" }
/**
* Gets the destination `va_list` argument.
*/
final Expr getDestinationVAList() { result = getChild(0) }
/**
* Gets the the source `va_list` argument.
*/
final Expr getSourceVAList() { result = getChild(1) }
}
/**

View File

@@ -74,7 +74,7 @@ abstract class Call extends Expr, NameQualifiableElement {
*/
abstract Function getTarget();
override int getPrecedence() { result = 16 }
override int getPrecedence() { result = 17 }
override string toString() { none() }
@@ -196,7 +196,7 @@ class FunctionCall extends Call, @funbindexpr {
* constructor calls, this predicate instead gets the `Class` of the constructor
* being called.
*/
private Type getTargetType() { result = Call.super.getType().stripType() }
Type getTargetType() { result = Call.super.getType().stripType() }
/**
* Gets the expected return type of the function called by this call.

View File

@@ -84,7 +84,7 @@ class CStyleCast extends Cast, @c_style_cast {
override string getCanonicalQLClass() { result = "CStyleCast" }
override int getPrecedence() { result = 15 }
override int getPrecedence() { result = 16 }
}
/**
@@ -103,7 +103,7 @@ class StaticCast extends Cast, @static_cast {
override string getCanonicalQLClass() { result = "StaticCast" }
override int getPrecedence() { result = 16 }
override int getPrecedence() { result = 17 }
}
/**
@@ -121,7 +121,7 @@ class ConstCast extends Cast, @const_cast {
override string getCanonicalQLClass() { result = "ConstCast" }
override int getPrecedence() { result = 16 }
override int getPrecedence() { result = 17 }
}
/**
@@ -139,7 +139,7 @@ class ReinterpretCast extends Cast, @reinterpret_cast {
override string getCanonicalQLClass() { result = "ReinterpretCast" }
override int getPrecedence() { result = 16 }
override int getPrecedence() { result = 17 }
}
private predicate isArithmeticOrEnum(Type type) {
@@ -608,7 +608,7 @@ class PrvalueAdjustmentConversion extends Cast {
class DynamicCast extends Cast, @dynamic_cast {
override string toString() { result = "dynamic_cast<" + this.getType().getName() + ">..." }
override int getPrecedence() { result = 16 }
override int getPrecedence() { result = 17 }
override string getCanonicalQLClass() { result = "DynamicCast" }
@@ -631,7 +631,7 @@ class UuidofOperator extends Expr, @uuidof {
else result = "__uuidof(0)"
}
override int getPrecedence() { result = 15 }
override int getPrecedence() { result = 16 }
/** Gets the contained type. */
Type getTypeOperand() { uuidof_bind(underlyingElement(this), unresolveElement(result)) }
@@ -669,7 +669,7 @@ class TypeidOperator extends Expr, @type_id {
override string toString() { result = "typeid ..." }
override int getPrecedence() { result = 16 }
override int getPrecedence() { result = 17 }
override predicate mayBeImpure() { this.getExpr().mayBeImpure() }
@@ -700,7 +700,7 @@ class SizeofPackOperator extends Expr, @sizeof_pack {
* A C/C++ sizeof expression.
*/
abstract class SizeofOperator extends Expr, @runtime_sizeof {
override int getPrecedence() { result = 15 }
override int getPrecedence() { result = 16 }
}
/**
@@ -763,7 +763,7 @@ class SizeofTypeOperator extends SizeofOperator {
* A C++11 `alignof` expression.
*/
abstract class AlignofOperator extends Expr, @runtime_alignof {
override int getPrecedence() { result = 15 }
override int getPrecedence() { result = 16 }
}
/**

View File

@@ -2,6 +2,7 @@ import semmle.code.cpp.Element
private import semmle.code.cpp.Enclosing
private import semmle.code.cpp.internal.ResolveClass
private import semmle.code.cpp.internal.AddressConstantExpression
private import semmle.code.cpp.models.implementations.Allocation
/**
* A C/C++ expression.
@@ -642,7 +643,7 @@ class AddressOfExpr extends UnaryOperation, @address_of {
override string getOperator() { result = "&" }
override int getPrecedence() { result = 15 }
override int getPrecedence() { result = 16 }
override predicate mayBeImpure() { this.getOperand().mayBeImpure() }
@@ -664,7 +665,7 @@ class ReferenceToExpr extends Conversion, @reference_to {
override string getCanonicalQLClass() { result = "ReferenceToExpr" }
override int getPrecedence() { result = 15 }
override int getPrecedence() { result = 16 }
}
/**
@@ -687,7 +688,7 @@ class PointerDereferenceExpr extends UnaryOperation, @indirect {
override string getOperator() { result = "*" }
override int getPrecedence() { result = 15 }
override int getPrecedence() { result = 16 }
override predicate mayBeImpure() {
this.getChild(0).mayBeImpure() or
@@ -721,7 +722,7 @@ class ReferenceDereferenceExpr extends Conversion, @ref_indirect {
* A C++ `new` or `new[]` expression.
*/
class NewOrNewArrayExpr extends Expr, @any_new_expr {
override int getPrecedence() { result = 15 }
override int getPrecedence() { result = 16 }
/**
* Gets the `operator new` or `operator new[]` that allocates storage.
@@ -804,8 +805,10 @@ class NewOrNewArrayExpr extends Expr, @any_new_expr {
* call the constructor of `T` but will not allocate memory.
*/
Expr getPlacementPointer() {
isStandardPlacementNewAllocator(this.getAllocator()) and
result = this.getAllocatorCall().getArgument(1)
result =
this
.getAllocatorCall()
.getArgument(this.getAllocator().(OperatorNewAllocationFunction).getPlacementArgument())
}
}
@@ -898,7 +901,7 @@ class DeleteExpr extends Expr, @delete_expr {
override string getCanonicalQLClass() { result = "DeleteExpr" }
override int getPrecedence() { result = 15 }
override int getPrecedence() { result = 16 }
/**
* Gets the compile-time type of the object being deleted.
@@ -972,7 +975,7 @@ class DeleteArrayExpr extends Expr, @delete_array_expr {
override string getCanonicalQLClass() { result = "DeleteArrayExpr" }
override int getPrecedence() { result = 15 }
override int getPrecedence() { result = 16 }
/**
* Gets the element type of the array being deleted.
@@ -1194,12 +1197,6 @@ private predicate convparents(Expr child, int idx, Element parent) {
)
}
private predicate isStandardPlacementNewAllocator(Function operatorNew) {
operatorNew.getName().matches("operator new%") and
operatorNew.getNumberOfParameters() = 2 and
operatorNew.getParameter(1).getType() instanceof VoidPointerType
}
// Pulled out for performance. See QL-796.
private predicate hasNoConversions(Expr e) { not e.hasConversion() }
@@ -1216,3 +1213,18 @@ private predicate constantTemplateLiteral(Expr e) {
or
constantTemplateLiteral(e.(Cast).getExpr())
}
/**
* A C++ three-way comparison operation, also known as the _spaceship
* operation_. This is specific to C++20 and later.
* ```
* auto c = (a <=> b);
* ```
*/
class SpaceshipExpr extends BinaryOperation, @spaceshipexpr {
override string getCanonicalQLClass() { result = "SpaceshipExpr" }
override int getPrecedence() { result = 11 }
override string getOperator() { result = "<=>" }
}

View File

@@ -16,7 +16,7 @@ class NotExpr extends UnaryLogicalOperation, @notexpr {
override string getCanonicalQLClass() { result = "NotExpr" }
override int getPrecedence() { result = 15 }
override int getPrecedence() { result = 16 }
}
/**

View File

@@ -2,6 +2,7 @@ import cpp
import semmle.code.cpp.security.Security
private import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.dataflow.DataFlow2
private import semmle.code.cpp.ir.dataflow.DataFlow3
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.internal.DataFlowDispatch as Dispatch
private import semmle.code.cpp.models.interfaces.Taint
@@ -60,7 +61,14 @@ private DataFlow::Node getNodeForSource(Expr source) {
(
result = DataFlow::exprNode(source)
or
result = DataFlow::definitionByReferenceNode(source)
// Some of the sources in `isUserInput` are intended to match the value of
// an expression, while others (those modeled below) are intended to match
// the taint that propagates out of an argument, like the `char *` argument
// to `gets`. It's impossible here to tell which is which, but the "access
// to argv" source is definitely not intended to match an output argument,
// and it causes false positives if we let it.
result = DataFlow::definitionByReferenceNode(source) and
not argv(source.(VariableAccess).getTarget())
)
}
@@ -76,6 +84,8 @@ private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
}
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
@@ -96,6 +106,8 @@ private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
}
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
private class FromGlobalVarTaintTrackingCfg extends DataFlow2::Configuration {
@@ -119,6 +131,8 @@ private class FromGlobalVarTaintTrackingCfg extends DataFlow2::Configuration {
}
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
private predicate readsVariable(LoadInstruction load, Variable var) {
@@ -163,6 +177,12 @@ private predicate nodeIsBarrier(DataFlow::Node node) {
)
}
private predicate nodeIsBarrierIn(DataFlow::Node node) {
// don't use dataflow into taint sources, as this leads to duplicate results.
node = getNodeForSource(any(Expr e))
}
cached
private predicate instructionTaintStep(Instruction i1, Instruction i2) {
// Expressions computed from tainted data are also tainted
exists(CallInstruction call, int argIndex | call = i2 |
@@ -182,7 +202,7 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
i2.(UnaryInstruction).getUnary() = i1
or
i2.(ChiInstruction).getPartial() = i1 and
not isChiForAllAliasedMemory(i2)
not i2.isResultConflated()
or
exists(BinaryInstruction bin |
bin = i2 and
@@ -275,19 +295,6 @@ private predicate modelTaintToParameter(Function f, int parameterIn, int paramet
)
}
/**
* Holds if `chi` is on the chain of chi-instructions for all aliased memory.
* Taint should not pass through these instructions since they tend to mix up
* unrelated objects.
*/
private predicate isChiForAllAliasedMemory(Instruction instr) {
instr.(ChiInstruction).getTotal() instanceof AliasedDefinitionInstruction
or
isChiForAllAliasedMemory(instr.(ChiInstruction).getTotal())
or
isChiForAllAliasedMemory(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
}
private predicate modelTaintToReturnValue(Function f, int parameterIn) {
// Taint flow from parameter to return value
exists(FunctionInput modelIn, FunctionOutput modelOut |
@@ -353,6 +360,16 @@ private Element adjustedSink(DataFlow::Node sink) {
result.(AssignOperation).getAnOperand() = sink.asExpr()
}
/**
* Holds if `tainted` may contain taint from `source`.
*
* A tainted expression is either directly user input, or is
* computed from user input in a way that users can probably
* control the exact output of the computation.
*
* This doesn't include data flow through global variables.
* If you need that you must call `taintedIncludingGlobalVars`.
*/
cached
predicate tainted(Expr source, Element tainted) {
exists(DefaultTaintTrackingCfg cfg, DataFlow::Node sink |
@@ -361,6 +378,21 @@ predicate tainted(Expr source, Element tainted) {
)
}
/**
* Holds if `tainted` may contain taint from `source`, where the taint passed
* through a global variable named `globalVar`.
*
* A tainted expression is either directly user input, or is
* computed from user input in a way that users can probably
* control the exact output of the computation.
*
* This version gives the same results as tainted but also includes
* data flow through global variables.
*
* The parameter `globalVar` is the qualified name of the last global variable
* used to move the value from source to tainted. If the taint did not pass
* through a global variable, then `globalVar = ""`.
*/
cached
predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
tainted(source, tainted) and
@@ -378,11 +410,245 @@ predicate taintedIncludingGlobalVars(Expr source, Element tainted, string global
)
}
/**
* Gets the global variable whose qualified name is `id`. Use this predicate
* together with `taintedIncludingGlobalVars`. Example:
*
* ```
* exists(string varName |
* taintedIncludingGlobalVars(source, tainted, varName) and
* var = globalVarFromId(varName)
* )
* ```
*/
GlobalOrNamespaceVariable globalVarFromId(string id) { id = result.getQualifiedName() }
/**
* Resolve potential target function(s) for `call`.
*
* If `call` is a call through a function pointer (`ExprCall`) or
* targets a virtual method, simple data flow analysis is performed
* in order to identify target(s).
*/
Function resolveCall(Call call) {
exists(CallInstruction callInstruction |
callInstruction.getAST() = call and
result = Dispatch::viableCallable(callInstruction)
)
}
/**
* Provides definitions for augmenting source/sink pairs with data-flow paths
* between them. From a `@kind path-problem` query, import this module in the
* global scope, extend `TaintTrackingConfiguration`, and use `taintedWithPath`
* in place of `tainted`.
*
* Importing this module will also import the query predicates that contain the
* taint paths.
*/
module TaintedWithPath {
private newtype TSingleton = MkSingleton()
/**
* A taint-tracking configuration that matches sources and sinks in the same
* way as the `tainted` predicate.
*
* Override `isSink` and `taintThroughGlobals` as needed, but do not provide
* a characteristic predicate.
*/
class TaintTrackingConfiguration extends TSingleton {
/** Override this to specify which elements are sinks in this configuration. */
abstract predicate isSink(Element e);
/**
* Override this predicate to `any()` to allow taint to flow through global
* variables.
*/
predicate taintThroughGlobals() { none() }
/** Gets a textual representation of this element. */
string toString() { result = "TaintTrackingConfiguration" }
}
private class AdjustedConfiguration extends DataFlow3::Configuration {
AdjustedConfiguration() { this = "AdjustedConfiguration" }
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
override predicate isSink(DataFlow::Node sink) {
exists(TaintTrackingConfiguration cfg | cfg.isSink(adjustedSink(sink)))
}
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
instructionTaintStep(n1.asInstruction(), n2.asInstruction())
or
exists(TaintTrackingConfiguration cfg | cfg.taintThroughGlobals() |
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
or
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
)
}
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
/*
* A sink `Element` may map to multiple `DataFlowX::PathNode`s via (the
* inverse of) `adjustedSink`. For example, an `Expr` maps to all its
* conversions, and a `Variable` maps to all loads and stores from it. Because
* the path node is part of the tuple that constitutes the alert, this leads
* to duplicate alerts.
*
* To avoid showing duplicates, we edit the graph to replace the final node
* coming from the data-flow library with a node that matches exactly the
* `Element` sink that's requested.
*
* The same is done for sources.
*/
private newtype TPathNode =
TWrapPathNode(DataFlow3::PathNode n) or
// There's a single newtype constructor for both sources and sinks since
// that makes it easiest to deal with the case where source = sink.
TEndpointPathNode(Element e) {
exists(AdjustedConfiguration cfg, DataFlow3::Node sourceNode, DataFlow3::Node sinkNode |
cfg.hasFlow(sourceNode, sinkNode)
|
sourceNode = getNodeForSource(e)
or
e = adjustedSink(sinkNode) and
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSink(e))
)
}
/** An opaque type used for the nodes of a data-flow path. */
class PathNode extends TPathNode {
/** Gets a textual representation of this element. */
string toString() { none() }
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
none()
}
}
private class WrapPathNode extends PathNode, TWrapPathNode {
DataFlow3::PathNode inner() { this = TWrapPathNode(result) }
override string toString() { result = this.inner().toString() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.inner().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
private class EndpointPathNode extends PathNode, TEndpointPathNode {
Expr inner() { this = TEndpointPathNode(result) }
override string toString() { result = this.inner().toString() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this
.inner()
.getLocation()
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
/** A PathNode whose `Element` is a source. It may also be a sink. */
private class InitialPathNode extends EndpointPathNode {
InitialPathNode() { exists(getNodeForSource(this.inner())) }
}
/** A PathNode whose `Element` is a sink. It may also be a source. */
private class FinalPathNode extends EndpointPathNode {
FinalPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSink(this.inner())) }
}
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
query predicate edges(PathNode a, PathNode b) {
DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), b.(WrapPathNode).inner())
or
// To avoid showing trivial-looking steps, we _replace_ the last node instead
// of adding an edge out of it.
exists(WrapPathNode sinkNode |
DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), sinkNode.inner()) and
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
)
or
// Same for the first node
exists(WrapPathNode sourceNode |
DataFlow3::PathGraph::edges(sourceNode.inner(), b.(WrapPathNode).inner()) and
sourceNode.inner().getNode() = getNodeForSource(a.(InitialPathNode).inner())
)
or
// Finally, handle the case where the path goes directly from a source to a
// sink, meaning that they both need to be translated.
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
DataFlow3::PathGraph::edges(sourceNode.inner(), sinkNode.inner()) and
sourceNode.inner().getNode() = getNodeForSource(a.(InitialPathNode).inner()) and
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
)
}
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
key = "semmle.label" and val = n.toString()
}
/**
* Holds if `tainted` may contain taint from `source`, where `sourceNode` and
* `sinkNode` are the corresponding `PathNode`s that can be used in a query
* to provide path explanations. Extend `TaintTrackingConfiguration` to use
* this predicate.
*
* A tainted expression is either directly user input, or is computed from
* user input in a way that users can probably control the exact output of
* the computation.
*/
predicate taintedWithPath(Expr source, Element tainted, PathNode sourceNode, PathNode sinkNode) {
exists(AdjustedConfiguration cfg, DataFlow3::Node flowSource, DataFlow3::Node flowSink |
source = sourceNode.(InitialPathNode).inner() and
flowSource = getNodeForSource(source) and
cfg.hasFlow(flowSource, flowSink) and
tainted = adjustedSink(flowSink) and
tainted = sinkNode.(FinalPathNode).inner()
)
}
private predicate isGlobalVariablePathNode(WrapPathNode n) {
n.inner().getNode().asVariable() instanceof GlobalOrNamespaceVariable
}
private predicate edgesWithoutGlobals(PathNode a, PathNode b) {
edges(a, b) and
not isGlobalVariablePathNode(a) and
not isGlobalVariablePathNode(b)
}
/**
* Holds if `tainted` can be reached from a taint source without passing
* through a global variable.
*/
predicate taintedWithoutGlobals(Element tainted) {
exists(PathNode sourceNode, FinalPathNode sinkNode |
sourceNode.(WrapPathNode).inner().getNode() = getNodeForSource(_) and
edgesWithoutGlobals+(sourceNode, sinkNode) and
tainted = sinkNode.inner()
)
}
}

View File

@@ -243,7 +243,7 @@ private module Cached {
* - Types are checked using the `compatibleTypes()` relation.
*/
cached
module Final {
private module Final {
/**
* Holds if `p` can flow to `node` in the same callable using only
* value-preserving steps, not taking call contexts into account.

View File

@@ -0,0 +1,175 @@
/**
* Provides consistency queries for checking invariants in the language-specific
* data-flow classes and predicates.
*/
private import DataFlowImplSpecific::Private
private import DataFlowImplSpecific::Public
private import tainttracking1.TaintTrackingParameter::Private
private import tainttracking1.TaintTrackingParameter::Public
module Consistency {
private class RelevantNode extends Node {
RelevantNode() {
this instanceof ArgumentNode or
this instanceof ParameterNode or
this instanceof ReturnNode or
this = getAnOutNode(_, _) or
simpleLocalFlowStep(this, _) or
simpleLocalFlowStep(_, this) or
jumpStep(this, _) or
jumpStep(_, this) or
storeStep(this, _, _) or
storeStep(_, _, this) or
readStep(this, _, _) or
readStep(_, _, this) or
defaultAdditionalTaintStep(this, _) or
defaultAdditionalTaintStep(_, this)
}
}
query predicate uniqueEnclosingCallable(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
c = count(n.getEnclosingCallable()) and
c != 1 and
msg = "Node should have one enclosing callable but has " + c + "."
)
}
query predicate uniqueTypeBound(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
c = count(n.getTypeBound()) and
c != 1 and
msg = "Node should have one type bound but has " + c + "."
)
}
query predicate uniqueTypeRepr(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
c = count(getErasedRepr(n.getTypeBound())) and
c != 1 and
msg = "Node should have one type representation but has " + c + "."
)
}
query predicate uniqueNodeLocation(Node n, string msg) {
exists(int c |
c =
count(string filepath, int startline, int startcolumn, int endline, int endcolumn |
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
) and
c != 1 and
msg = "Node should have one location but has " + c + "."
)
}
query predicate missingLocation(string msg) {
exists(int c |
c =
strictcount(Node n |
not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
)
) and
msg = "Nodes without location: " + c
)
}
query predicate uniqueNodeToString(Node n, string msg) {
exists(int c |
c = count(n.toString()) and
c != 1 and
msg = "Node should have one toString but has " + c + "."
)
}
query predicate missingToString(string msg) {
exists(int c |
c = strictcount(Node n | not exists(n.toString())) and
msg = "Nodes without toString: " + c
)
}
query predicate parameterCallable(ParameterNode p, string msg) {
exists(DataFlowCallable c | p.isParameterOf(c, _) and c != p.getEnclosingCallable()) and
msg = "Callable mismatch for parameter."
}
query predicate localFlowIsLocal(Node n1, Node n2, string msg) {
simpleLocalFlowStep(n1, n2) and
n1.getEnclosingCallable() != n2.getEnclosingCallable() and
msg = "Local flow step does not preserve enclosing callable."
}
private DataFlowType typeRepr() { result = getErasedRepr(any(Node n).getTypeBound()) }
query predicate compatibleTypesReflexive(DataFlowType t, string msg) {
t = typeRepr() and
not compatibleTypes(t, t) and
msg = "Type compatibility predicate is not reflexive."
}
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
isUnreachableInCall(n, call) and
exists(DataFlowCallable c |
c = n.getEnclosingCallable() and
not viableCallable(call) = c
) and
msg = "Call context for isUnreachableInCall is inconsistent with call graph."
}
query predicate localCallNodes(DataFlowCall call, Node n, string msg) {
(
n = getAnOutNode(call, _) and
msg = "OutNode and call does not share enclosing callable."
or
n.(ArgumentNode).argumentOf(call, _) and
msg = "ArgumentNode and call does not share enclosing callable."
) and
n.getEnclosingCallable() != call.getEnclosingCallable()
}
query predicate postIsNotPre(PostUpdateNode n, string msg) {
n.getPreUpdateNode() = n and msg = "PostUpdateNode should not equal its pre-update node."
}
query predicate postHasUniquePre(PostUpdateNode n, string msg) {
exists(int c |
c = count(n.getPreUpdateNode()) and
c != 1 and
msg = "PostUpdateNode should have one pre-update node but has " + c + "."
)
}
query predicate uniquePostUpdate(Node n, string msg) {
1 < strictcount(PostUpdateNode post | post.getPreUpdateNode() = n) and
msg = "Node has multiple PostUpdateNodes."
}
query predicate postIsInSameCallable(PostUpdateNode n, string msg) {
n.getEnclosingCallable() != n.getPreUpdateNode().getEnclosingCallable() and
msg = "PostUpdateNode does not share callable with its pre-update node."
}
private predicate hasPost(Node n) { exists(PostUpdateNode post | post.getPreUpdateNode() = n) }
query predicate reverseRead(Node n, string msg) {
exists(Node n2 | readStep(n, _, n2) and hasPost(n2) and not hasPost(n)) and
msg = "Origin of readStep is missing a PostUpdateNode."
}
query predicate storeIsPostUpdate(Node n, string msg) {
storeStep(_, _, n) and
not n instanceof PostUpdateNode and
msg = "Store targets should be PostUpdateNodes."
}
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
not hasPost(n) and
not isImmutableOrUnobservable(n) and
msg = "ArgumentNode is missing PostUpdateNode."
}
}

View File

@@ -154,7 +154,7 @@ Type getErasedRepr(Type t) {
}
/** Gets a string representation of a type returned by `getErasedRepr`. */
string ppReprType(Type t) { result = t.toString() }
string ppReprType(Type t) { none() } // stub implementation
/**
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
@@ -202,3 +202,16 @@ class DataFlowCall extends CallInstruction {
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
int accessPathLimit() { result = 5 }
/**
* Holds if `n` does not require a `PostUpdateNode` as it either cannot be
* modified or its modification cannot be observed, for example if it is a
* freshly created object that is not saved in a variable.
*
* This predicate is only used for consistency checks.
*/
predicate isImmutableOrUnobservable(Node n) {
// The rules for whether an IR argument gets a post-update node are too
// complex to model here.
any()
}

View File

@@ -239,6 +239,17 @@ class DefinitionByReferenceNode extends InstructionNode {
Parameter getParameter() {
exists(CallInstruction ci | result = ci.getStaticCallTarget().getParameter(instr.getIndex()))
}
override string toString() {
// This string should be unique enough to be helpful but common enough to
// avoid storing too many different strings.
result =
instr.getPrimaryInstruction().(CallInstruction).getStaticCallTarget().getName() +
" output argument"
or
not exists(instr.getPrimaryInstruction().(CallInstruction).getStaticCallTarget()) and
result = "output argument"
}
}
/**
@@ -289,7 +300,7 @@ ExprNode exprNode(Expr e) { result.getExpr() = e }
* Gets the `Node` corresponding to `e`, if any. Here, `e` may be a
* `Conversion`.
*/
ExprNode convertedExprNode(Expr e) { result.getExpr() = e }
ExprNode convertedExprNode(Expr e) { result.getConvertedExpr() = e }
/**
* Gets the `Node` corresponding to the value of `p` at function entry.
@@ -323,6 +334,7 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
simpleInstructionLocalFlowStep(nodeFrom.asInstruction(), nodeTo.asInstruction())
}
cached
private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction iTo) {
iTo.(CopyInstruction).getSourceValue() = iFrom
or

View File

@@ -1,3 +1,13 @@
/**
* Provides an implementation of global (interprocedural) taint tracking.
* This file re-exports the local (intraprocedural) taint-tracking analysis
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
* exposed through the `Configuration` class. For some languages, this file
* exists in several identical copies, allowing queries to use multiple
* `Configuration` classes that depend on each other without introducing
* mutual recursion among those configurations.
*/
import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private

View File

@@ -1,3 +1,13 @@
/**
* Provides an implementation of global (interprocedural) taint tracking.
* This file re-exports the local (intraprocedural) taint-tracking analysis
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
* exposed through the `Configuration` class. For some languages, this file
* exists in several identical copies, allowing queries to use multiple
* `Configuration` classes that depend on each other without introducing
* mutual recursion among those configurations.
*/
import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private

View File

@@ -16,6 +16,8 @@ class IRConfiguration extends TIRConfiguration {
* Holds if IR should be created for function `func`. By default, holds for all functions.
*/
predicate shouldCreateIRForFunction(Language::Function func) { any() }
predicate shouldEvaluateDebugStringsForFunction(Language::Function func) { any() }
}
private newtype TIREscapeAnalysisConfiguration = MkIREscapeAnalysisConfiguration()

View File

@@ -70,7 +70,7 @@ private newtype TOpcode =
TVarArgsStart() or
TVarArgsEnd() or
TVarArg() or
TVarArgCopy() or
TNextVarArg() or
TCallSideEffect() or
TCallReadSideEffect() or
TIndirectReadSideEffect() or
@@ -629,20 +629,20 @@ module Opcode {
final override string toString() { result = "BuiltIn" }
}
class VarArgsStart extends BuiltInOperationOpcode, TVarArgsStart {
class VarArgsStart extends UnaryOpcode, TVarArgsStart {
final override string toString() { result = "VarArgsStart" }
}
class VarArgsEnd extends BuiltInOperationOpcode, TVarArgsEnd {
class VarArgsEnd extends UnaryOpcode, TVarArgsEnd {
final override string toString() { result = "VarArgsEnd" }
}
class VarArg extends BuiltInOperationOpcode, TVarArg {
class VarArg extends UnaryOpcode, TVarArg {
final override string toString() { result = "VarArg" }
}
class VarArgCopy extends BuiltInOperationOpcode, TVarArgCopy {
final override string toString() { result = "VarArgCopy" }
class NextVarArg extends UnaryOpcode, TNextVarArg {
final override string toString() { result = "NextVarArg" }
}
class CallSideEffect extends WriteSideEffectOpcode, EscapedWriteOpcode, MayWriteOpcode,

View File

@@ -27,6 +27,9 @@ class IRBlockBase extends TIRBlock {
* by debugging and printing code only.
*/
int getDisplayIndex() {
exists(IRConfiguration::IRConfiguration config |
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
) and
this =
rank[result + 1](IRBlock funcBlock |
funcBlock.getEnclosingFunction() = getEnclosingFunction()

View File

@@ -5,6 +5,7 @@ import IRTypeSanity // module is in IRType.qll
module InstructionSanity {
private import internal.InstructionImports as Imports
private import Imports::OperandTag
private import Imports::Overlap
private import internal.IRInternal
/**
@@ -272,4 +273,48 @@ module InstructionSanity {
func = switchInstr.getEnclosingIRFunction() and
funcText = Language::getIdentityString(func.getFunction())
}
/**
* Holds if `instr` is on the chain of chi/phi instructions for all aliased
* memory.
*/
private predicate isOnAliasedDefinitionChain(Instruction instr) {
instr instanceof AliasedDefinitionInstruction
or
isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal())
or
isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
}
private predicate shouldBeConflated(Instruction instr) {
isOnAliasedDefinitionChain(instr)
or
instr instanceof UnmodeledDefinitionInstruction
or
instr.getOpcode() instanceof Opcode::InitializeNonLocal
}
query predicate notMarkedAsConflated(Instruction instr) {
shouldBeConflated(instr) and
not instr.isResultConflated()
}
query predicate wronglyMarkedAsConflated(Instruction instr) {
instr.isResultConflated() and
not shouldBeConflated(instr)
}
query predicate invalidOverlap(
MemoryOperand useOperand, string message, IRFunction func, string funcText
) {
exists(Overlap overlap |
overlap = useOperand.getDefinitionOverlap() and
overlap instanceof MayPartiallyOverlap and
message =
"MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
overlap.toString() + "'." and
func = useOperand.getEnclosingIRFunction() and
funcText = Language::getIdentityString(func.getFunction())
)
}
}

View File

@@ -23,7 +23,8 @@ class IRVariable extends TIRVariable {
IRVariable() {
this = TIRUserVariable(_, _, func) or
this = TIRTempVariable(func, _, _, _) or
this = TIRStringLiteral(func, _, _, _)
this = TIRStringLiteral(func, _, _, _) or
this = TIRDynamicInitializationFlag(func, _, _)
}
string toString() { none() }
@@ -149,7 +150,8 @@ class IRGeneratedVariable extends IRVariable {
IRGeneratedVariable() {
this = TIRTempVariable(func, ast, _, type) or
this = TIRStringLiteral(func, ast, type, _)
this = TIRStringLiteral(func, ast, type, _) or
this = TIRDynamicInitializationFlag(func, ast, type)
}
final override Language::LanguageType getLanguageType() { result = type }
@@ -208,7 +210,17 @@ class IRReturnVariable extends IRTempVariable {
class IRThrowVariable extends IRTempVariable {
IRThrowVariable() { tag = ThrowTempVar() }
override string getBaseString() { result = "#throw" }
final override string getBaseString() { result = "#throw" }
}
/**
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
* function that accepts a variable number of arguments.
*/
class IREllipsisVariable extends IRTempVariable {
IREllipsisVariable() { tag = EllipsisTempVar() }
final override string toString() { result = "#ellipsis" }
}
/**
@@ -226,7 +238,30 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
}
override string getBaseString() { result = "#string" }
final override string getBaseString() { result = "#string" }
final Language::StringLiteral getLiteral() { result = literal }
}
/**
* A variable generated to track whether a specific non-stack variable has been initialized. This is
* used to model the runtime initialization of static local variables in C++, as well as static
* fields in C#.
*/
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
Language::Variable var;
IRDynamicInitializationFlag() {
this = TIRDynamicInitializationFlag(func, var, type) and ast = var
}
final override string toString() { result = var.toString() + "#init" }
final Language::Variable getVariable() { result = var }
final override string getUniqueId() {
result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString()
}
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
}

View File

@@ -15,6 +15,9 @@ private import Imports::OperandTag
* `File` and line number. Used for assigning register names when printing IR.
*/
private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File file, int line) {
exists(IRConfiguration::IRConfiguration config |
config.shouldEvaluateDebugStringsForFunction(irFunc.getFunction())
) and
exists(Language::Location location |
irFunc = result.getEnclosingIRFunction() and
location = result.getLocation() and
@@ -39,6 +42,12 @@ class Instruction extends Construction::TInstruction {
result = getResultString() + " = " + getOperationString() + " " + getOperandsString()
}
private predicate shouldGenerateDumpStrings() {
exists(IRConfiguration::IRConfiguration config |
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
)
}
/**
* Gets a string describing the operation of this instruction. This includes
* the opcode and the immediate value, if any. For example:
@@ -46,6 +55,7 @@ class Instruction extends Construction::TInstruction {
* VariableAddress[x]
*/
final string getOperationString() {
shouldGenerateDumpStrings() and
if exists(getImmediateString())
then result = getOperationPrefix() + getOpcode().toString() + "[" + getImmediateString() + "]"
else result = getOperationPrefix() + getOpcode().toString()
@@ -57,10 +67,12 @@ class Instruction extends Construction::TInstruction {
string getImmediateString() { none() }
private string getOperationPrefix() {
shouldGenerateDumpStrings() and
if this instanceof SideEffectInstruction then result = "^" else result = ""
}
private string getResultPrefix() {
shouldGenerateDumpStrings() and
if getResultIRType() instanceof IRVoidType
then result = "v"
else
@@ -74,6 +86,7 @@ class Instruction extends Construction::TInstruction {
* used by debugging and printing code only.
*/
int getDisplayIndexInBlock() {
shouldGenerateDumpStrings() and
exists(IRBlock block |
this = block.getInstruction(result)
or
@@ -87,6 +100,7 @@ class Instruction extends Construction::TInstruction {
}
private int getLineRank() {
shouldGenerateDumpStrings() and
this =
rank[result](Instruction instr |
instr =
@@ -105,6 +119,7 @@ class Instruction extends Construction::TInstruction {
* Example: `r1_1`
*/
string getResultId() {
shouldGenerateDumpStrings() and
result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank()
}
@@ -116,6 +131,7 @@ class Instruction extends Construction::TInstruction {
* Example: `r1_1(int*)`
*/
final string getResultString() {
shouldGenerateDumpStrings() and
result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")"
}
@@ -126,6 +142,7 @@ class Instruction extends Construction::TInstruction {
* Example: `func:r3_4, this:r3_5`
*/
string getOperandsString() {
shouldGenerateDumpStrings() and
result =
concat(Operand operand |
operand = getAnOperand()
@@ -321,6 +338,17 @@ class Instruction extends Construction::TInstruction {
Construction::hasModeledMemoryResult(this)
}
/**
* Holds if this is an instruction with a memory result that represents a
* conflation of more than one memory allocation.
*
* This happens in practice when dereferencing a pointer that cannot be
* tracked back to a single local allocation. Such memory is instead modeled
* as originating on the `AliasedDefinitionInstruction` at the entry of the
* function.
*/
final predicate isResultConflated() { Construction::hasConflatedMemoryResult(this) }
/**
* Gets the successor of this instruction along the control flow edge
* specified by `kind`.

View File

@@ -384,6 +384,8 @@ class PositionalArgumentOperand extends ArgumentOperand {
class SideEffectOperand extends TypedOperand {
override SideEffectOperandTag tag;
override string toString() { result = "SideEffect" }
}
/**

View File

@@ -18,19 +18,19 @@ class PrintIRConfiguration extends TPrintIRConfiguration {
predicate shouldPrintFunction(Language::Function func) { any() }
}
private predicate shouldPrintFunction(Language::Function func) {
exists(PrintIRConfiguration config | config.shouldPrintFunction(func))
}
/**
* Override of `IRConfiguration` to only create IR for the functions that are to be dumped.
* Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped.
*/
private class FilteredIRConfiguration extends IRConfiguration {
override predicate shouldCreateIRForFunction(Language::Function func) {
override predicate shouldEvaluateDebugStringsForFunction(Language::Function func) {
shouldPrintFunction(func)
}
}
private predicate shouldPrintFunction(Language::Function func) {
exists(PrintIRConfiguration config | config.shouldPrintFunction(func))
}
private string getAdditionalInstructionProperty(Instruction instr, string key) {
exists(IRPropertyProvider provider | result = provider.getInstructionProperty(instr, key))
}

View File

@@ -0,0 +1,17 @@
private import internal.ValueNumberingImports
private import ValueNumbering
/**
* Provides additional information about value numbering in IR dumps.
*/
class ValueNumberPropertyProvider extends IRPropertyProvider {
override string getInstructionProperty(Instruction instr, string key) {
exists(ValueNumber vn |
vn = valueNumber(instr) and
key = "valnum" and
if strictcount(vn.getAnInstruction()) > 1
then result = vn.getDebugString()
else result = "unique"
)
}
}

View File

@@ -1,21 +1,6 @@
private import internal.ValueNumberingInternal
private import internal.ValueNumberingImports
/**
* Provides additional information about value numbering in IR dumps.
*/
class ValueNumberPropertyProvider extends IRPropertyProvider {
override string getInstructionProperty(Instruction instr, string key) {
exists(ValueNumber vn |
vn = valueNumber(instr) and
key = "valnum" and
if strictcount(vn.getAnInstruction()) > 1
then result = vn.getDebugString()
else result = "unique"
)
}
}
/**
* The value number assigned to a particular set of instructions that produce equivalent results.
*/

View File

@@ -354,6 +354,7 @@ class AllAliasedMemory extends TAllAliasedMemory, MemoryLocation {
final override predicate isMayAccess() { isMayAccess = true }
}
/** A virtual variable that groups all escaped memory within a function. */
class AliasedVirtualVariable extends AllAliasedMemory, VirtualVariable {
AliasedVirtualVariable() { not isMayAccess() }
}
@@ -429,10 +430,18 @@ private Overlap getExtentOverlap(MemoryLocation def, MemoryLocation use) {
use instanceof EntireAllocationMemoryLocation and
result instanceof MustExactlyOverlap
or
// EntireAllocationMemoryLocation totally overlaps any location within the same virtual
// variable.
not use instanceof EntireAllocationMemoryLocation and
result instanceof MustTotallyOverlap
if def.getAllocation() = use.getAllocation()
then
// EntireAllocationMemoryLocation totally overlaps any location within
// the same allocation.
result instanceof MustTotallyOverlap
else (
// There is no overlap with a location that's known to belong to a
// different allocation, but all other locations may partially overlap.
not exists(use.getAllocation()) and
result instanceof MayPartiallyOverlap
)
)
or
exists(VariableMemoryLocation defVariableLocation |

View File

@@ -1,2 +1,3 @@
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
import SSAConstruction as Construction
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration

View File

@@ -3,3 +3,4 @@ import semmle.code.cpp.ir.implementation.IRType as IRType
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
import semmle.code.cpp.ir.implementation.Opcode as Opcode
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
import semmle.code.cpp.ir.internal.Overlap as Overlap

View File

@@ -65,6 +65,29 @@ private module Cached {
instruction instanceof ChiInstruction // Chis always have modeled results
}
cached
predicate hasConflatedMemoryResult(Instruction instruction) {
instruction instanceof UnmodeledDefinitionInstruction
or
instruction instanceof AliasedDefinitionInstruction
or
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
or
// Chi instructions track virtual variables, and therefore a chi instruction is
// conflated if it's associated with the aliased virtual variable.
exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) |
Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof
Alias::AliasedVirtualVariable
)
or
// Phi instructions track locations, and therefore a phi instruction is
// conflated if it's associated with a conflated location.
exists(Alias::MemoryLocation location |
instruction = Phi(_, location) and
not exists(location.getAllocation())
)
}
cached
Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) {
exists(OldInstruction oldInstruction, OldIR::RegisterOperand oldOperand |
@@ -84,14 +107,15 @@ private module Cached {
oldOperand instanceof OldIR::NonPhiMemoryOperand and
exists(
OldBlock useBlock, int useRank, Alias::MemoryLocation useLocation,
Alias::MemoryLocation defLocation, OldBlock defBlock, int defRank, int defOffset
Alias::MemoryLocation defLocation, OldBlock defBlock, int defRank, int defOffset,
Alias::MemoryLocation actualDefLocation
|
useLocation = Alias::getOperandMemoryLocation(oldOperand) and
hasUseAtRank(useLocation, useBlock, useRank, oldInstruction) and
definitionReachesUse(useLocation, defBlock, defRank, useBlock, useRank) and
hasDefinitionAtRank(useLocation, defLocation, defBlock, defRank, defOffset) and
instr = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, _) and
overlap = Alias::getOverlap(defLocation, useLocation)
instr = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
overlap = Alias::getOverlap(actualDefLocation, useLocation)
)
}

View File

@@ -10,6 +10,11 @@ newtype TIRVariable =
) {
Construction::hasTempVariable(func, ast, tag, type)
} or
TIRDynamicInitializationFlag(
Language::Function func, Language::Variable var, Language::LanguageType type
) {
Construction::hasDynamicInitializationFlag(func, var, type)
} or
TIRStringLiteral(
Language::Function func, Language::AST ast, Language::LanguageType type,
Language::StringLiteral literal

View File

@@ -27,6 +27,9 @@ class IRBlockBase extends TIRBlock {
* by debugging and printing code only.
*/
int getDisplayIndex() {
exists(IRConfiguration::IRConfiguration config |
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
) and
this =
rank[result + 1](IRBlock funcBlock |
funcBlock.getEnclosingFunction() = getEnclosingFunction()

View File

@@ -5,6 +5,7 @@ import IRTypeSanity // module is in IRType.qll
module InstructionSanity {
private import internal.InstructionImports as Imports
private import Imports::OperandTag
private import Imports::Overlap
private import internal.IRInternal
/**
@@ -272,4 +273,48 @@ module InstructionSanity {
func = switchInstr.getEnclosingIRFunction() and
funcText = Language::getIdentityString(func.getFunction())
}
/**
* Holds if `instr` is on the chain of chi/phi instructions for all aliased
* memory.
*/
private predicate isOnAliasedDefinitionChain(Instruction instr) {
instr instanceof AliasedDefinitionInstruction
or
isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal())
or
isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
}
private predicate shouldBeConflated(Instruction instr) {
isOnAliasedDefinitionChain(instr)
or
instr instanceof UnmodeledDefinitionInstruction
or
instr.getOpcode() instanceof Opcode::InitializeNonLocal
}
query predicate notMarkedAsConflated(Instruction instr) {
shouldBeConflated(instr) and
not instr.isResultConflated()
}
query predicate wronglyMarkedAsConflated(Instruction instr) {
instr.isResultConflated() and
not shouldBeConflated(instr)
}
query predicate invalidOverlap(
MemoryOperand useOperand, string message, IRFunction func, string funcText
) {
exists(Overlap overlap |
overlap = useOperand.getDefinitionOverlap() and
overlap instanceof MayPartiallyOverlap and
message =
"MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
overlap.toString() + "'." and
func = useOperand.getEnclosingIRFunction() and
funcText = Language::getIdentityString(func.getFunction())
)
}
}

View File

@@ -23,7 +23,8 @@ class IRVariable extends TIRVariable {
IRVariable() {
this = TIRUserVariable(_, _, func) or
this = TIRTempVariable(func, _, _, _) or
this = TIRStringLiteral(func, _, _, _)
this = TIRStringLiteral(func, _, _, _) or
this = TIRDynamicInitializationFlag(func, _, _)
}
string toString() { none() }
@@ -149,7 +150,8 @@ class IRGeneratedVariable extends IRVariable {
IRGeneratedVariable() {
this = TIRTempVariable(func, ast, _, type) or
this = TIRStringLiteral(func, ast, type, _)
this = TIRStringLiteral(func, ast, type, _) or
this = TIRDynamicInitializationFlag(func, ast, type)
}
final override Language::LanguageType getLanguageType() { result = type }
@@ -208,7 +210,17 @@ class IRReturnVariable extends IRTempVariable {
class IRThrowVariable extends IRTempVariable {
IRThrowVariable() { tag = ThrowTempVar() }
override string getBaseString() { result = "#throw" }
final override string getBaseString() { result = "#throw" }
}
/**
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
* function that accepts a variable number of arguments.
*/
class IREllipsisVariable extends IRTempVariable {
IREllipsisVariable() { tag = EllipsisTempVar() }
final override string toString() { result = "#ellipsis" }
}
/**
@@ -226,7 +238,30 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
}
override string getBaseString() { result = "#string" }
final override string getBaseString() { result = "#string" }
final Language::StringLiteral getLiteral() { result = literal }
}
/**
* A variable generated to track whether a specific non-stack variable has been initialized. This is
* used to model the runtime initialization of static local variables in C++, as well as static
* fields in C#.
*/
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
Language::Variable var;
IRDynamicInitializationFlag() {
this = TIRDynamicInitializationFlag(func, var, type) and ast = var
}
final override string toString() { result = var.toString() + "#init" }
final Language::Variable getVariable() { result = var }
final override string getUniqueId() {
result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString()
}
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
}

View File

@@ -15,6 +15,9 @@ private import Imports::OperandTag
* `File` and line number. Used for assigning register names when printing IR.
*/
private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File file, int line) {
exists(IRConfiguration::IRConfiguration config |
config.shouldEvaluateDebugStringsForFunction(irFunc.getFunction())
) and
exists(Language::Location location |
irFunc = result.getEnclosingIRFunction() and
location = result.getLocation() and
@@ -39,6 +42,12 @@ class Instruction extends Construction::TInstruction {
result = getResultString() + " = " + getOperationString() + " " + getOperandsString()
}
private predicate shouldGenerateDumpStrings() {
exists(IRConfiguration::IRConfiguration config |
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
)
}
/**
* Gets a string describing the operation of this instruction. This includes
* the opcode and the immediate value, if any. For example:
@@ -46,6 +55,7 @@ class Instruction extends Construction::TInstruction {
* VariableAddress[x]
*/
final string getOperationString() {
shouldGenerateDumpStrings() and
if exists(getImmediateString())
then result = getOperationPrefix() + getOpcode().toString() + "[" + getImmediateString() + "]"
else result = getOperationPrefix() + getOpcode().toString()
@@ -57,10 +67,12 @@ class Instruction extends Construction::TInstruction {
string getImmediateString() { none() }
private string getOperationPrefix() {
shouldGenerateDumpStrings() and
if this instanceof SideEffectInstruction then result = "^" else result = ""
}
private string getResultPrefix() {
shouldGenerateDumpStrings() and
if getResultIRType() instanceof IRVoidType
then result = "v"
else
@@ -74,6 +86,7 @@ class Instruction extends Construction::TInstruction {
* used by debugging and printing code only.
*/
int getDisplayIndexInBlock() {
shouldGenerateDumpStrings() and
exists(IRBlock block |
this = block.getInstruction(result)
or
@@ -87,6 +100,7 @@ class Instruction extends Construction::TInstruction {
}
private int getLineRank() {
shouldGenerateDumpStrings() and
this =
rank[result](Instruction instr |
instr =
@@ -105,6 +119,7 @@ class Instruction extends Construction::TInstruction {
* Example: `r1_1`
*/
string getResultId() {
shouldGenerateDumpStrings() and
result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank()
}
@@ -116,6 +131,7 @@ class Instruction extends Construction::TInstruction {
* Example: `r1_1(int*)`
*/
final string getResultString() {
shouldGenerateDumpStrings() and
result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")"
}
@@ -126,6 +142,7 @@ class Instruction extends Construction::TInstruction {
* Example: `func:r3_4, this:r3_5`
*/
string getOperandsString() {
shouldGenerateDumpStrings() and
result =
concat(Operand operand |
operand = getAnOperand()
@@ -321,6 +338,17 @@ class Instruction extends Construction::TInstruction {
Construction::hasModeledMemoryResult(this)
}
/**
* Holds if this is an instruction with a memory result that represents a
* conflation of more than one memory allocation.
*
* This happens in practice when dereferencing a pointer that cannot be
* tracked back to a single local allocation. Such memory is instead modeled
* as originating on the `AliasedDefinitionInstruction` at the entry of the
* function.
*/
final predicate isResultConflated() { Construction::hasConflatedMemoryResult(this) }
/**
* Gets the successor of this instruction along the control flow edge
* specified by `kind`.

View File

@@ -384,6 +384,8 @@ class PositionalArgumentOperand extends ArgumentOperand {
class SideEffectOperand extends TypedOperand {
override SideEffectOperandTag tag;
override string toString() { result = "SideEffect" }
}
/**

View File

@@ -18,19 +18,19 @@ class PrintIRConfiguration extends TPrintIRConfiguration {
predicate shouldPrintFunction(Language::Function func) { any() }
}
private predicate shouldPrintFunction(Language::Function func) {
exists(PrintIRConfiguration config | config.shouldPrintFunction(func))
}
/**
* Override of `IRConfiguration` to only create IR for the functions that are to be dumped.
* Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped.
*/
private class FilteredIRConfiguration extends IRConfiguration {
override predicate shouldCreateIRForFunction(Language::Function func) {
override predicate shouldEvaluateDebugStringsForFunction(Language::Function func) {
shouldPrintFunction(func)
}
}
private predicate shouldPrintFunction(Language::Function func) {
exists(PrintIRConfiguration config | config.shouldPrintFunction(func))
}
private string getAdditionalInstructionProperty(Instruction instr, string key) {
exists(IRPropertyProvider provider | result = provider.getInstructionProperty(instr, key))
}

View File

@@ -0,0 +1,17 @@
private import internal.ValueNumberingImports
private import ValueNumbering
/**
* Provides additional information about value numbering in IR dumps.
*/
class ValueNumberPropertyProvider extends IRPropertyProvider {
override string getInstructionProperty(Instruction instr, string key) {
exists(ValueNumber vn |
vn = valueNumber(instr) and
key = "valnum" and
if strictcount(vn.getAnInstruction()) > 1
then result = vn.getDebugString()
else result = "unique"
)
}
}

View File

@@ -1,21 +1,6 @@
private import internal.ValueNumberingInternal
private import internal.ValueNumberingImports
/**
* Provides additional information about value numbering in IR dumps.
*/
class ValueNumberPropertyProvider extends IRPropertyProvider {
override string getInstructionProperty(Instruction instr, string key) {
exists(ValueNumber vn |
vn = valueNumber(instr) and
key = "valnum" and
if strictcount(vn.getAnInstruction()) > 1
then result = vn.getDebugString()
else result = "unique"
)
}
}
/**
* The value number assigned to a particular set of instructions that produce equivalent results.
*/

View File

@@ -51,9 +51,25 @@ private module Cached {
getTypeForPRValue(literal.getType()) = type
}
cached
predicate hasDynamicInitializationFlag(Function func, StaticLocalVariable var, CppType type) {
var.getFunction() = func and
var.hasDynamicInitialization() and
type = getBoolType()
}
cached
predicate hasModeledMemoryResult(Instruction instruction) { none() }
cached
predicate hasConflatedMemoryResult(Instruction instruction) {
instruction instanceof UnmodeledDefinitionInstruction
or
instruction instanceof AliasedDefinitionInstruction
or
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
}
cached
Expr getInstructionConvertedResultExpression(Instruction instruction) {
exists(TranslatedExpr translatedExpr |

View File

@@ -1,2 +1,3 @@
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
import IRConstruction as Construction
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration

View File

@@ -3,3 +3,4 @@ import semmle.code.cpp.ir.implementation.IRType as IRType
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
import semmle.code.cpp.ir.implementation.Opcode as Opcode
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
import semmle.code.cpp.ir.internal.Overlap as Overlap

View File

@@ -8,6 +8,11 @@ newtype TInstructionTag =
InitializerStoreTag() or
InitializerIndirectAddressTag() or
InitializerIndirectStoreTag() or
DynamicInitializationFlagAddressTag() or
DynamicInitializationFlagLoadTag() or
DynamicInitializationConditionalBranchTag() or
DynamicInitializationFlagConstantTag() or
DynamicInitializationFlagStoreTag() or
ZeroPadStringConstantTag() or
ZeroPadStringElementIndexTag() or
ZeroPadStringElementAddressTag() or
@@ -59,6 +64,13 @@ newtype TInstructionTag =
InitializerElementAddressTag() or
InitializerElementDefaultValueTag() or
InitializerElementDefaultValueStoreTag() or
VarArgsStartEllipsisAddressTag() or
VarArgsStartTag() or
VarArgsVAListLoadTag() or
VarArgsArgAddressTag() or
VarArgsArgLoadTag() or
VarArgsMoveNextTag() or
VarArgsVAListStoreTag() or
AsmTag() or
AsmInputTag(int elementIndex) { exists(AsmStmt asm | exists(asm.getChild(elementIndex))) }
@@ -183,7 +195,31 @@ string getInstructionTagId(TInstructionTag tag) {
or
tag = InitializerElementDefaultValueStoreTag() and result = "InitElemDefValStore"
or
tag = VarArgsStartEllipsisAddressTag() and result = "VarArgsStartEllipsisAddr"
or
tag = VarArgsStartTag() and result = "VarArgsStart"
or
tag = VarArgsVAListLoadTag() and result = "VarArgsVAListLoad"
or
tag = VarArgsArgAddressTag() and result = "VarArgsArgAddr"
or
tag = VarArgsArgLoadTag() and result = "VaArgsArgLoad"
or
tag = VarArgsMoveNextTag() and result = "VarArgsMoveNext"
or
tag = VarArgsVAListStoreTag() and result = "VarArgsVAListStore"
or
tag = AsmTag() and result = "Asm"
or
exists(int index | tag = AsmInputTag(index) and result = "AsmInputTag(" + index + ")")
or
tag = DynamicInitializationFlagAddressTag() and result = "DynInitFlagAddr"
or
tag = DynamicInitializationFlagLoadTag() and result = "DynInitFlagLoad"
or
tag = DynamicInitializationConditionalBranchTag() and result = "DynInitCondBranch"
or
tag = DynamicInitializationFlagConstantTag() and result = "DynInitFlagConst"
or
tag = DynamicInitializationFlagStoreTag() and result = "DynInitFlagStore"
}

View File

@@ -206,7 +206,44 @@ abstract class TranslatedCall extends TranslatedExpr {
predicate hasPreciseSideEffect() { exists(getSideEffects()) }
TranslatedSideEffects getSideEffects() { result.getCall() = expr }
final TranslatedSideEffects getSideEffects() { result.getExpr() = expr }
}
abstract class TranslatedSideEffects extends TranslatedElement {
abstract Expr getExpr();
final override Locatable getAST() { result = getExpr() }
final override Function getFunction() { result = getExpr().getEnclosingFunction() }
override TranslatedElement getChild(int i) {
result =
rank[i + 1](TranslatedSideEffect tse, int isWrite, int index |
(
tse.getCall() = getExpr() and
tse.getArgumentIndex() = index and
if tse.isWrite() then isWrite = 1 else isWrite = 0
)
|
tse order by isWrite, index
)
}
final override Instruction getChildSuccessor(TranslatedElement te) {
exists(int i |
getChild(i) = te and
if exists(getChild(i + 1))
then result = getChild(i + 1).getFirstInstruction()
else result = getParent().getChildSuccessor(this)
)
}
/**
* Gets the `TranslatedFunction` containing this expression.
*/
final TranslatedFunction getEnclosingFunction() {
result = getTranslatedFunction(getExpr().getEnclosingFunction())
}
}
/**
@@ -308,56 +345,27 @@ class TranslatedStructorCall extends TranslatedFunctionCall {
override predicate hasQualifier() { any() }
}
class TranslatedSideEffects extends TranslatedElement, TTranslatedSideEffects {
Call expr;
class TranslatedAllocationSideEffects extends TranslatedSideEffects,
TTranslatedAllocationSideEffects {
AllocationExpr expr;
TranslatedSideEffects() { this = TTranslatedSideEffects(expr) }
TranslatedAllocationSideEffects() { this = TTranslatedAllocationSideEffects(expr) }
override string toString() { result = "(side effects for " + expr.toString() + ")" }
final override AllocationExpr getExpr() { result = expr }
override Locatable getAST() { result = expr }
override string toString() { result = "(allocation side effects for " + expr.toString() + ")" }
Call getCall() { result = expr }
override TranslatedElement getChild(int i) {
result =
rank[i + 1](TranslatedSideEffect tse, int isWrite, int index |
(
tse.getCall() = getCall() and
tse.getArgumentIndex() = index and
if tse.isWrite() then isWrite = 1 else isWrite = 0
)
|
tse order by isWrite, index
)
}
override Instruction getChildSuccessor(TranslatedElement te) {
exists(int i |
getChild(i) = te and
if exists(getChild(i + 1))
then result = getChild(i + 1).getFirstInstruction()
else result = getParent().getChildSuccessor(this)
)
}
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) {
expr.getTarget() instanceof AllocationFunction and
opcode instanceof Opcode::InitializeDynamicAllocation and
tag = OnlyInstructionTag() and
type = getUnknownType()
}
override Instruction getFirstInstruction() {
if expr.getTarget() instanceof AllocationFunction
then result = getInstruction(OnlyInstructionTag())
else result = getChild(0).getFirstInstruction()
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = OnlyInstructionTag() and
kind = gotoEdge() and
expr.getTarget() instanceof AllocationFunction and
if exists(getChild(0))
then result = getChild(0).getFirstInstruction()
else result = getParent().getChildSuccessor(this)
@@ -371,23 +379,34 @@ class TranslatedSideEffects extends TranslatedElement, TTranslatedSideEffects {
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
tag = OnlyInstructionTag() and
result = getTranslatedExpr(expr).getInstruction(CallTag())
if expr instanceof NewOrNewArrayExpr
then result = getTranslatedAllocatorCall(expr).getInstruction(CallTag())
else result = getTranslatedExpr(expr).getInstruction(CallTag())
}
/**
* Gets the `TranslatedFunction` containing this expression.
*/
final TranslatedFunction getEnclosingFunction() {
result = getTranslatedFunction(expr.getEnclosingFunction())
}
/**
* Gets the `Function` containing this expression.
*/
override Function getFunction() { result = expr.getEnclosingFunction() }
}
class TranslatedStructorCallSideEffects extends TranslatedSideEffects {
class TranslatedCallSideEffects extends TranslatedSideEffects, TTranslatedCallSideEffects {
Call expr;
TranslatedCallSideEffects() { this = TTranslatedCallSideEffects(expr) }
override string toString() { result = "(side effects for " + expr.toString() + ")" }
override Call getExpr() { result = expr }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) { none() }
override Instruction getFirstInstruction() { result = getChild(0).getFirstInstruction() }
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
tag = OnlyInstructionTag() and
result = getTranslatedExpr(expr).getInstruction(CallTag())
}
}
class TranslatedStructorCallSideEffects extends TranslatedCallSideEffects {
TranslatedStructorCallSideEffects() { getParent().(TranslatedStructorCall).hasQualifier() }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType t) {

View File

@@ -6,6 +6,7 @@ private import semmle.code.cpp.ir.internal.IRUtilities
private import InstructionTag
private import TranslatedElement
private import TranslatedExpr
private import TranslatedFunction
private import TranslatedInitialization
/**
@@ -66,17 +67,172 @@ abstract class TranslatedLocalVariableDeclaration extends TranslatedVariableInit
}
/**
* Represents the IR translation of a local variable declaration within a declaration statement.
* The IR translation of a local variable declaration within a declaration statement.
*/
class TranslatedVariableDeclarationEntry extends TranslatedLocalVariableDeclaration,
class TranslatedAutoVariableDeclarationEntry extends TranslatedLocalVariableDeclaration,
TranslatedDeclarationEntry {
LocalVariable var;
StackVariable var;
TranslatedVariableDeclarationEntry() { var = entry.getDeclaration() }
TranslatedAutoVariableDeclarationEntry() { var = entry.getDeclaration() }
override LocalVariable getVariable() { result = var }
}
/**
* The IR translation of the declaration of a static local variable.
* This element generates the logic that determines whether or not the variable has already been
* initialized, and if not, invokes the initializer and sets the dynamic initialization flag for the
* variable. The actual initialization code is handled in
* `TranslatedStaticLocalVariableInitialization`, which is a child of this element.
*
* The generated code to do the initialization only once is:
* ```
* Block 1
* r1225_1(glval<bool>) = VariableAddress[c#init] :
* r1225_2(bool) = Load : &:r1225_1, ~mu1222_4
* v1225_3(void) = ConditionalBranch : r1225_2
* False -> Block 2
* True -> Block 3
*
* Block 2
* r1225_4(glval<int>) = VariableAddress[c] :
* <actual initialization of `c`>
* r1225_8(bool) = Constant[1] :
* mu1225_9(bool) = Store : &:r1225_1, r1225_8
* Goto -> Block 3
*
* Block 3
* ```
*
* Note that the flag variable, `c#init`, is assumed to be zero-initialized at program startup, just
* like any other variable with static storage duration.
*/
class TranslatedStaticLocalVariableDeclarationEntry extends TranslatedDeclarationEntry {
StaticLocalVariable var;
TranslatedStaticLocalVariableDeclarationEntry() { var = entry.getDeclaration() }
final override TranslatedElement getChild(int id) { id = 0 and result = getInitialization() }
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) {
tag = DynamicInitializationFlagAddressTag() and
opcode instanceof Opcode::VariableAddress and
type = getBoolGLValueType()
or
tag = DynamicInitializationFlagLoadTag() and
opcode instanceof Opcode::Load and
type = getBoolType()
or
tag = DynamicInitializationConditionalBranchTag() and
opcode instanceof Opcode::ConditionalBranch and
type = getVoidType()
or
tag = DynamicInitializationFlagConstantTag() and
opcode instanceof Opcode::Constant and
type = getBoolType()
or
tag = DynamicInitializationFlagStoreTag() and
opcode instanceof Opcode::Store and
type = getBoolType()
}
final override Instruction getFirstInstruction() {
result = getInstruction(DynamicInitializationFlagAddressTag())
}
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = DynamicInitializationFlagAddressTag() and
kind instanceof GotoEdge and
result = getInstruction(DynamicInitializationFlagLoadTag())
or
tag = DynamicInitializationFlagLoadTag() and
kind instanceof GotoEdge and
result = getInstruction(DynamicInitializationConditionalBranchTag())
or
tag = DynamicInitializationConditionalBranchTag() and
(
kind instanceof TrueEdge and
result = getParent().getChildSuccessor(this)
or
kind instanceof FalseEdge and
result = getInitialization().getFirstInstruction()
)
or
tag = DynamicInitializationFlagConstantTag() and
kind instanceof GotoEdge and
result = getInstruction(DynamicInitializationFlagStoreTag())
or
tag = DynamicInitializationFlagStoreTag() and
kind instanceof GotoEdge and
result = getParent().getChildSuccessor(this)
}
final override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitialization() and
result = getInstruction(DynamicInitializationFlagConstantTag())
}
final override IRDynamicInitializationFlag getInstructionVariable(InstructionTag tag) {
tag = DynamicInitializationFlagAddressTag() and
result.getVariable() = var
}
final override string getInstructionConstantValue(InstructionTag tag) {
tag = DynamicInitializationFlagConstantTag() and result = "1"
}
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
tag = DynamicInitializationFlagLoadTag() and
(
operandTag instanceof AddressOperandTag and
result = getInstruction(DynamicInitializationFlagAddressTag())
or
operandTag instanceof LoadOperandTag and
result = getTranslatedFunction(var.getFunction()).getUnmodeledDefinitionInstruction()
)
or
tag = DynamicInitializationConditionalBranchTag() and
operandTag instanceof ConditionOperandTag and
result = getInstruction(DynamicInitializationFlagLoadTag())
or
tag = DynamicInitializationFlagStoreTag() and
(
operandTag instanceof AddressOperandTag and
result = getInstruction(DynamicInitializationFlagAddressTag())
or
operandTag instanceof StoreValueOperandTag and
result = getInstruction(DynamicInitializationFlagConstantTag())
)
}
private TranslatedStaticLocalVariableInitialization getInitialization() {
result.getVariable() = var
}
}
/**
* The initialization of a static local variable. This element will only exist for a static variable
* with a dynamic initializer.
*/
class TranslatedStaticLocalVariableInitialization extends TranslatedElement,
TranslatedLocalVariableDeclaration, TTranslatedStaticLocalVariableInitialization {
VariableDeclarationEntry entry;
StaticLocalVariable var;
TranslatedStaticLocalVariableInitialization() {
this = TTranslatedStaticLocalVariableInitialization(entry) and
var = entry.getDeclaration()
}
final override string toString() { result = "init: " + entry.toString() }
final override Locatable getAST() { result = entry }
final override LocalVariable getVariable() { result = var }
final override Function getFunction() { result = var.getFunction() }
}
/**
* Gets the `TranslatedRangeBasedForVariableDeclaration` that represents the declaration of
* `var`.

View File

@@ -54,7 +54,7 @@ private predicate ignoreExprAndDescendants(Expr expr) {
// Otherwise the initializer does not run in function scope.
exists(Initializer init, StaticStorageDurationVariable var |
init = var.getInitializer() and
var.hasConstantInitialization() and
not var.hasDynamicInitialization() and
expr = init.getExpr().getFullyConverted()
)
or
@@ -83,6 +83,10 @@ private predicate ignoreExprAndDescendants(Expr expr) {
exists(DeleteExpr deleteExpr | deleteExpr.getAllocatorCall() = expr)
or
exists(DeleteArrayExpr deleteArrayExpr | deleteArrayExpr.getAllocatorCall() = expr)
or
exists(BuiltInVarArgsStart vaStartExpr |
vaStartExpr.getLastNamedParameter().getFullyConverted() = expr
)
}
/**
@@ -256,6 +260,26 @@ predicate hasTranslatedLoad(Expr expr) {
not ignoreLoad(expr)
}
/**
* Holds if the specified `DeclarationEntry` needs an IR translation. An IR translation is only
* necessary for automatic local variables, or for static local variables with dynamic
* initialization.
*/
private predicate translateDeclarationEntry(DeclarationEntry entry) {
exists(DeclStmt declStmt, LocalVariable var |
translateStmt(declStmt) and
declStmt.getADeclarationEntry() = entry and
// Only declarations of local variables need to be translated to IR.
var = entry.getDeclaration() and
(
not var.isStatic()
or
// Ignore static variables unless they have a dynamic initializer.
var.(StaticLocalVariable).hasDynamicInitialization()
)
)
}
newtype TTranslatedElement =
// An expression that is not being consumed as a condition
TTranslatedValueExpr(Expr expr) {
@@ -382,6 +406,7 @@ newtype TTranslatedElement =
translateFunction(func)
)
} or
TTranslatedEllipsisParameter(Function func) { translateFunction(func) and func.isVarargs() } or
TTranslatedReadEffects(Function func) { translateFunction(func) } or
// The read side effects in a function's return block
TTranslatedReadEffect(Parameter param) {
@@ -393,13 +418,12 @@ newtype TTranslatedElement =
)
} or
// A local declaration
TTranslatedDeclarationEntry(DeclarationEntry entry) {
exists(DeclStmt declStmt |
translateStmt(declStmt) and
declStmt.getADeclarationEntry() = entry and
// Only declarations of local variables need to be translated to IR.
entry.getDeclaration() instanceof LocalVariable
)
TTranslatedDeclarationEntry(DeclarationEntry entry) { translateDeclarationEntry(entry) } or
// The dynamic initialization of a static local variable. This is a separate object from the
// declaration entry.
TTranslatedStaticLocalVariableInitialization(DeclarationEntry entry) {
translateDeclarationEntry(entry) and
entry.getDeclaration() instanceof StaticLocalVariable
} or
// A compiler-generated variable to implement a range-based for loop. These don't have a
// `DeclarationEntry` in the database, so we have to go by the `Variable` itself.
@@ -418,11 +442,22 @@ newtype TTranslatedElement =
// The declaration/initialization part of a `ConditionDeclExpr`
TTranslatedConditionDecl(ConditionDeclExpr expr) { not ignoreExpr(expr) } or
// The side effects of a `Call`
TTranslatedSideEffects(Call expr) {
exists(TTranslatedArgumentSideEffect(expr, _, _, _)) or
expr instanceof ConstructorCall or
expr.getTarget() instanceof AllocationFunction
} or // A precise side effect of an argument to a `Call`
TTranslatedCallSideEffects(Call expr) {
// Exclude allocations such as `malloc` (which happen to also be function calls).
// Both `TranslatedCallSideEffects` and `TranslatedAllocationSideEffects` generate
// the same side effects for its children as they both extend the `TranslatedSideEffects`
// class.
// Note: We can separate allocation side effects and call side effects into two
// translated elements as no call can be both a `ConstructorCall` and an `AllocationExpr`.
not expr instanceof AllocationExpr and
(
exists(TTranslatedArgumentSideEffect(expr, _, _, _)) or
expr instanceof ConstructorCall
)
} or
// The side effects of an allocation, i.e. `new`, `new[]` or `malloc`
TTranslatedAllocationSideEffects(AllocationExpr expr) or
// A precise side effect of an argument to a `Call`
TTranslatedArgumentSideEffect(Call call, Expr expr, int n, boolean isWrite) {
(
expr = call.getArgument(n).getFullyConverted()

View File

@@ -1649,6 +1649,11 @@ class TranslatedAllocatorCall extends TTranslatedAllocatorCall, TranslatedDirect
final override int getNumberOfArguments() {
result = expr.getAllocatorCall().getNumberOfArguments()
or
// Make sure there's a result even when there is no allocator, as otherwise
// TranslatedCall::getChild() will not return the side effects for this call.
not exists(expr.getAllocatorCall()) and
result = 0
}
final override TranslatedExpr getArgument(int index) {
@@ -2020,7 +2025,12 @@ class TranslatedBuiltInOperation extends TranslatedNonConstantExpr {
override BuiltInOperation expr;
TranslatedBuiltInOperation() {
not expr instanceof BuiltInOperationBuiltInAddressOf // Handled specially
// The following expressions are handled specially.
not expr instanceof BuiltInOperationBuiltInAddressOf and
not expr instanceof BuiltInVarArgsStart and
not expr instanceof BuiltInVarArg and
not expr instanceof BuiltInVarArgsEnd and
not expr instanceof BuiltInVarArgCopy
}
final override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) }
@@ -2075,39 +2085,318 @@ class TranslatedBuiltInOperation extends TranslatedNonConstantExpr {
}
/**
* The IR translation of a `BuiltInVarArgsStart` expression.
* Holds if the expression `expr` is one of the `va_list` operands to a `va_*` macro.
*/
class TranslatedVarArgsStart extends TranslatedBuiltInOperation {
override BuiltInVarArgsStart expr;
final override Opcode getOpcode() { result instanceof Opcode::VarArgsStart }
private predicate isVAListExpr(Expr expr) {
exists(VarArgsExpr parent, Expr originalExpr |
(
originalExpr = parent.(BuiltInVarArgsStart).getVAList()
or
originalExpr = parent.(BuiltInVarArgsEnd).getVAList()
or
originalExpr = parent.(BuiltInVarArg).getVAList()
or
originalExpr = parent.(BuiltInVarArgCopy).getSourceVAList()
or
originalExpr = parent.(BuiltInVarArgCopy).getDestinationVAList()
) and
expr = originalExpr.getFullyConverted()
)
}
/**
* The IR translation of a `BuiltInVarArgsEnd` expression.
* Gets the type of the `va_list` being accessed by `expr`, where `expr` is a `va_list` operand of a
* `va_*` macro.
*
* In the Unix ABI, `va_list` is declared as `typedef struct __va_list_tag va_list[1];`. When used
* as the type of a local variable, this gets an implicit array-to-pointer conversion, so that the
* actual argument to the `va_*` macro is a prvalue of type `__va_list_tag*`. When used as the type
* of a function parameter, the parameter's type decays to `__va_list_tag*`, so that the argument
* to the `va_*` macro is still a prvalue of type `__va_list_tag*`, with no implicit conversion
* necessary. In either case, we treat `__va_list_tag` as the representative type of the `va_list`.
*
* In the Windows ABI, `va_list` is declared as a pointer type (usually `char*`). Whether used as
* the type of a local variable or of a parameter, this means that the argument to the `va_*` macro
* is always an _lvalue_ of type `char*`. We treat `char*` as the representative type of the
* `va_list`.
*/
class TranslatedVarArgsEnd extends TranslatedBuiltInOperation {
override BuiltInVarArgsEnd expr;
private Type getVAListType(Expr expr) {
isVAListExpr(expr) and
if expr.isPRValueCategory()
then
// In the Unix ABI, this will be a prvalue of type `__va_list_tag*`. We want the `__va_list_tag`
// type.
result = expr.getType().getUnderlyingType().(PointerType).getBaseType()
else
// In the Windows ABI, this will be an lvalue of some pointer type. We want that pointer type.
result = expr.getType()
}
final override Opcode getOpcode() { result instanceof Opcode::VarArgsEnd }
/**
* The IR translation of a `BuiltInVarArgsStart` expression.
*/
class TranslatedVarArgsStart extends TranslatedNonConstantExpr {
override BuiltInVarArgsStart expr;
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = VarArgsStartEllipsisAddressTag() and
opcode instanceof Opcode::VariableAddress and
resultType = getEllipsisVariableGLValueType()
or
tag = VarArgsStartTag() and
opcode instanceof Opcode::VarArgsStart and
resultType = getTypeForPRValue(getVAListType(expr.getVAList().getFullyConverted()))
or
tag = VarArgsVAListStoreTag() and
opcode instanceof Opcode::Store and
resultType = getTypeForPRValue(getVAListType(expr.getVAList().getFullyConverted()))
}
final override Instruction getFirstInstruction() {
result = getInstruction(VarArgsStartEllipsisAddressTag())
}
final override Instruction getResult() { none() }
final override TranslatedElement getChild(int id) { id = 0 and result = getVAList() }
private TranslatedExpr getVAList() {
result = getTranslatedExpr(expr.getVAList().getFullyConverted())
}
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = VarArgsStartEllipsisAddressTag() and
kind instanceof GotoEdge and
result = getInstruction(VarArgsStartTag())
or
tag = VarArgsStartTag() and
kind instanceof GotoEdge and
result = getVAList().getFirstInstruction()
or
tag = VarArgsVAListStoreTag() and
kind instanceof GotoEdge and
result = getParent().getChildSuccessor(this)
}
final override Instruction getChildSuccessor(TranslatedElement child) {
child = getVAList() and
result = getInstruction(VarArgsVAListStoreTag())
}
final override IRVariable getInstructionVariable(InstructionTag tag) {
tag = VarArgsStartEllipsisAddressTag() and
result = getEnclosingFunction().getEllipsisVariable()
}
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
tag = VarArgsStartTag() and
operandTag instanceof UnaryOperandTag and
result = getInstruction(VarArgsStartEllipsisAddressTag())
or
tag = VarArgsVAListStoreTag() and
(
operandTag instanceof AddressOperandTag and result = getVAList().getResult()
or
operandTag instanceof StoreValueOperandTag and result = getInstruction(VarArgsStartTag())
)
}
}
/**
* The IR translation of a `BuiltInVarArg` expression.
*/
class TranslatedVarArg extends TranslatedBuiltInOperation {
class TranslatedVarArg extends TranslatedNonConstantExpr {
override BuiltInVarArg expr;
final override Opcode getOpcode() { result instanceof Opcode::VarArg }
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = VarArgsVAListLoadTag() and
opcode instanceof Opcode::Load and
resultType = getTypeForPRValue(getVAListType(expr.getVAList().getFullyConverted()))
or
tag = VarArgsArgAddressTag() and
opcode instanceof Opcode::VarArg and
resultType = getResultType()
or
tag = VarArgsMoveNextTag() and
opcode instanceof Opcode::NextVarArg and
resultType = getTypeForPRValue(getVAListType(expr.getVAList().getFullyConverted()))
or
tag = VarArgsVAListStoreTag() and
opcode instanceof Opcode::Store and
resultType = getTypeForPRValue(getVAListType(expr.getVAList().getFullyConverted()))
}
final override Instruction getFirstInstruction() { result = getVAList().getFirstInstruction() }
final override Instruction getResult() { result = getInstruction(VarArgsArgAddressTag()) }
final override TranslatedElement getChild(int id) { id = 0 and result = getVAList() }
private TranslatedExpr getVAList() {
result = getTranslatedExpr(expr.getVAList().getFullyConverted())
}
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = VarArgsVAListLoadTag() and
kind instanceof GotoEdge and
result = getInstruction(VarArgsArgAddressTag())
or
tag = VarArgsArgAddressTag() and
kind instanceof GotoEdge and
result = getInstruction(VarArgsMoveNextTag())
or
tag = VarArgsMoveNextTag() and
kind instanceof GotoEdge and
result = getInstruction(VarArgsVAListStoreTag())
or
tag = VarArgsVAListStoreTag() and
kind instanceof GotoEdge and
result = getParent().getChildSuccessor(this)
}
final override Instruction getChildSuccessor(TranslatedElement child) {
child = getVAList() and
result = getInstruction(VarArgsVAListLoadTag())
}
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
tag = VarArgsVAListLoadTag() and
(
operandTag instanceof AddressOperandTag and
result = getVAList().getResult()
or
operandTag instanceof LoadOperandTag and
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
)
or
tag = VarArgsArgAddressTag() and
operandTag instanceof UnaryOperandTag and
result = getInstruction(VarArgsVAListLoadTag())
or
tag = VarArgsMoveNextTag() and
operandTag instanceof UnaryOperandTag and
result = getInstruction(VarArgsVAListLoadTag())
or
tag = VarArgsVAListStoreTag() and
(
operandTag instanceof AddressOperandTag and result = getVAList().getResult()
or
operandTag instanceof StoreValueOperandTag and result = getInstruction(VarArgsMoveNextTag())
)
}
}
/**
* The IR translation of a `BuiltInVarArgsEnd` expression.
*/
class TranslatedVarArgsEnd extends TranslatedNonConstantExpr {
override BuiltInVarArgsEnd expr;
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
opcode instanceof Opcode::VarArgsEnd and
resultType = getVoidType()
}
final override Instruction getFirstInstruction() { result = getVAList().getFirstInstruction() }
final override Instruction getResult() { none() }
final override TranslatedElement getChild(int id) { id = 0 and result = getVAList() }
private TranslatedExpr getVAList() {
result = getTranslatedExpr(expr.getVAList().getFullyConverted())
}
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = OnlyInstructionTag() and
kind instanceof GotoEdge and
result = getParent().getChildSuccessor(this)
}
final override Instruction getChildSuccessor(TranslatedElement child) {
child = getVAList() and
result = getInstruction(OnlyInstructionTag())
}
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
tag = OnlyInstructionTag() and
operandTag instanceof UnaryOperandTag and
result = getVAList().getResult()
}
}
/**
* The IR translation of a `BuiltInVarArgCopy` expression.
*/
class TranslatedVarArgCopy extends TranslatedBuiltInOperation {
class TranslatedVarArgCopy extends TranslatedNonConstantExpr {
override BuiltInVarArgCopy expr;
final override Opcode getOpcode() { result instanceof Opcode::VarArgCopy }
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = VarArgsVAListLoadTag() and
opcode instanceof Opcode::Load and
resultType = getTypeForPRValue(getVAListType(expr.getSourceVAList().getFullyConverted()))
or
tag = VarArgsVAListStoreTag() and
opcode instanceof Opcode::Store and
resultType = getTypeForPRValue(getVAListType(expr.getDestinationVAList().getFullyConverted()))
}
final override Instruction getFirstInstruction() {
result = getSourceVAList().getFirstInstruction()
}
final override Instruction getResult() { result = getInstruction(VarArgsVAListStoreTag()) }
final override TranslatedElement getChild(int id) {
id = 0 and result = getDestinationVAList()
or
id = 1 and result = getSourceVAList()
}
private TranslatedExpr getDestinationVAList() {
result = getTranslatedExpr(expr.getDestinationVAList().getFullyConverted())
}
private TranslatedExpr getSourceVAList() {
result = getTranslatedExpr(expr.getSourceVAList().getFullyConverted())
}
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = VarArgsVAListLoadTag() and
kind instanceof GotoEdge and
result = getDestinationVAList().getFirstInstruction()
or
tag = VarArgsVAListStoreTag() and
kind instanceof GotoEdge and
result = getParent().getChildSuccessor(this)
}
final override Instruction getChildSuccessor(TranslatedElement child) {
child = getSourceVAList() and
result = getInstruction(VarArgsVAListLoadTag())
or
child = getDestinationVAList() and
result = getInstruction(VarArgsVAListStoreTag())
}
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
tag = VarArgsVAListLoadTag() and
(
operandTag instanceof AddressOperandTag and
result = getSourceVAList().getResult()
or
operandTag instanceof LoadOperandTag and
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
)
or
tag = VarArgsVAListStoreTag() and
(
operandTag instanceof AddressOperandTag and result = getDestinationVAList().getResult()
or
operandTag instanceof StoreValueOperandTag and result = getInstruction(VarArgsVAListLoadTag())
)
}
}
/**

View File

@@ -10,12 +10,45 @@ private import TranslatedElement
private import TranslatedExpr
private import TranslatedInitialization
private import TranslatedStmt
private import VarArgs
/**
* Gets the `TranslatedFunction` that represents function `func`.
*/
TranslatedFunction getTranslatedFunction(Function func) { result.getAST() = func }
/**
* Gets the size, in bytes, of the variable used to represent the `...` parameter in a varargs
* function. This is determined by finding the total size of all of the arguments passed to the
* `...` in each call in the program, and choosing the maximum of those, with a minimum of 8 bytes.
*/
private int getEllipsisVariableByteSize() {
result =
max(int variableSize |
variableSize =
max(Call call, int callSize |
callSize =
sum(int argIndex |
isEllipsisArgumentIndex(call, argIndex)
|
call.getArgument(argIndex).getType().getSize()
)
|
callSize
)
or
variableSize = 8
|
variableSize
)
}
CppType getEllipsisVariablePRValueType() {
result = getUnknownOpaqueType(getEllipsisVariableByteSize())
}
CppType getEllipsisVariableGLValueType() { result = getTypeForGLValue(any(UnknownType t)) }
/**
* Represents the IR translation of a function. This is the root elements for
* all other elements associated with this function.
@@ -60,6 +93,9 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
final private TranslatedParameter getParameter(int index) {
result = getTranslatedParameter(func.getParameter(index))
or
index = getEllipsisParameterIndexForFunction(func) and
result = getTranslatedEllipsisParameter(func)
}
final override Instruction getFirstInstruction() { result = getInstruction(EnterFunctionTag()) }
@@ -113,7 +149,9 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
final override Instruction getChildSuccessor(TranslatedElement child) {
exists(int paramIndex |
child = getParameter(paramIndex) and
if exists(func.getParameter(paramIndex + 1))
if
exists(func.getParameter(paramIndex + 1)) or
getEllipsisParameterIndexForFunction(func) = paramIndex + 1
then result = getParameter(paramIndex + 1).getFirstInstruction()
else result = getConstructorInitList().getFirstInstruction()
)
@@ -237,10 +275,18 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
result = getReturnVariable()
}
final override predicate needsUnknownOpaqueType(int byteSize) {
byteSize = getEllipsisVariableByteSize()
}
final override predicate hasTempVariable(TempVariableTag tag, CppType type) {
tag = ReturnValueTempVar() and
hasReturnValue() and
type = getTypeForPRValue(getReturnType())
or
tag = EllipsisTempVar() and
func.isVarargs() and
type = getEllipsisVariablePRValueType()
}
/**
@@ -258,6 +304,11 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
result = getIRTempVariable(func, ReturnValueTempVar())
}
/**
* Get the variable that represents the `...` parameter, if any.
*/
final IREllipsisVariable getEllipsisVariable() { result.getEnclosingFunction() = func }
/**
* Holds if the function has a non-`void` return type.
*/
@@ -316,34 +367,29 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
}
/**
* Gets the `TranslatedParameter` that represents parameter `param`.
* Gets the `TranslatedPositionalParameter` that represents parameter `param`.
*/
TranslatedParameter getTranslatedParameter(Parameter param) { result.getAST() = param }
TranslatedPositionalParameter getTranslatedParameter(Parameter param) { result.getAST() = param }
/**
* Represents the IR translation of a function parameter, including the
* initialization of that parameter with the incoming argument.
* Gets the `TranslatedEllipsisParameter` for function `func`, if one exists.
*/
class TranslatedParameter extends TranslatedElement, TTranslatedParameter {
Parameter param;
TranslatedEllipsisParameter getTranslatedEllipsisParameter(Function func) {
result.getFunction() = func
}
TranslatedParameter() { this = TTranslatedParameter(param) }
final override string toString() { result = param.toString() }
final override Locatable getAST() { result = param }
final override Function getFunction() {
result = param.getFunction() or
result = param.getCatchBlock().getEnclosingFunction()
}
/**
* The IR translation of a parameter to a function. This can be either a user-declared parameter
* (`TranslatedPositionParameter`) or the synthesized parameter used to represent a `...` in a
* varargs function (`TranslatedEllipsisParameter`).
*/
abstract class TranslatedParameter extends TranslatedElement {
final override TranslatedElement getChild(int id) { none() }
final override Instruction getFirstInstruction() {
result = getInstruction(InitializerVariableAddressTag())
}
final override TranslatedElement getChild(int id) { none() }
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
kind instanceof GotoEdge and
(
@@ -368,16 +414,16 @@ class TranslatedParameter extends TranslatedElement, TTranslatedParameter {
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = InitializerVariableAddressTag() and
opcode instanceof Opcode::VariableAddress and
resultType = getTypeForGLValue(getVariableType(param))
resultType = getGLValueType()
or
tag = InitializerStoreTag() and
opcode instanceof Opcode::InitializeParameter and
resultType = getTypeForPRValue(getVariableType(param))
resultType = getPRValueType()
or
hasIndirection() and
tag = InitializerIndirectAddressTag() and
opcode instanceof Opcode::Load and
resultType = getTypeForPRValue(getVariableType(param))
resultType = getPRValueType()
or
hasIndirection() and
tag = InitializerIndirectStoreTag() and
@@ -391,7 +437,7 @@ class TranslatedParameter extends TranslatedElement, TTranslatedParameter {
tag = InitializerVariableAddressTag() or
tag = InitializerIndirectStoreTag()
) and
result = getIRUserVariable(getFunction(), param)
result = getIRVariable()
}
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
@@ -416,13 +462,74 @@ class TranslatedParameter extends TranslatedElement, TTranslatedParameter {
result = getInstruction(InitializerIndirectAddressTag())
}
predicate hasIndirection() {
abstract predicate hasIndirection();
abstract CppType getGLValueType();
abstract CppType getPRValueType();
abstract IRAutomaticVariable getIRVariable();
}
/**
* Represents the IR translation of a function parameter, including the
* initialization of that parameter with the incoming argument.
*/
class TranslatedPositionalParameter extends TranslatedParameter, TTranslatedParameter {
Parameter param;
TranslatedPositionalParameter() { this = TTranslatedParameter(param) }
final override string toString() { result = param.toString() }
final override Locatable getAST() { result = param }
final override Function getFunction() {
result = param.getFunction() or
result = param.getCatchBlock().getEnclosingFunction()
}
final override predicate hasIndirection() {
exists(Type t | t = param.getUnspecifiedType() |
t instanceof ArrayType or
t instanceof PointerType or
t instanceof ReferenceType
)
}
final override CppType getGLValueType() { result = getTypeForGLValue(getVariableType(param)) }
final override CppType getPRValueType() { result = getTypeForPRValue(getVariableType(param)) }
final override IRAutomaticUserVariable getIRVariable() {
result = getIRUserVariable(getFunction(), param)
}
}
/**
* The IR translation of the synthesized parameter used to represent the `...` in a varargs
* function.
*/
class TranslatedEllipsisParameter extends TranslatedParameter, TTranslatedEllipsisParameter {
Function func;
TranslatedEllipsisParameter() { this = TTranslatedEllipsisParameter(func) }
final override string toString() { result = "..." }
final override Locatable getAST() { result = func }
final override Function getFunction() { result = func }
final override predicate hasIndirection() { any() }
final override CppType getGLValueType() { result = getEllipsisVariableGLValueType() }
final override CppType getPRValueType() { result = getEllipsisVariablePRValueType() }
final override IREllipsisVariable getIRVariable() {
result = getTranslatedFunction(func).getEllipsisVariable()
}
}
private TranslatedConstructorInitList getTranslatedConstructorInitList(Function func) {

View File

@@ -115,10 +115,14 @@ abstract class TranslatedVariableInitialization extends TranslatedElement, Initi
* evaluating the initializer.
*/
final predicate hasUninitializedInstruction() {
not exists(getInitialization()) or
getInitialization() instanceof TranslatedListInitialization or
getInitialization() instanceof TranslatedConstructorInitialization or
getInitialization().(TranslatedStringLiteralInitialization).zeroInitRange(_, _)
(
not exists(getInitialization()) or
getInitialization() instanceof TranslatedListInitialization or
getInitialization() instanceof TranslatedConstructorInitialization or
getInitialization().(TranslatedStringLiteralInitialization).zeroInitRange(_, _)
) and
// Variables with static or thread-local storage duration are zero-initialized at program startup.
getIRVariable() instanceof IRAutomaticVariable
}
}

View File

@@ -0,0 +1,62 @@
/**
* Utilities for determining which parameters and arguments correspond to the `...` parameter for
* varargs functions.
*/
private import cpp
/**
* Gets the index of the `...` parameter, if any. If present, the value will always be equal to
* `func.getNumberOfParameters()`.
*/
int getEllipsisParameterIndexForFunction(Function func) {
func.isVarargs() and result = func.getNumberOfParameters()
}
/**
* Gets the index of the `...` parameter, if any.
*/
int getEllipsisParameterIndexForRoutineType(RoutineType type) {
// Since the extractor doesn't record this information directly, we look for routine types whose
// last parameter type is `UnknownType`.
type.getParameterType(result) instanceof UnknownType and
result = strictcount(type.getAParameterType()) - 1
}
/**
* Gets the index of the `...` parameter, if any. This will be one greater than the index of the
* last declared positional parameter.
*/
int getEllipsisParameterIndex(Call call) {
exists(FunctionCall funcCall |
funcCall = call and
if funcCall.getTargetType() instanceof RoutineType
then result = getEllipsisParameterIndexForRoutineType(funcCall.getTargetType())
else result = getEllipsisParameterIndexForFunction(funcCall.getTarget())
)
or
exists(ExprCall exprCall |
exprCall = call and
result = getEllipsisParameterIndexForRoutineType(exprCall.getExpr().getType().stripType())
)
}
/**
* Gets the index of the parameter that will be initialized with the value of the argument
* specified by `argIndex`. For ordinary positional parameters, the argument and parameter indices
* will be equal. For a call to a varargs function, all arguments passed to the `...` will be
* mapped to the index returned by `getEllipsisParameterIndex()`.
*/
int getParameterIndexForArgument(Call call, int argIndex) {
exists(call.getArgument(argIndex)) and
if argIndex >= getEllipsisParameterIndex(call)
then result = getEllipsisParameterIndex(call)
else result = argIndex
}
/**
* Holds if the argument specified by `index` is an argument to the `...` of a varargs function.
*/
predicate isEllipsisArgumentIndex(Call call, int index) {
exists(call.getArgument(index)) and index >= getEllipsisParameterIndex(call)
}

View File

@@ -27,6 +27,9 @@ class IRBlockBase extends TIRBlock {
* by debugging and printing code only.
*/
int getDisplayIndex() {
exists(IRConfiguration::IRConfiguration config |
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
) and
this =
rank[result + 1](IRBlock funcBlock |
funcBlock.getEnclosingFunction() = getEnclosingFunction()

View File

@@ -5,6 +5,7 @@ import IRTypeSanity // module is in IRType.qll
module InstructionSanity {
private import internal.InstructionImports as Imports
private import Imports::OperandTag
private import Imports::Overlap
private import internal.IRInternal
/**
@@ -272,4 +273,48 @@ module InstructionSanity {
func = switchInstr.getEnclosingIRFunction() and
funcText = Language::getIdentityString(func.getFunction())
}
/**
* Holds if `instr` is on the chain of chi/phi instructions for all aliased
* memory.
*/
private predicate isOnAliasedDefinitionChain(Instruction instr) {
instr instanceof AliasedDefinitionInstruction
or
isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal())
or
isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
}
private predicate shouldBeConflated(Instruction instr) {
isOnAliasedDefinitionChain(instr)
or
instr instanceof UnmodeledDefinitionInstruction
or
instr.getOpcode() instanceof Opcode::InitializeNonLocal
}
query predicate notMarkedAsConflated(Instruction instr) {
shouldBeConflated(instr) and
not instr.isResultConflated()
}
query predicate wronglyMarkedAsConflated(Instruction instr) {
instr.isResultConflated() and
not shouldBeConflated(instr)
}
query predicate invalidOverlap(
MemoryOperand useOperand, string message, IRFunction func, string funcText
) {
exists(Overlap overlap |
overlap = useOperand.getDefinitionOverlap() and
overlap instanceof MayPartiallyOverlap and
message =
"MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
overlap.toString() + "'." and
func = useOperand.getEnclosingIRFunction() and
funcText = Language::getIdentityString(func.getFunction())
)
}
}

View File

@@ -23,7 +23,8 @@ class IRVariable extends TIRVariable {
IRVariable() {
this = TIRUserVariable(_, _, func) or
this = TIRTempVariable(func, _, _, _) or
this = TIRStringLiteral(func, _, _, _)
this = TIRStringLiteral(func, _, _, _) or
this = TIRDynamicInitializationFlag(func, _, _)
}
string toString() { none() }
@@ -149,7 +150,8 @@ class IRGeneratedVariable extends IRVariable {
IRGeneratedVariable() {
this = TIRTempVariable(func, ast, _, type) or
this = TIRStringLiteral(func, ast, type, _)
this = TIRStringLiteral(func, ast, type, _) or
this = TIRDynamicInitializationFlag(func, ast, type)
}
final override Language::LanguageType getLanguageType() { result = type }
@@ -208,7 +210,17 @@ class IRReturnVariable extends IRTempVariable {
class IRThrowVariable extends IRTempVariable {
IRThrowVariable() { tag = ThrowTempVar() }
override string getBaseString() { result = "#throw" }
final override string getBaseString() { result = "#throw" }
}
/**
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
* function that accepts a variable number of arguments.
*/
class IREllipsisVariable extends IRTempVariable {
IREllipsisVariable() { tag = EllipsisTempVar() }
final override string toString() { result = "#ellipsis" }
}
/**
@@ -226,7 +238,30 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
}
override string getBaseString() { result = "#string" }
final override string getBaseString() { result = "#string" }
final Language::StringLiteral getLiteral() { result = literal }
}
/**
* A variable generated to track whether a specific non-stack variable has been initialized. This is
* used to model the runtime initialization of static local variables in C++, as well as static
* fields in C#.
*/
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
Language::Variable var;
IRDynamicInitializationFlag() {
this = TIRDynamicInitializationFlag(func, var, type) and ast = var
}
final override string toString() { result = var.toString() + "#init" }
final Language::Variable getVariable() { result = var }
final override string getUniqueId() {
result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString()
}
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
}

View File

@@ -15,6 +15,9 @@ private import Imports::OperandTag
* `File` and line number. Used for assigning register names when printing IR.
*/
private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File file, int line) {
exists(IRConfiguration::IRConfiguration config |
config.shouldEvaluateDebugStringsForFunction(irFunc.getFunction())
) and
exists(Language::Location location |
irFunc = result.getEnclosingIRFunction() and
location = result.getLocation() and
@@ -39,6 +42,12 @@ class Instruction extends Construction::TInstruction {
result = getResultString() + " = " + getOperationString() + " " + getOperandsString()
}
private predicate shouldGenerateDumpStrings() {
exists(IRConfiguration::IRConfiguration config |
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
)
}
/**
* Gets a string describing the operation of this instruction. This includes
* the opcode and the immediate value, if any. For example:
@@ -46,6 +55,7 @@ class Instruction extends Construction::TInstruction {
* VariableAddress[x]
*/
final string getOperationString() {
shouldGenerateDumpStrings() and
if exists(getImmediateString())
then result = getOperationPrefix() + getOpcode().toString() + "[" + getImmediateString() + "]"
else result = getOperationPrefix() + getOpcode().toString()
@@ -57,10 +67,12 @@ class Instruction extends Construction::TInstruction {
string getImmediateString() { none() }
private string getOperationPrefix() {
shouldGenerateDumpStrings() and
if this instanceof SideEffectInstruction then result = "^" else result = ""
}
private string getResultPrefix() {
shouldGenerateDumpStrings() and
if getResultIRType() instanceof IRVoidType
then result = "v"
else
@@ -74,6 +86,7 @@ class Instruction extends Construction::TInstruction {
* used by debugging and printing code only.
*/
int getDisplayIndexInBlock() {
shouldGenerateDumpStrings() and
exists(IRBlock block |
this = block.getInstruction(result)
or
@@ -87,6 +100,7 @@ class Instruction extends Construction::TInstruction {
}
private int getLineRank() {
shouldGenerateDumpStrings() and
this =
rank[result](Instruction instr |
instr =
@@ -105,6 +119,7 @@ class Instruction extends Construction::TInstruction {
* Example: `r1_1`
*/
string getResultId() {
shouldGenerateDumpStrings() and
result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank()
}
@@ -116,6 +131,7 @@ class Instruction extends Construction::TInstruction {
* Example: `r1_1(int*)`
*/
final string getResultString() {
shouldGenerateDumpStrings() and
result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")"
}
@@ -126,6 +142,7 @@ class Instruction extends Construction::TInstruction {
* Example: `func:r3_4, this:r3_5`
*/
string getOperandsString() {
shouldGenerateDumpStrings() and
result =
concat(Operand operand |
operand = getAnOperand()
@@ -321,6 +338,17 @@ class Instruction extends Construction::TInstruction {
Construction::hasModeledMemoryResult(this)
}
/**
* Holds if this is an instruction with a memory result that represents a
* conflation of more than one memory allocation.
*
* This happens in practice when dereferencing a pointer that cannot be
* tracked back to a single local allocation. Such memory is instead modeled
* as originating on the `AliasedDefinitionInstruction` at the entry of the
* function.
*/
final predicate isResultConflated() { Construction::hasConflatedMemoryResult(this) }
/**
* Gets the successor of this instruction along the control flow edge
* specified by `kind`.

View File

@@ -384,6 +384,8 @@ class PositionalArgumentOperand extends ArgumentOperand {
class SideEffectOperand extends TypedOperand {
override SideEffectOperandTag tag;
override string toString() { result = "SideEffect" }
}
/**

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