mirror of
https://github.com/github/codeql.git
synced 2026-05-01 11:45:14 +02:00
Merge branch 'master' of https://github.com/semmle/ql
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
{ "provide": [ "*/ql/src/qlpack.yml",
|
||||
"*/ql/test/qlpack.yml",
|
||||
"*/upgrades/qlpack.yml",
|
||||
"misc/legacy-support/*/qlpack.yml",
|
||||
"misc/suite-helpers/qlpack.yml",
|
||||
"codeql/.codeqlmanifest.json" ] }
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/cpp/ @Semmle/cpp-analysis
|
||||
/csharp/ @Semmle/cs
|
||||
/java/ @Semmle/java
|
||||
/javascript/ @Semmle/js
|
||||
/cpp/ @Semmle/cpp-analysis
|
||||
/python/ @Semmle/python
|
||||
/cpp/**/*.qhelp @hubwriter
|
||||
/csharp/**/*.qhelp @jf205
|
||||
/java/**/*.qhelp @felicitymay
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Contributing to QL
|
||||
# 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!
|
||||
|
||||
@@ -9,13 +9,13 @@ Before we accept your pull request, we require that you have agreed to our Contr
|
||||
If you have an idea for a query that you would like to share with other Semmle 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 Semmle queries.
|
||||
|
||||
1. **Consult the QL documentation for query writers**
|
||||
1. **Consult the documentation for query writers**
|
||||
|
||||
There is lots of useful documentation to help you write QL, ranging from information about query file structure to language-specific tutorials. For more information on the documentation available, see [Writing QL queries](https://help.semmle.com/QL/learn-ql/writing-queries/writing-queries.html) on [help.semmle.com](https://help.semmle.com).
|
||||
There is lots of useful documentation to help you write queries, ranging from information about query file structure to tutorials for specific target languages. For more information on the documentation available, see [Writing CodeQL queries](https://help.semmle.com/QL/learn-ql/writing-queries/writing-queries.html) on [help.semmle.com](https://help.semmle.com).
|
||||
|
||||
2. **Format your QL correctly**
|
||||
2. **Format your code correctly**
|
||||
|
||||
All of Semmle's standard QL queries and libraries are uniformly formatted for clarity and consistency, so we strongly recommend that all QL contributions follow the same formatting guidelines. If you use QL for Eclipse, you can auto-format your query in the [QL editor](https://help.semmle.com/ql-for-eclipse/Content/WebHelp/ql-editor.html). For more information, see the [QL style guide](https://github.com/Semmle/ql/blob/master/docs/ql-style-guide.md).
|
||||
All of Semmle's 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 QL for Eclipse, you can auto-format your query in the [QL editor](https://help.semmle.com/ql-for-eclipse/Content/WebHelp/ql-editor.html). For more information, see the [CodeQL style guide](https://github.com/Semmle/ql/blob/master/docs/ql-style-guide.md).
|
||||
|
||||
3. **Make sure your query has the correct metadata**
|
||||
|
||||
@@ -29,7 +29,7 @@ Follow the steps below to help other users understand what your query does, and
|
||||
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 QL for Eclipse.
|
||||
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.
|
||||
|
||||
5. **Save your query in a `.ql` file in correct language directory in this repository**
|
||||
5. **Save your query in a `.ql` file in the correct language directory in this repository**
|
||||
|
||||
There are five language-specific directories in this repository:
|
||||
|
||||
@@ -54,7 +54,7 @@ repositories, which might be made 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 QL product.
|
||||
on the basis of our legitimate interest in creating the CodeQL product.
|
||||
|
||||
Please do get in touch (privacy@semmle.com) if you have any questions about
|
||||
this or our data protection policies.
|
||||
|
||||
14
README.md
14
README.md
@@ -1,16 +1,16 @@
|
||||
# Semmle QL
|
||||
# CodeQL
|
||||
|
||||
This open source repository contains the standard QL libraries and queries that power [LGTM](https://lgtm.com), and the other products that [Semmle](https://semmle.com) makes available to its customers worldwide.
|
||||
This open source repository contains the standard CodeQL libraries and queries that power [LGTM](https://lgtm.com), and the other products that [Semmle](https://semmle.com) makes available to its customers worldwide.
|
||||
|
||||
## How do I learn QL and run queries?
|
||||
## How do I learn CodeQL and run queries?
|
||||
|
||||
There is [extensive documentation](https://help.semmle.com/QL/learn-ql/) on getting started with writing QL.
|
||||
You can use the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com or the [QL for Eclipse](https://lgtm.com/help/lgtm/running-queries-ide) plugin to try out your queries on any open-source project that's currently being analyzed.
|
||||
There is [extensive documentation](https://help.semmle.com/QL/learn-ql/) on getting started with writing CodeQL.
|
||||
You can use the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com or the [CodeQL for Visual Studio Code](https://help.semmle.com/codeql/codeql-for-vscode.html) extension to try out your queries on any open source project that's currently being analyzed.
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions to our standard library and standard checks. Do you have an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request! Before you do, though, please take the time to read our [contributing guidelines](CONTRIBUTING.md). You can also consult our [style guides](https://github.com/Semmle/ql/tree/master/docs) to learn how to format your QL for consistency and clarity, how to write query metadata, and how to write query help documentation for your query.
|
||||
We welcome contributions to our standard library and standard checks. Do you have an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request! Before you do, though, please take the time to read our [contributing guidelines](CONTRIBUTING.md). You can also consult our [style guides](https://github.com/Semmle/ql/tree/master/docs) to learn how to format your code for consistency and clarity, how to write query metadata, and how to write query help documentation for your query.
|
||||
|
||||
## License
|
||||
|
||||
The QL queries in this repository are licensed under [Apache License 2.0](LICENSE) by [Semmle](https://semmle.com).
|
||||
The code in this repository is licensed under [Apache License 2.0](LICENSE) by [Semmle](https://semmle.com).
|
||||
|
||||
@@ -9,6 +9,8 @@ The following changes in version 1.23 affect C/C++ analysis in all applications.
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | reliability, japanese-era | This query is a combination of two old queries that were identical in purpose but separate as an implementation detail. This new query replaces Hard-coded Japanese era start date in call (`cpp/japanese-era/constructor-or-method-with-exact-era-date`) and Hard-coded Japanese era start date in struct (`cpp/japanese-era/struct-with-exact-era-date`). |
|
||||
| Signed overflow check (`cpp/signed-overflow-check`) | correctness, security | Finds overflow checks that rely on signed integer addition to overflow, which has undefined behavior. Example: `a + b < a`. |
|
||||
| Pointer overflow check (`cpp/pointer-overflow-check`) | correctness, security | Finds overflow checks that rely on pointer addition to overflow, which has undefined behavior. Example: `ptr + a < ptr`. |
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
@@ -22,8 +24,11 @@ The following changes in version 1.23 affect C/C++ analysis in all applications.
|
||||
| Too few arguments to formatting function (`cpp/wrong-number-format-arguments`) | Fewer false positive results | Fixed false positives resulting from mistmatching declarations of a formatting function. |
|
||||
| Too many arguments to formatting function (`cpp/too-many-format-arguments`) | Fewer false positive results | Fixed false positives resulting from mistmatching declarations of a formatting function. |
|
||||
| Unclear comparison precedence (`cpp/comparison-precedence`) | Fewer false positive results | False positives involving template classes and functions have been fixed. |
|
||||
| Comparison of narrow type with wide type in loop condition (`cpp/comparison-with-wider-type`) | Higher precision | The precision of this query has been increased to "high" as the alerts from this query have proved to be valuable on real-world projects. With this precision, results are now displayed by default in LGTM. |
|
||||
| Non-constant format string (`cpp/non-constant-format`) | Fewer false positive results | Fixed false positives resulting from mistmatching declarations of a formatting function. |
|
||||
| Wrong type of arguments to formatting function (`cpp/wrong-type-format-argument`) | More correct results and fewer false positive results | This query now understands explicitly specified argument numbers in format strings, such as the `1$` in `%1$s`. |
|
||||
|
||||
## Changes to QL libraries
|
||||
## Changes to libraries
|
||||
|
||||
* The data-flow library has been extended with a new feature to aid debugging.
|
||||
Instead of specifying `isSink(Node n) { any() }` on a configuration to
|
||||
@@ -38,6 +43,10 @@ The following changes in version 1.23 affect C/C++ analysis in all applications.
|
||||
definition of `x` when `x` is a variable of pointer type. It no longer
|
||||
considers deep paths such as `f(&x.myField)` to be definitions of `x`. These
|
||||
changes are in line with the user expectations we've observed.
|
||||
* The data-flow library now makes it easier to specify barriers/sanitizers
|
||||
arising from guards by overriding the predicate
|
||||
`isBarrierGuard`/`isSanitizerGuard` on data-flow and taint-tracking
|
||||
configurations respectively.
|
||||
* There is now a `DataFlow::localExprFlow` predicate and a
|
||||
`TaintTracking::localExprTaint` predicate to make it easy to use the most
|
||||
common case of local data flow and taint: from one `Expr` to another.
|
||||
@@ -45,7 +54,14 @@ The following changes in version 1.23 affect C/C++ analysis in all applications.
|
||||
clarity (e.g. `isOutReturnPointer()` to `isReturnValueDeref()`). The existing member predicates
|
||||
have been deprecated, and will be removed in a future release. Code that uses the old member
|
||||
predicates should be updated to use the corresponding new member predicate.
|
||||
* The predicates `Declaration.hasStdName()` and `Declaration.hasGlobalOrStdName`
|
||||
have been added, simplifying handling of C++ standard library functions.
|
||||
* The control-flow graph is now computed in QL, not in the extractor. This can
|
||||
lead to regressions (or improvements) in how queries are optimized because
|
||||
optimization in QL relies on static size estimates, and the control-flow edge
|
||||
relations will now have different size estimates than before.
|
||||
* Support has been added for non-type template arguments. This means that the
|
||||
return type of `Declaration::getTemplateArgument()` and
|
||||
`Declaration::getATemplateArgument` have changed to `Locatable`. See the
|
||||
documentation for `Declaration::getTemplateArgument()` and
|
||||
`Declaration::getTemplateArgumentKind()` for details.
|
||||
|
||||
@@ -4,11 +4,12 @@ The following changes in version 1.23 affect C# analysis in all applications.
|
||||
|
||||
## New queries
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
| Deserialized delegate (`cs/deserialized-delegate`) | security, external/cwe/cwe-502 | Finds unsafe deserialization of delegate types. |
|
||||
| Deserialization of untrusted data (`cs/unsafe-deserialization-untrusted-input`) | security, external/cwe/cwe-502 | Finds flow of untrusted input to calls to unsafe deserializers. |
|
||||
| Unsafe year argument for 'DateTime' constructor (`cs/unsafe-year-construction`) | reliability, date-time | Finds incorrect manipulation of `DateTime` values, which could lead to invalid dates. |
|
||||
| Unsafe deserializer (`cs/unsafe-deserialization`) | security, external/cwe/cwe-502 | Finds calls to unsafe deserializers. |
|
||||
| Mishandling the Japanese era start date (`cs/mishandling-japanese-era`) | reliability, date-time | Finds hard-coded Japanese era start dates that could be invalid. |
|
||||
|
||||
## Changes to existing queries
|
||||
@@ -24,7 +25,7 @@ The following changes in version 1.23 affect C# analysis in all applications.
|
||||
|
||||
* `nameof` expressions are now extracted correctly when the name is a namespace.
|
||||
|
||||
## Changes to QL libraries
|
||||
## Changes to libraries
|
||||
|
||||
* The new class `NamespaceAccess` models accesses to namespaces, for example in `nameof` expressions.
|
||||
* The data-flow library now makes it easier to specify barriers/sanitizers
|
||||
@@ -43,5 +44,7 @@ The following changes in version 1.23 affect C# analysis in all applications.
|
||||
* There is now a `DataFlow::localExprFlow` predicate and a
|
||||
`TaintTracking::localExprTaint` predicate to make it easy to use the most
|
||||
common case of local data flow and taint: from one `Expr` to another.
|
||||
* Data is now tracked through null-coalescing expressions (`??`).
|
||||
* A new library `semmle.code.csharp.Unification` has been added. This library exposes two predicates `unifiable` and `subsumes` for calculating type unification and type subsumption, respectively.
|
||||
|
||||
## Changes to autobuilder
|
||||
|
||||
@@ -19,7 +19,7 @@ The following changes in version 1.23 affect Java analysis in all applications.
|
||||
| Query built without neutralizing special characters (`java/concatenated-sql-query`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as SQL expressions sinks. |
|
||||
| Useless comparison test (`java/constant-comparison`) | Fewer false positives | Additional overflow check patterns are now recognized and no longer reported. |
|
||||
|
||||
## Changes to QL libraries
|
||||
## Changes to libraries
|
||||
|
||||
* The data-flow library has been extended with a new feature to aid debugging.
|
||||
Instead of specifying `isSink(Node n) { any() }` on a configuration to
|
||||
|
||||
@@ -2,30 +2,38 @@
|
||||
|
||||
## General improvements
|
||||
|
||||
* Suppor for `globalThis` has been added.
|
||||
* Automatic classification of generated and minified files has been improved, in particular files generated by Doxygen are now recognized.
|
||||
|
||||
* Support for `globalThis` has been added.
|
||||
|
||||
* Support for the following frameworks and libraries has been improved:
|
||||
- [firebase](https://www.npmjs.com/package/firebase)
|
||||
- [get-them-args](https://www.npmjs.com/package/get-them-args)
|
||||
- [minimist](https://www.npmjs.com/package/minimist)
|
||||
- [mongodb](https://www.npmjs.com/package/mongodb)
|
||||
- [mongoose](https://www.npmjs.com/package/mongoose)
|
||||
- [optimist](https://www.npmjs.com/package/optimist)
|
||||
- [parse-torrent](https://www.npmjs.com/package/parse-torrent)
|
||||
- [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible)
|
||||
- [yargs](https://www.npmjs.com/package/yargs)
|
||||
|
||||
* The call graph has been improved to resolve method calls in more cases. This may produce more security alerts.
|
||||
|
||||
* TypeScript 3.6 features are supported.
|
||||
|
||||
* TypeScript 3.6 and 3.7 features are now supported.
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|---------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Unused index variable (`js/unused-index-variable`) | correctness | Highlights loops that iterate over an array, but do not use the index variable to access array elements, indicating a possible typo or logic error. Results are shown on LGTM by default. |
|
||||
| Ignoring result from pure array method (`js/ignore-array-result`) | maintainability, correctness | Highlights calls to array methods without side effects where the return value is ignored. Results are shown on LGTM by default. |
|
||||
| Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | security, correctness, external/cwe/cwe-020 | Highlights checks for `javascript:` URLs that do not take `data:` or `vbscript:` URLs into account. Results are shown on LGTM by default. |
|
||||
| Loop bound injection (`js/loop-bound-injection`) | security, external/cwe/cwe-834 | Highlights loops where a user-controlled object with an arbitrary .length value can trick the server to loop indefinitely. Results are shown on LGTM by default. |
|
||||
| Suspicious method name (`js/suspicious-method-name-declaration`) | correctness, typescript, methods | Highlights suspiciously named methods where the developer likely meant to write a constructor or function. Results are shown on LGTM by default. |
|
||||
| Shell command built from environment values (`js/shell-command-injection-from-environment`) | correctness, security, external/cwe/cwe-078, external/cwe/cwe-088 | Highlights shell commands that may change behavior inadvertently depending on the execution environment, indicating a possible violation of [CWE-78](https://cwe.mitre.org/data/definitions/78.html). Results are shown on LGTM by default.|
|
||||
| Suspicious method name (`js/suspicious-method-name-declaration`) | correctness, typescript, methods | Highlights suspiciously named methods where the developer likely meant to write a constructor or function. Results are shown on LGTM by default. |
|
||||
| Unreachable method overloads (`js/unreachable-method-overloads`) | correctness, typescript | Highlights method overloads that are impossible to use from client code. Results are shown on LGTM by default. |
|
||||
| Unused index variable (`js/unused-index-variable`) | correctness | Highlights loops that iterate over an array, but do not use the index variable to access array elements, indicating a possible typo or logic error. Results are shown on LGTM by default. |
|
||||
| Use of returnless function (`js/use-of-returnless-function`) | maintainability, correctness | Highlights calls where the return value is used, but the callee never returns a value. Results are shown on LGTM by default. |
|
||||
| Useless regular expression character escape (`js/useless-regexp-character-escape`) | correctness, security, external/cwe/cwe-20 | Highlights regular expression strings with useless character escapes, indicating a possible violation of [CWE-20](https://cwe.mitre.org/data/definitions/20.html). Results are shown on LGTM by default. |
|
||||
| Unreachable method overloads (`js/unreachable-method-overloads`) | correctness, typescript | Highlights method overloads that are impossible to use from client code. Results are shown on LGTM by default. |
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
@@ -43,10 +51,17 @@
|
||||
| Reflected cross-site scripting (`js/reflected-xss`) | Fewer false-positive results | The query now recognizes more sanitizers. |
|
||||
| Stored cross-site scripting (`js/stored-xss`) | Fewer false-positive results | The query now recognizes more sanitizers. |
|
||||
| Uncontrolled command line (`js/command-line-injection`) | More results | This query now treats responses from servers as untrusted. |
|
||||
| Uncontrolled data used in path expression (`js/path-injection`) | Fewer false-positive results | This query now recognizes calls to Express `sendFile` as safe in some cases. |
|
||||
| Unknown directive (`js/unknown-directive`) | Fewer false positive results | This query no longer flags uses of ":", which is sometimes used like a directive. |
|
||||
|
||||
## Changes to QL libraries
|
||||
## Changes to libraries
|
||||
|
||||
* `Expr.getDocumentation()` now handles chain assignments.
|
||||
* String literals are now parsed as regular expressions.
|
||||
Consequently, a `RegExpTerm` may occur as part of a string literal or
|
||||
as a regular expression literal. Queries that search for regular expressions may need to
|
||||
use `RegExpTerm.isPartOfRegExpLiteral` or `RegExpTerm.isUsedAsRegExp` to restrict the search.
|
||||
A regular expression AST can be obtained from a string literal using `StringLiteral.asRegExp`.
|
||||
|
||||
## Removal of deprecated queries
|
||||
|
||||
|
||||
@@ -20,3 +20,8 @@
|
||||
|----------------------------|------------------------|------------|
|
||||
| Unreachable code | Fewer false positives | Analysis now accounts for uses of `contextlib.suppress` to suppress exceptions. |
|
||||
| `__iter__` method returns a non-iterator | Better alert message | Alert now highlights which class is expected to be an iterator. |
|
||||
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
* Django library now recognizes positional arguments from a `django.conf.urls.url` regex (Django version 1.x)
|
||||
|
||||
@@ -5,6 +5,17 @@
|
||||
## Changes to code extraction
|
||||
|
||||
* Asynchronous generator methods are now parsed correctly and no longer cause a spurious syntax error.
|
||||
* Files in `node_modules` and `bower_components` folders are no longer extracted by default. If you still want to extract files from these folders, you can add the following filters to your `lgtm.yml` file (or add them to existing filters):
|
||||
|
||||
```yaml
|
||||
extraction:
|
||||
javascript:
|
||||
index:
|
||||
filters:
|
||||
- include: "**/node_modules"
|
||||
- include: "**/bower_components"
|
||||
```
|
||||
|
||||
* Recognition of CommonJS modules has improved. As a result, some files that were previously extracted as
|
||||
global scripts are now extracted as modules.
|
||||
* Top-level `await` is now supported.
|
||||
|
||||
23
change-notes/1.24/analysis-javascript.md
Normal file
23
change-notes/1.24/analysis-javascript.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Improvements to JavaScript analysis
|
||||
|
||||
## General improvements
|
||||
|
||||
* Support for the following frameworks and libraries has been improved:
|
||||
- [react](https://www.npmjs.com/package/react)
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|---------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
|
||||
| Clear-text logging of sensitive information (`js/clear-text-logging`) | More results | More results involving `process.env` and indirect calls to logging methods are recognized. |
|
||||
| Incomplete string escaping or encoding (`js/incomplete-sanitization`) | Fewer false positive results | This query now recognizes additional cases where a single replacement is likely to be intentional. |
|
||||
| Unbound event handler receiver (`js/unbound-event-handler-receiver`) | Fewer false positive results | This query now recognizes additional ways event handler receivers can be bound. |
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Files moved to ``docs`` directory
|
||||
|
||||
Now that all of the QL documentation is in this repository,
|
||||
Now that all of the CodeQL documentation is in this repository,
|
||||
notes on the languages, compilers, and frameworks supported have moved.
|
||||
They're now stored as part of the Sphinx ``support`` project with the other documentation:
|
||||
``docs/language/support``.
|
||||
|
||||
@@ -21,6 +21,7 @@ from Variable v
|
||||
where
|
||||
v.isStatic() and
|
||||
v.hasDefinition() and
|
||||
not v.isConstexpr() and
|
||||
not exists(VariableAccess a | a.getTarget() = v) and
|
||||
not v instanceof MemberVariable and
|
||||
not declarationHasSideEffects(v) and
|
||||
|
||||
@@ -13,7 +13,7 @@ import semmle.code.cpp.pointsto.PointsTo
|
||||
import Negativity
|
||||
|
||||
predicate closeCall(FunctionCall fc, Variable v) {
|
||||
fc.getTarget().hasGlobalName("close") and v.getAnAccess() = fc.getArgument(0)
|
||||
fc.getTarget().hasGlobalOrStdName("close") and v.getAnAccess() = fc.getArgument(0)
|
||||
or
|
||||
exists(FunctionCall midcall, Function mid, int arg |
|
||||
fc.getArgument(arg) = v.getAnAccess() and
|
||||
|
||||
@@ -13,7 +13,7 @@ import semmle.code.cpp.pointsto.PointsTo
|
||||
|
||||
predicate closed(Expr e) {
|
||||
exists(FunctionCall fc |
|
||||
fc.getTarget().hasGlobalName("close") and
|
||||
fc.getTarget().hasGlobalOrStdName("close") and
|
||||
fc.getArgument(0) = e
|
||||
)
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ predicate allocCallOrIndirect(Expr e) {
|
||||
* can cause memory leaks.
|
||||
*/
|
||||
predicate verifiedRealloc(FunctionCall reallocCall, Variable v, ControlFlowNode verified) {
|
||||
reallocCall.getTarget().hasGlobalName("realloc") and
|
||||
reallocCall.getTarget().hasGlobalOrStdName("realloc") and
|
||||
reallocCall.getArgument(0) = v.getAnAccess() and
|
||||
(
|
||||
exists(Variable newV, ControlFlowNode node |
|
||||
@@ -79,7 +79,7 @@ predicate verifiedRealloc(FunctionCall reallocCall, Variable v, ControlFlowNode
|
||||
predicate freeCallOrIndirect(ControlFlowNode n, Variable v) {
|
||||
// direct free call
|
||||
freeCall(n, v.getAnAccess()) and
|
||||
not n.(FunctionCall).getTarget().hasGlobalName("realloc")
|
||||
not n.(FunctionCall).getTarget().hasGlobalOrStdName("realloc")
|
||||
or
|
||||
// verified realloc call
|
||||
verifiedRealloc(_, v, n)
|
||||
|
||||
@@ -13,10 +13,7 @@
|
||||
import cpp
|
||||
|
||||
class MallocCall extends FunctionCall {
|
||||
MallocCall() {
|
||||
this.getTarget().hasGlobalName("malloc") or
|
||||
this.getTarget().hasQualifiedName("std", "malloc")
|
||||
}
|
||||
MallocCall() { this.getTarget().hasGlobalOrStdName("malloc") }
|
||||
|
||||
Expr getAllocatedSize() {
|
||||
if this.getArgument(0) instanceof VariableAccess
|
||||
@@ -36,12 +33,12 @@ predicate spaceProblem(FunctionCall append, string msg) {
|
||||
malloc.getAllocatedSize() = add and
|
||||
buffer.getAnAccess() = strlen.getStringExpr() and
|
||||
(
|
||||
insert.getTarget().hasGlobalName("strcpy") or
|
||||
insert.getTarget().hasGlobalName("strncpy")
|
||||
insert.getTarget().hasGlobalOrStdName("strcpy") or
|
||||
insert.getTarget().hasGlobalOrStdName("strncpy")
|
||||
) and
|
||||
(
|
||||
append.getTarget().hasGlobalName("strcat") or
|
||||
append.getTarget().hasGlobalName("strncat")
|
||||
append.getTarget().hasGlobalOrStdName("strcat") or
|
||||
append.getTarget().hasGlobalOrStdName("strncat")
|
||||
) and
|
||||
malloc.getASuccessor+() = insert and
|
||||
insert.getArgument(1) = buffer.getAnAccess() and
|
||||
|
||||
@@ -25,7 +25,7 @@ import semmle.code.cpp.security.TaintTracking
|
||||
predicate sourceSized(FunctionCall fc, Expr src) {
|
||||
exists(string name |
|
||||
(name = "strncpy" or name = "strncat" or name = "memcpy" or name = "memmove") and
|
||||
fc.getTarget().hasGlobalName(name)
|
||||
fc.getTarget().hasGlobalOrStdName(name)
|
||||
) and
|
||||
exists(Expr dest, Expr size, Variable v |
|
||||
fc.getArgument(0) = dest and
|
||||
|
||||
@@ -60,19 +60,19 @@ predicate overflowOffsetInLoop(BufferAccess bufaccess, string msg) {
|
||||
predicate bufferAndSizeFunction(Function f, int buf, int size) {
|
||||
f.hasGlobalName("read") and buf = 1 and size = 2
|
||||
or
|
||||
f.hasGlobalName("fgets") and buf = 0 and size = 1
|
||||
f.hasGlobalOrStdName("fgets") and buf = 0 and size = 1
|
||||
or
|
||||
f.hasGlobalName("strncpy") and buf = 0 and size = 2
|
||||
f.hasGlobalOrStdName("strncpy") and buf = 0 and size = 2
|
||||
or
|
||||
f.hasGlobalName("strncat") and buf = 0 and size = 2
|
||||
f.hasGlobalOrStdName("strncat") and buf = 0 and size = 2
|
||||
or
|
||||
f.hasGlobalName("memcpy") and buf = 0 and size = 2
|
||||
f.hasGlobalOrStdName("memcpy") and buf = 0 and size = 2
|
||||
or
|
||||
f.hasGlobalName("memmove") and buf = 0 and size = 2
|
||||
f.hasGlobalOrStdName("memmove") and buf = 0 and size = 2
|
||||
or
|
||||
f.hasGlobalName("snprintf") and buf = 0 and size = 1
|
||||
f.hasGlobalOrStdName("snprintf") and buf = 0 and size = 1
|
||||
or
|
||||
f.hasGlobalName("vsnprintf") and buf = 0 and size = 1
|
||||
f.hasGlobalOrStdName("vsnprintf") and buf = 0 and size = 1
|
||||
}
|
||||
|
||||
class CallWithBufferSize extends FunctionCall {
|
||||
|
||||
@@ -17,12 +17,12 @@ import cpp
|
||||
class Allocation extends FunctionCall {
|
||||
Allocation() {
|
||||
exists(string name |
|
||||
this.getTarget().hasGlobalName(name) and
|
||||
this.getTarget().hasGlobalOrStdName(name) and
|
||||
(name = "malloc" or name = "calloc" or name = "realloc")
|
||||
)
|
||||
}
|
||||
|
||||
private string getName() { this.getTarget().hasGlobalName(result) }
|
||||
private string getName() { this.getTarget().hasGlobalOrStdName(result) }
|
||||
|
||||
int getSize() {
|
||||
this.getName() = "malloc" and
|
||||
|
||||
@@ -17,12 +17,12 @@ import cpp
|
||||
class Allocation extends FunctionCall {
|
||||
Allocation() {
|
||||
exists(string name |
|
||||
this.getTarget().hasGlobalName(name) and
|
||||
this.getTarget().hasGlobalOrStdName(name) and
|
||||
(name = "malloc" or name = "calloc" or name = "realloc")
|
||||
)
|
||||
}
|
||||
|
||||
private string getName() { this.getTarget().hasGlobalName(result) }
|
||||
private string getName() { this.getTarget().hasGlobalOrStdName(result) }
|
||||
|
||||
int getSize() {
|
||||
this.getName() = "malloc" and
|
||||
|
||||
@@ -16,7 +16,7 @@ import semmle.code.cpp.controlflow.LocalScopeVariableReachability
|
||||
predicate isFreeExpr(Expr e, LocalScopeVariable v) {
|
||||
exists(VariableAccess va | va.getTarget() = v |
|
||||
exists(FunctionCall fc | fc = e |
|
||||
fc.getTarget().hasGlobalName("free") and
|
||||
fc.getTarget().hasGlobalOrStdName("free") and
|
||||
va = fc.getArgument(0)
|
||||
)
|
||||
or
|
||||
|
||||
@@ -59,7 +59,7 @@ class Options extends string {
|
||||
predicate exits(Function f) {
|
||||
f.getAnAttribute().hasName("noreturn")
|
||||
or
|
||||
exists(string name | f.hasGlobalName(name) |
|
||||
exists(string name | f.hasGlobalOrStdName(name) |
|
||||
name = "exit" or
|
||||
name = "_exit" or
|
||||
name = "abort" or
|
||||
@@ -91,7 +91,7 @@ class Options extends string {
|
||||
* By default holds only for `fgets`.
|
||||
*/
|
||||
predicate alwaysCheckReturnValue(Function f) {
|
||||
f.hasGlobalName("fgets") or
|
||||
f.hasGlobalOrStdName("fgets") or
|
||||
CustomOptions::alwaysCheckReturnValue(f) // old Options.qll
|
||||
}
|
||||
|
||||
|
||||
@@ -13,17 +13,13 @@
|
||||
|
||||
import cpp
|
||||
import PointlessSelfComparison
|
||||
import semmle.code.cpp.commons.Exclusions
|
||||
|
||||
from ComparisonOperation cmp
|
||||
where
|
||||
pointlessSelfComparison(cmp) and
|
||||
not nanTest(cmp) and
|
||||
not overflowTest(cmp) and
|
||||
not exists(MacroInvocation mi |
|
||||
// cmp is in mi
|
||||
mi.getAnExpandedElement() = cmp and
|
||||
// and cmp was apparently not passed in as a macro parameter
|
||||
cmp.getLocation().getStartLine() = mi.getLocation().getStartLine() and
|
||||
cmp.getLocation().getStartColumn() = mi.getLocation().getStartColumn()
|
||||
)
|
||||
not cmp.isFromTemplateInstantiation(_) and
|
||||
not isFromMacroDefinition(cmp)
|
||||
select cmp, "Self comparison."
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
bool foo(int n1, unsigned short delta) {
|
||||
return n1 + delta < n1; // BAD
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
bool bar(unsigned short n1, unsigned short delta) {
|
||||
// NB: Comparison is always false
|
||||
return n1 + delta < n1; // GOOD (but misleading)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#include <limits.h>
|
||||
bool foo(int n1, unsigned short delta) {
|
||||
return n1 > INT_MAX - delta; // GOOD
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
bool bar(unsigned short n1, unsigned short delta) {
|
||||
return (unsigned short)(n1 + delta) < n1; // GOOD
|
||||
}
|
||||
115
cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.qhelp
Normal file
115
cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.qhelp
Normal file
@@ -0,0 +1,115 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
When checking for integer overflow, you may often write tests like
|
||||
<code>a + b < a</code>. This works fine if <code>a</code> or
|
||||
<code>b</code> are unsigned integers, since any overflow in the addition
|
||||
will cause the value to simply "wrap around." However, using
|
||||
<i>signed</i> integers is problematic because signed overflow has undefined
|
||||
behavior according to the C and C++ standards. If the addition overflows
|
||||
and has an undefined result, the comparison will likewise be undefined;
|
||||
it may produce an unintended result, or may be deleted entirely by an
|
||||
optimizing compiler.
|
||||
</p>
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>
|
||||
Solutions to this problem can be thought of as falling into one of two
|
||||
categories: (1) rewrite the signed expression so that overflow cannot occur
|
||||
but the signedness remains, or (2) rewrite (or cast) the signed expression
|
||||
into unsigned form.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Below we list examples of expressions where signed overflow may
|
||||
occur, along with proposed solutions. The list should not be
|
||||
considered exhaustive.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Given <code>unsigned short i, delta</code> and <code>i + delta < i</code>,
|
||||
it is possible to rewrite it as <code>(unsigned short)(i + delta) < i</code>.
|
||||
Note that <code>i + delta</code>does not actually overflow, due to <code>int</code> promotion
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Given <code>unsigned short i, delta</code> and <code>i + delta < i</code>,
|
||||
it is also possible to rewrite it as <code>USHORT_MAX - delta</code>. It must be true
|
||||
that <code>delta > 0</code> and the <code>limits.h</code> or <code>climits</code>
|
||||
header has been included.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Given <code>int i, delta</code> and <code>i + delta < i</code>,
|
||||
it is possible to rewrite it as <code>INT_MAX - delta</code>. It must be true
|
||||
that <code>delta > 0</code> and the <code>limits.h</code> or <code>climits</code>
|
||||
header has been included.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Given <code>int i, delta</code> and <code>i + delta < i</code>,
|
||||
it is also possible to rewrite it as <code>(unsigned)i + delta < i</code>.
|
||||
Note that program semantics are affected by this change.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Given <code>int i, delta</code> and <code>i + delta < i</code>,
|
||||
it is also possible to rewrite it as <code>unsigned int i, delta</code> and
|
||||
<code>i + delta < i</code>. Note that program semantics are
|
||||
affected by this change.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
In the following example, even though <code>delta</code> has been declared
|
||||
<code>unsigned short</code>, C/C++ type promotion rules require that its
|
||||
type is promoted to the larger type used in the addition and comparison,
|
||||
namely a <code>signed int</code>. Addition is performed on
|
||||
signed integers, and may have undefined behavior if an overflow occurs.
|
||||
As a result, the entire (comparison) expression may also have an undefined
|
||||
result.
|
||||
</p>
|
||||
<sample src="SignedOverflowCheck-bad1.cpp" />
|
||||
<p>
|
||||
The following example builds upon the previous one. Instead of
|
||||
performing an addition (which could overflow), we have re-framed the
|
||||
solution so that a subtraction is used instead. Since <code>delta</code>
|
||||
is promoted to a <code>signed int</code> and <code>INT_MAX</code> denotes
|
||||
the largest possible positive value for an <code>signed int</code>,
|
||||
the expression <code>INT_MAX - delta</code> can never be less than zero
|
||||
or more than <code>INT_MAX</code>. Hence, any overflow and underflow
|
||||
are avoided.
|
||||
</p>
|
||||
<sample src="SignedOverflowCheck-good1.cpp" />
|
||||
<p>
|
||||
In the following example, even though both <code>n</code> and <code>delta</code>
|
||||
have been declared <code>unsigned short</code>, both are promoted to
|
||||
<code>signed int</code> prior to addition. Because we started out with the
|
||||
narrower <code>short</code> type, the addition is guaranteed not to overflow
|
||||
and is therefore defined. But the fact that <code>n1 + delta</code> never
|
||||
overflows means that the condition <code>n1 + delta < n1</code> will never
|
||||
hold true, which likely is not what the programmer intended. (see also the
|
||||
<code>cpp/bad-addition-overflow-check</code> query).
|
||||
</p>
|
||||
<sample src="SignedOverflowCheck-bad2.cpp" />
|
||||
<p>
|
||||
The next example provides a solution to the previous one. Even though
|
||||
<code>i + delta</code> does not overflow, casting it to an
|
||||
<code>unsigned short</code> truncates the addition modulo 2^16,
|
||||
so that <code>unsigned short</code> "wrap around" may now be observed.
|
||||
Furthermore, since the left-hand side is now of type <code>unsigned short</code>,
|
||||
the right-hand side does not need to be promoted to a <code>signed int</code>.
|
||||
</p>
|
||||
|
||||
<sample src="SignedOverflowCheck-good2.cpp" />
|
||||
</example>
|
||||
<references>
|
||||
<li><a href="http://c-faq.com/expr/preservingrules.html">comp.lang.c FAQ list · Question 3.19 (Preserving rules)</a></li>
|
||||
<li><a href="https://wiki.sei.cmu.edu/confluence/display/c/INT31-C.+Ensure+that+integer+conversions+do+not+result+in+lost+or+misinterpreted+data">INT31-C. Ensure that integer conversions do not result in lost or misinterpreted data</a></li>
|
||||
<li>W. Dietz, P. Li, J. Regehr, V. Adve. <a href="https://www.cs.utah.edu/~regehr/papers/overflow12.pdf">Understanding Integer Overflow in C/C++</a></li>
|
||||
</references>
|
||||
</qhelp>
|
||||
31
cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.ql
Normal file
31
cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.ql
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* @name Signed overflow check
|
||||
* @description Testing for overflow by adding a value to a variable
|
||||
* to see if it "wraps around" works only for
|
||||
* unsigned integer values.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @id cpp/signed-overflow-check
|
||||
* @tags correctness
|
||||
* security
|
||||
*/
|
||||
|
||||
import cpp
|
||||
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
private import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
|
||||
from RelationalOperation ro, AddExpr add, Expr expr1, Expr expr2
|
||||
where
|
||||
ro.getAnOperand() = add and
|
||||
add.getAnOperand() = expr1 and
|
||||
ro.getAnOperand() = expr2 and
|
||||
globalValueNumber(expr1) = globalValueNumber(expr2) and
|
||||
add.getUnspecifiedType().(IntegralType).isSigned() and
|
||||
not exists(MacroInvocation mi | mi.getAnAffectedElement() = add) and
|
||||
exprMightOverflowPositively(add) and
|
||||
exists(Compilation c | c.getAFileCompiled() = ro.getFile() |
|
||||
not c.getAnArgument() = "-fwrapv" and
|
||||
not c.getAnArgument() = "-fno-strict-overflow"
|
||||
)
|
||||
select ro, "Testing for signed overflow may produce undefined results."
|
||||
@@ -15,7 +15,10 @@ import cpp
|
||||
|
||||
from ExprInVoidContext op
|
||||
where
|
||||
op instanceof EQExpr
|
||||
or
|
||||
op.(FunctionCall).getTarget().hasName("operator==")
|
||||
not op.isUnevaluated() and
|
||||
(
|
||||
op instanceof EQExpr
|
||||
or
|
||||
op.(FunctionCall).getTarget().hasName("operator==")
|
||||
)
|
||||
select op, "This '==' operator has no effect. The assignment ('=') operator was probably intended."
|
||||
|
||||
@@ -84,8 +84,10 @@ where
|
||||
not peivc.getEnclosingFunction().isDefaulted() and
|
||||
not exists(Macro m | peivc = m.getAnInvocation().getAnExpandedElement()) and
|
||||
not peivc.isFromTemplateInstantiation(_) and
|
||||
not peivc.isFromUninstantiatedTemplate(_) and
|
||||
parent = peivc.getParent() and
|
||||
not parent.isInMacroExpansion() and
|
||||
not peivc.isUnevaluated() and
|
||||
not parent instanceof PureExprInVoidContext and
|
||||
not peivc.getEnclosingFunction().isCompilerGenerated() and
|
||||
not peivc.getType() instanceof UnknownType and
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
import BufferAccess
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
class NetworkFunctionCall extends FunctionCall {
|
||||
NetworkFunctionCall() {
|
||||
getTarget().hasName("ntohd") or
|
||||
getTarget().hasName("ntohf") or
|
||||
getTarget().hasName("ntohl") or
|
||||
getTarget().hasName("ntohll") or
|
||||
getTarget().hasName("ntohs")
|
||||
}
|
||||
}
|
||||
|
||||
class NetworkToBufferSizeConfiguration extends DataFlow::Configuration {
|
||||
NetworkToBufferSizeConfiguration() { this = "NetworkToBufferSizeConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof NetworkFunctionCall }
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
node.asExpr() = any(BufferAccess ba).getAccessedLength()
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node) {
|
||||
exists(GuardCondition gc, GVN gvn |
|
||||
gc.getAChild*() = gvn.getAnExpr() and
|
||||
globalValueNumber(node.asExpr()) = gvn and
|
||||
gc.controls(node.asExpr().getBasicBlock(), _)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ byte order function, such as <code>ntohl</code>.
|
||||
The use of a network-to-host byte order function is therefore a good indicator that the returned
|
||||
value is unvalidated data retrieved from the network, and should not be used without further
|
||||
validation. In particular, the returned value should not be used as an array index or array length
|
||||
value without validation, which may result in a buffer overflow vulnerability.
|
||||
value without validation, as this could result in a buffer overflow vulnerability.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
@@ -31,10 +31,10 @@ it to host byte order. The data is then used as an index in an array access expr
|
||||
there is no validation that the data returned by <code>ntohl</code> is within the bounds of the array,
|
||||
which could lead to reading outside the bounds of the buffer.
|
||||
</p>
|
||||
<sample src="NtohlArrayBad.cpp" />
|
||||
<sample src="NtohlArrayNoBound-bad.cpp" />
|
||||
<p>In the corrected example, the returned data is validated against the known size of the buffer,
|
||||
before being used as an array index.</p>
|
||||
<sample src="NtohlArrayGood.cpp" />
|
||||
<sample src="NtohlArrayNoBound-good.cpp" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
@@ -1,55 +1,7 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
private import semmle.code.cpp.dataflow.RecursionPrevention
|
||||
|
||||
/**
|
||||
* A buffer which includes an allocation size.
|
||||
*/
|
||||
abstract class BufferWithSize extends DataFlow::Node {
|
||||
abstract Expr getSizeExpr();
|
||||
|
||||
BufferAccess getAnAccess() {
|
||||
any(BufferWithSizeConfig bsc).hasFlow(this, DataFlow::exprNode(result.getPointer()))
|
||||
}
|
||||
}
|
||||
|
||||
/** An allocation function. */
|
||||
abstract class Alloc extends Function { }
|
||||
|
||||
/**
|
||||
* Allocation functions identified by the QL for C/C++ standard library.
|
||||
*/
|
||||
class DefaultAlloc extends Alloc {
|
||||
DefaultAlloc() { allocationFunction(this) }
|
||||
}
|
||||
|
||||
/** A buffer created through a call to an allocation function. */
|
||||
class AllocBuffer extends BufferWithSize {
|
||||
FunctionCall call;
|
||||
|
||||
AllocBuffer() {
|
||||
asExpr() = call and
|
||||
call.getTarget() instanceof Alloc
|
||||
}
|
||||
|
||||
override Expr getSizeExpr() { result = call.getArgument(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Find accesses of buffers for which we have a size expression.
|
||||
*/
|
||||
private class BufferWithSizeConfig extends TaintTracking::Configuration {
|
||||
BufferWithSizeConfig() { this = "BufferWithSize" }
|
||||
|
||||
override predicate isSource(DataFlow::Node n) { n = any(BufferWithSize b) }
|
||||
|
||||
override predicate isSink(DataFlow::Node n) { n.asExpr() = any(BufferAccess ae).getPointer() }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node s) {
|
||||
s = any(BufferWithSize b) and
|
||||
s.asExpr().getControlFlowScope() instanceof Alloc
|
||||
}
|
||||
}
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
/**
|
||||
* An access (read or write) to a buffer, provided as a pair of
|
||||
@@ -172,3 +124,31 @@ class MallocSizeExpr extends BufferAccess, FunctionCall {
|
||||
|
||||
override Expr getAccessedLength() { result = getArgument(1) }
|
||||
}
|
||||
|
||||
class NetworkFunctionCall extends FunctionCall {
|
||||
NetworkFunctionCall() {
|
||||
getTarget().hasName("ntohd") or
|
||||
getTarget().hasName("ntohf") or
|
||||
getTarget().hasName("ntohl") or
|
||||
getTarget().hasName("ntohll") or
|
||||
getTarget().hasName("ntohs")
|
||||
}
|
||||
}
|
||||
|
||||
class NetworkToBufferSizeConfiguration extends DataFlow::Configuration {
|
||||
NetworkToBufferSizeConfiguration() { this = "NetworkToBufferSizeConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof NetworkFunctionCall }
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
node.asExpr() = any(BufferAccess ba).getAccessedLength()
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node) {
|
||||
exists(GuardCondition gc, GVN gvn |
|
||||
gc.getAChild*() = gvn.getAnExpr() and
|
||||
globalValueNumber(node.asExpr()) = gvn and
|
||||
gc.controls(node.asExpr().getBasicBlock(), _)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
bool not_in_range(T *ptr, T *ptr_end, size_t i) {
|
||||
return ptr + i >= ptr_end || ptr + i < ptr; // BAD
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
bool not_in_range(T *ptr, T *ptr_end, size_t i) {
|
||||
return i >= ptr_end - ptr; // GOOD
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
When checking for integer overflow, you may often write tests like
|
||||
<code>p + i < p</code>. This works fine if <code>p</code> and
|
||||
<code>i</code> are unsigned integers, since any overflow in the addition
|
||||
will cause the value to simply "wrap around." However, using this pattern when
|
||||
<code>p</code> is a pointer is problematic because pointer overflow has
|
||||
undefined behavior according to the C and C++ standards. If the addition
|
||||
overflows and has an undefined result, the comparison will likewise be
|
||||
undefined; it may produce an unintended result, or may be deleted entirely by an
|
||||
optimizing compiler.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>
|
||||
To check whether an index <code>i</code> is less than the length of an array,
|
||||
simply compare these two numbers as unsigned integers: <code>i < ARRAY_LENGTH</code>.
|
||||
If the length of the array is defined as the difference between two pointers
|
||||
<code>ptr</code> and <code>p_end</code>, write <code>i < p_end - ptr</code>.
|
||||
If <code>i</code> is signed, cast it to unsigned
|
||||
in order to guard against negative <code>i</code>. For example, write
|
||||
<code>(size_t)i < p_end - ptr</code>.
|
||||
</p>
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>
|
||||
An invalid check for pointer overflow is most often seen as part of checking
|
||||
whether a number <code>a</code> is too large by checking first if adding the
|
||||
number to <code>ptr</code> goes past the end of an allocation and then
|
||||
checking if adding it to <code>ptr</code> creates a pointer so large that it
|
||||
overflows and wraps around.
|
||||
</p>
|
||||
|
||||
<sample src="PointerOverflow-bad.cpp" />
|
||||
|
||||
<p>
|
||||
In both of these checks, the operations are performed in the wrong order.
|
||||
First, an expression that may cause undefined behavior is evaluated
|
||||
(<code>ptr + i</code>), and then the result is checked for being in range.
|
||||
But once undefined behavior has happened in the pointer addition, it cannot
|
||||
be recovered from: it's too late to perform the range check after a possible
|
||||
pointer overflow.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
While it's not the subject of this query, the expression <code>ptr + i <
|
||||
ptr_end</code> is also an invalid range check. It's undefined behavor in
|
||||
C/C++ to create a pointer that points more than one past the end of an
|
||||
allocation.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The next example shows how to portably check whether an unsigned number is outside the
|
||||
range of an allocation between <code>ptr</code> and <code>ptr_end</code>.
|
||||
</p>
|
||||
<sample src="PointerOverflow-good.cpp" />
|
||||
</example>
|
||||
<references>
|
||||
<li>Embedded in Academia: <a href="https://blog.regehr.org/archives/1395">Pointer Overflow Checking</a>.</li>
|
||||
<li>LWN: <a href="https://lwn.net/Articles/278137/">GCC and pointer overflows</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
31
cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow.ql
Normal file
31
cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow.ql
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* @name Pointer overflow check
|
||||
* @description Adding a value to a pointer to check if it overflows relies
|
||||
* on undefined behavior and may lead to memory corruption.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id cpp/pointer-overflow-check
|
||||
* @tags reliability
|
||||
* security
|
||||
*/
|
||||
|
||||
import cpp
|
||||
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
private import semmle.code.cpp.commons.Exclusions
|
||||
|
||||
from RelationalOperation ro, PointerAddExpr add, Expr expr1, Expr expr2
|
||||
where
|
||||
ro.getAnOperand() = add and
|
||||
add.getAnOperand() = expr1 and
|
||||
ro.getAnOperand() = expr2 and
|
||||
globalValueNumber(expr1) = globalValueNumber(expr2) and
|
||||
// Exclude macros but not their arguments
|
||||
not isFromMacroDefinition(ro) and
|
||||
// There must be a compilation of this file without a flag that makes pointer
|
||||
// overflow well defined.
|
||||
exists(Compilation c | c.getAFileCompiled() = ro.getFile() |
|
||||
not c.getAnArgument() = "-fwrapv-pointer" and
|
||||
not c.getAnArgument() = "-fno-strict-overflow"
|
||||
)
|
||||
select ro, "Range check relying on pointer overflow."
|
||||
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using the TLS or SSLv23 protocol from the boost::asio library, but not disabling deprecated protocols may expose the software to known vulnerabilities or permit weak encryption algorithms to be used. Disabling the minimum-recommended protocols is also flagged.</p>
|
||||
</overview>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
<a href="https://www.boost.org/doc/libs/1_71_0/doc/html/boost_asio.html">Boost.Asio documentation</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* @name Boost_asio TLS Settings Misconfiguration
|
||||
* @description Using the TLS or SSLv23 protocol from the boost::asio library, but not disabling deprecated protocols, or disabling minimum-recommended protocols.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @id cpp/boost/tls_settings_misconfiguration
|
||||
* @tags security
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.boostorg.asio.protocols
|
||||
|
||||
class ExistsAnyFlowConfig extends DataFlow::Configuration {
|
||||
ExistsAnyFlowConfig() { this = "ExistsAnyFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(BoostorgAsio::SslContextClass c | c.getAContructorCall() = source.asExpr())
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(BoostorgAsio::SslSetOptionsFunction f, FunctionCall fcSetOptions |
|
||||
f.getACallToThisFunction() = fcSetOptions and
|
||||
fcSetOptions.getQualifier() = sink.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
bindingset[flag]
|
||||
predicate isOptionSet(ConstructorCall cc, int flag, FunctionCall fcSetOptions) {
|
||||
exists(ExistsAnyFlowConfig anyFlowConfig, VariableAccess contextSetOptions |
|
||||
anyFlowConfig.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(contextSetOptions)) and
|
||||
exists(BoostorgAsio::SslSetOptionsFunction f | f.getACallToThisFunction() = fcSetOptions |
|
||||
contextSetOptions = fcSetOptions.getQualifier() and
|
||||
forall(
|
||||
Expr optionArgument, BoostorgAsio::SslOptionConfig optionArgConfig,
|
||||
Expr optionArgumentSource
|
||||
|
|
||||
optionArgument = fcSetOptions.getArgument(0) and
|
||||
optionArgConfig
|
||||
.hasFlow(DataFlow::exprNode(optionArgumentSource), DataFlow::exprNode(optionArgument))
|
||||
|
|
||||
optionArgument.getValue().toInt().bitShiftRight(16).bitAnd(flag) = flag
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[flag]
|
||||
predicate isOptionNotSet(ConstructorCall cc, int flag) {
|
||||
not exists(FunctionCall fcSetOptions | isOptionSet(cc, flag, fcSetOptions))
|
||||
}
|
||||
|
||||
from
|
||||
BoostorgAsio::SslContextCallTlsProtocolConfig configConstructor, Expr protocolSource,
|
||||
Expr protocolSink, ConstructorCall cc, Expr e, string msg
|
||||
where
|
||||
configConstructor.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink)) and
|
||||
cc.getArgument(0) = protocolSink and
|
||||
(
|
||||
BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
|
||||
not (
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoSsl3(), _) and
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1(), _) and
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1(), _) and
|
||||
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2())
|
||||
)
|
||||
or
|
||||
BoostorgAsio::isExprTlsBoostProtocol(protocolSource) and
|
||||
not BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
|
||||
not (
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1(), _) and
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1(), _) and
|
||||
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2())
|
||||
)
|
||||
) and
|
||||
(
|
||||
BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
|
||||
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoSsl3()) and
|
||||
e = cc and
|
||||
msg = "no_sslv3 has not been set"
|
||||
or
|
||||
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1()) and
|
||||
e = cc and
|
||||
msg = "no_tlsv1 has not been set"
|
||||
or
|
||||
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1()) and
|
||||
e = cc and
|
||||
msg = "no_tlsv1_1 has not been set"
|
||||
or
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2(), e) and
|
||||
msg = "no_tlsv1_2 was set"
|
||||
)
|
||||
select cc, "Usage of $@ with protocol $@ is not configured correctly: The option $@.", cc,
|
||||
"boost::asio::ssl::context::context", protocolSource, protocolSource.toString(), e, msg
|
||||
@@ -1,15 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using TLS or SSLv23 protool from the boost::asio library, but not disabling deprecated protocols or disabling minimum-recommended protocols.</p>
|
||||
</overview>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
<a href="https://www.boost.org/doc/libs/1_71_0/doc/html/boost_asio.html">Boost.Asio documentation</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
/**
|
||||
* @name Boost_asio TLS Settings Misconfiguration
|
||||
* @description Using TLS or SSLv23 protool from the boost::asio library, but not disabling deprecated protocols or disabling minimum-recommended protocols
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @id cpp/boost/tls_settings_misconfiguration
|
||||
* @tags security
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.boostorg.asio.protocols
|
||||
|
||||
class ExistsAnyFlowConfig extends DataFlow::Configuration {
|
||||
ExistsAnyFlowConfig() { this = "ExistsAnyFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { any() }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { any() }
|
||||
}
|
||||
|
||||
bindingset[flag]
|
||||
predicate isOptionSet(ConstructorCall cc, int flag, FunctionCall fcSetOptions) {
|
||||
exists(
|
||||
BoostorgAsio::SslContextFlowsToSetOptionConfig config, ExistsAnyFlowConfig testConfig,
|
||||
Expr optionsSink
|
||||
|
|
||||
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
|
||||
exists(VariableAccess contextSetOptions |
|
||||
testConfig.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(contextSetOptions)) and
|
||||
exists(BoostorgAsio::SslSetOptionsFunction f | f.getACallToThisFunction() = fcSetOptions |
|
||||
contextSetOptions = fcSetOptions.getQualifier() and
|
||||
forall(
|
||||
Expr optionArgument, BoostorgAsio::SslOptionConfig optionArgConfig,
|
||||
Expr optionArgumentSource
|
||||
|
|
||||
optionArgument = fcSetOptions.getArgument(0) and
|
||||
optionArgConfig
|
||||
.hasFlow(DataFlow::exprNode(optionArgumentSource), DataFlow::exprNode(optionArgument))
|
||||
|
|
||||
optionArgument.getValue().toInt().bitShiftRight(16).bitAnd(flag) = flag
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[flag]
|
||||
predicate isOptionNotSet(ConstructorCall cc, int flag) {
|
||||
not exists(
|
||||
BoostorgAsio::SslContextFlowsToSetOptionConfig config, ExistsAnyFlowConfig testConfig,
|
||||
Expr optionsSink
|
||||
|
|
||||
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
|
||||
exists(VariableAccess contextSetOptions |
|
||||
testConfig.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(contextSetOptions)) and
|
||||
exists(FunctionCall fcSetOptions, BoostorgAsio::SslSetOptionsFunction f |
|
||||
f.getACallToThisFunction() = fcSetOptions
|
||||
|
|
||||
contextSetOptions = fcSetOptions.getQualifier() and
|
||||
forall(
|
||||
Expr optionArgument, BoostorgAsio::SslOptionConfig optionArgConfig,
|
||||
Expr optionArgumentSource
|
||||
|
|
||||
optionArgument = fcSetOptions.getArgument(0) and
|
||||
optionArgConfig
|
||||
.hasFlow(DataFlow::exprNode(optionArgumentSource), DataFlow::exprNode(optionArgument))
|
||||
|
|
||||
optionArgument.getValue().toInt().bitShiftRight(16).bitAnd(flag) = flag
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
from
|
||||
BoostorgAsio::SslContextCallTlsProtocolConfig configConstructor,
|
||||
BoostorgAsio::SslContextFlowsToSetOptionConfig config, Expr protocolSource, Expr protocolSink,
|
||||
ConstructorCall cc, Expr e, string msg
|
||||
where
|
||||
configConstructor.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink)) and
|
||||
cc.getArgument(0) = protocolSink and
|
||||
(
|
||||
BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
|
||||
not exists(Expr optionsSink |
|
||||
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoSsl3(), _) and
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1(), _) and
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1(), _) and
|
||||
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2())
|
||||
)
|
||||
or
|
||||
BoostorgAsio::isExprTlsBoostProtocol(protocolSource) and
|
||||
not BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
|
||||
not exists(Expr optionsSink |
|
||||
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1(), _) and
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1(), _) and
|
||||
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2())
|
||||
)
|
||||
) and
|
||||
(
|
||||
BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
|
||||
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoSsl3()) and
|
||||
e = cc and
|
||||
msg = "no_sslv3 has not been set"
|
||||
or
|
||||
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1()) and
|
||||
e = cc and
|
||||
msg = "no_tlsv1 has not been set"
|
||||
or
|
||||
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1()) and
|
||||
e = cc and
|
||||
msg = "no_tlsv1_1 has not been set"
|
||||
or
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2(), e) and
|
||||
msg = "no_tlsv1_2 was set"
|
||||
)
|
||||
select cc, "Usage of $@ with protocol $@ is not configured correctly: The option $@.", cc,
|
||||
"boost::asio::ssl::context::context", protocolSource, protocolSource.toString(), e, msg
|
||||
@@ -7,6 +7,10 @@
|
||||
* @id cpp/ignore-return-value-sal
|
||||
* @problem.severity warning
|
||||
* @tags reliability
|
||||
* external/cwe/cwe-573
|
||||
* external/cwe/cwe-252
|
||||
* @opaque-id SM02344
|
||||
* @microsoft.severity Important
|
||||
*/
|
||||
|
||||
import SAL
|
||||
|
||||
@@ -126,7 +126,7 @@ class SALParameter extends Parameter {
|
||||
}
|
||||
|
||||
/**
|
||||
* A SAL element, i.e. a SAL annotation or a declaration entry
|
||||
* A SAL element, that is, a SAL annotation or a declaration entry
|
||||
* that may have SAL annotations.
|
||||
*/
|
||||
library class SALElement extends Element {
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* @name SAL requires inspecting return value
|
||||
* @description When a return value is discarded even though the SAL annotation
|
||||
* requires inspecting it, a recoverable error may turn into a
|
||||
* whole-program crash.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @tags reliability
|
||||
* external/cwe/cwe-573
|
||||
* external/cwe/cwe-252
|
||||
* @opaque-id SM02344
|
||||
* @microsoft.severity Important
|
||||
* @id cpp/ignorereturnvaluesal
|
||||
*/
|
||||
|
||||
import Microsoft.SAL
|
||||
|
||||
from Function f, FunctionCall call
|
||||
where
|
||||
call.getTarget() = f and
|
||||
call instanceof ExprInVoidContext and
|
||||
any(SALCheckReturn a).getDeclaration() = f and
|
||||
not any(Options o).okToIgnoreReturnValue(call)
|
||||
select call, "Return value of $@ discarded although a SAL annotation " + "requires inspecting it.",
|
||||
f, f.getName()
|
||||
@@ -34,8 +34,10 @@ class FileFunction extends FunctionWithWrappers {
|
||||
nme.matches("CreateFile%")
|
||||
)
|
||||
or
|
||||
this.hasQualifiedName("std", "fopen")
|
||||
or
|
||||
// on any of the fstream classes, or filebuf
|
||||
exists(string nme | this.getDeclaringType().getSimpleName() = nme |
|
||||
exists(string nme | this.getDeclaringType().hasQualifiedName("std", nme) |
|
||||
nme = "basic_fstream" or
|
||||
nme = "basic_ifstream" or
|
||||
nme = "basic_ofstream" or
|
||||
|
||||
@@ -34,7 +34,7 @@ characters before writing to the HTML page.</p>
|
||||
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet">XSS
|
||||
<a href="https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html">XSS
|
||||
(Cross Site Scripting) Prevention Cheat Sheet</a>.
|
||||
</li>
|
||||
<li>
|
||||
|
||||
@@ -17,8 +17,8 @@ import semmle.code.cpp.security.TaintTracking
|
||||
/** A call that prints its arguments to `stdout`. */
|
||||
class PrintStdoutCall extends FunctionCall {
|
||||
PrintStdoutCall() {
|
||||
getTarget().hasGlobalName("puts") or
|
||||
getTarget().hasGlobalName("printf")
|
||||
getTarget().hasGlobalOrStdName("puts") or
|
||||
getTarget().hasGlobalOrStdName("printf")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,7 @@ import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.models.implementations.Memcpy
|
||||
|
||||
class MallocCall extends FunctionCall {
|
||||
MallocCall() {
|
||||
this.getTarget().hasGlobalName("malloc") or
|
||||
this.getTarget().hasQualifiedName("std", "malloc")
|
||||
}
|
||||
MallocCall() { this.getTarget().hasGlobalOrStdName("malloc") }
|
||||
|
||||
Expr getAllocatedSize() {
|
||||
if this.getArgument(0) instanceof VariableAccess
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* @id cpp/comparison-with-wider-type
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @precision high
|
||||
* @tags reliability
|
||||
* security
|
||||
* external/cwe/cwe-190
|
||||
|
||||
@@ -37,7 +37,7 @@ which is then subsequently accessed to fetch properties of the device. However,
|
||||
check the return value from the function call to <code>initDeviceConfig</code>. If the
|
||||
device number passed to the <code>notify</code> function was invalid, the
|
||||
<code>initDeviceConfig</code> function will leave the <code>config</code> variable uninitialized,
|
||||
which would result in the <code>notify</code> function accessing uninitialized memory.</p>
|
||||
which will result in the <code>notify</code> function accessing uninitialized memory.</p>
|
||||
|
||||
<sample src="ConditionallyUninitializedVariableBad.c" />
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @name Conditionally uninitialized variable
|
||||
* @description When an initialization function is used to initialize a local variable, but the
|
||||
* returned status code is not checked, the variable may be left in an uninitialized
|
||||
* state, and reading the variable may result in undefined behaviour.
|
||||
* state, and reading the variable may result in undefined behavior.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @opaque-id SM02313
|
||||
|
||||
@@ -19,7 +19,7 @@ int notify(int deviceNumber) {
|
||||
DeviceConfig config;
|
||||
initDeviceConfig(&config, deviceNumber);
|
||||
// BAD: Using config without checking the status code that is returned
|
||||
if (config->isEnabled) {
|
||||
notifyChannel(config->channel);
|
||||
if (config.isEnabled) {
|
||||
notifyChannel(config.channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ void notify(int deviceNumber) {
|
||||
int statusCode = initDeviceConfig(&config, deviceNumber);
|
||||
if (statusCode == 0) {
|
||||
// GOOD: Status code returned by initialization function is checked, so this is safe
|
||||
if (config->isEnabled) {
|
||||
notifyChannel(config->channel);
|
||||
if (config.isEnabled) {
|
||||
notifyChannel(config.channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import cpp
|
||||
import external.ExternalArtifact
|
||||
private import semmle.code.cpp.dispatch.VirtualDispatch
|
||||
private import semmle.code.cpp.dispatch.VirtualDispatchPrototype
|
||||
import semmle.code.cpp.NestedFields
|
||||
import Microsoft.SAL
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
@@ -89,9 +89,9 @@ class ParameterNullCheck extends ParameterCheck {
|
||||
(
|
||||
va = this.(NotExpr).getOperand() or
|
||||
va = any(EQExpr eq | eq = this and eq.getAnOperand().getValue() = "0").getAnOperand() or
|
||||
va = getAssertedFalseCondition(this) or
|
||||
va = getCheckedFalseCondition(this) or
|
||||
va = any(NEExpr eq |
|
||||
eq = getAssertedFalseCondition(this) and eq.getAnOperand().getValue() = "0"
|
||||
eq = getCheckedFalseCondition(this) and eq.getAnOperand().getValue() = "0"
|
||||
).getAnOperand()
|
||||
)
|
||||
or
|
||||
@@ -101,7 +101,7 @@ class ParameterNullCheck extends ParameterCheck {
|
||||
va = this or
|
||||
va = any(NEExpr eq | eq = this and eq.getAnOperand().getValue() = "0").getAnOperand() or
|
||||
va = any(EQExpr eq |
|
||||
eq = getAssertedFalseCondition(this) and eq.getAnOperand().getValue() = "0"
|
||||
eq = getCheckedFalseCondition(this) and eq.getAnOperand().getValue() = "0"
|
||||
).getAnOperand()
|
||||
)
|
||||
)
|
||||
@@ -567,7 +567,7 @@ Expr getAnInitializedArgument(Call call) { result = call.getArgument(initialized
|
||||
* the call, under the given context and evidence.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
int conditionallyInitializedArgument(
|
||||
private int conditionallyInitializedArgument(
|
||||
Call call, ConditionalInitializationFunction target, Context c, Evidence e
|
||||
) {
|
||||
target = getTarget(call) and
|
||||
@@ -588,7 +588,7 @@ Expr getAConditionallyInitializedArgument(
|
||||
/**
|
||||
* Gets the type signature for the functions parameters.
|
||||
*/
|
||||
string typeSig(Function f) {
|
||||
private string typeSig(Function f) {
|
||||
result = concat(int i, Type pt |
|
||||
pt = f.getParameter(i).getType()
|
||||
|
|
||||
@@ -599,7 +599,7 @@ string typeSig(Function f) {
|
||||
/**
|
||||
* Holds where qualifiedName and typeSig make up the signature for the function.
|
||||
*/
|
||||
predicate functionSignature(Function f, string qualifiedName, string typeSig) {
|
||||
private predicate functionSignature(Function f, string qualifiedName, string typeSig) {
|
||||
qualifiedName = f.getQualifiedName() and
|
||||
typeSig = typeSig(f)
|
||||
}
|
||||
@@ -611,7 +611,7 @@ predicate functionSignature(Function f, string qualifiedName, string typeSig) {
|
||||
* This is useful for identifying call to target dependencies across libraries, where the libraries
|
||||
* are never statically linked together.
|
||||
*/
|
||||
Function getAPossibleDefinition(Function undefinedFunction) {
|
||||
private Function getAPossibleDefinition(Function undefinedFunction) {
|
||||
not undefinedFunction.isDefined() and
|
||||
exists(string qn, string typeSig |
|
||||
functionSignature(undefinedFunction, qn, typeSig) and functionSignature(result, qn, typeSig)
|
||||
@@ -620,32 +620,47 @@ Function getAPossibleDefinition(Function undefinedFunction) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible target for the Call, using the name and parameter matching if we did not associate
|
||||
* Helper predicate for `getTarget`, that computes possible targets of a `Call`.
|
||||
*
|
||||
* If there is at least one defined target after performing some simple virtual dispatch
|
||||
* resolution, then the result is all the defined targets.
|
||||
*/
|
||||
private Function getTarget1(Call c) {
|
||||
result = VirtualDispatch::getAViableTarget(c) and
|
||||
result.isDefined()
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper predicate for `getTarget`, that computes possible targets of a `Call`.
|
||||
*
|
||||
* If we can use the heuristic matching of functions to find definitions for some of the viable
|
||||
* targets, return those.
|
||||
*/
|
||||
private Function getTarget2(Call c) {
|
||||
not exists(getTarget1(c)) and
|
||||
result = getAPossibleDefinition(VirtualDispatch::getAViableTarget(c))
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper predicate for `getTarget`, that computes possible targets of a `Call`.
|
||||
*
|
||||
* Otherwise, the result is the undefined `Function` instances.
|
||||
*/
|
||||
private Function getTarget3(Call c) {
|
||||
not exists(getTarget1(c)) and
|
||||
not exists(getTarget2(c)) and
|
||||
result = VirtualDispatch::getAViableTarget(c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible target for the `Call`, using the name and parameter matching if we did not associate
|
||||
* this call with a specific definition at link or compile time, and performing simple virtual
|
||||
* dispatch resolution.
|
||||
*/
|
||||
Function getTarget(Call c) {
|
||||
if VirtualDispatch::getAViableTarget(c).isDefined()
|
||||
then
|
||||
/*
|
||||
* If there is at least one defined target after performing some simple virtual dispatch
|
||||
* resolution, then the result is all the defined targets.
|
||||
*/
|
||||
|
||||
result = VirtualDispatch::getAViableTarget(c) and
|
||||
result.isDefined()
|
||||
else
|
||||
if exists(getAPossibleDefinition(VirtualDispatch::getAViableTarget(c)))
|
||||
then
|
||||
/*
|
||||
* If we can use the heuristic matching of functions to find definitions for some of the viable
|
||||
* targets, return those.
|
||||
*/
|
||||
|
||||
result = getAPossibleDefinition(VirtualDispatch::getAViableTarget(c))
|
||||
else
|
||||
// Otherwise, the result is the undefined `Function` instances
|
||||
result = VirtualDispatch::getAViableTarget(c)
|
||||
result = getTarget1(c) or
|
||||
result = getTarget2(c) or
|
||||
result = getTarget3(c)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -669,7 +684,7 @@ FieldAccess getAFieldAccess(Variable v) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a condition which is asserted to be false by the given `ne` expression, according to this pattern:
|
||||
* Gets a condition which is checked to be false by the given `ne` expression, according to this pattern:
|
||||
* ```
|
||||
* int a = !!result;
|
||||
* if (!a) { // <- ne
|
||||
@@ -677,7 +692,7 @@ FieldAccess getAFieldAccess(Variable v) {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
Expr getAssertedFalseCondition(NotExpr ne) {
|
||||
private Expr getCheckedFalseCondition(NotExpr ne) {
|
||||
exists(LocalVariable v |
|
||||
result = v.getInitializer().getExpr().(NotExpr).getOperand().(NotExpr).getOperand() and
|
||||
ne.getOperand() = v.getAnAccess() and
|
||||
|
||||
@@ -190,11 +190,11 @@ private predicate windowsSystemInfo(FunctionCall source, Element use) {
|
||||
// void WINAPI GetSystemInfo(_Out_ LPSYSTEM_INFO lpSystemInfo);
|
||||
// void WINAPI GetNativeSystemInfo(_Out_ LPSYSTEM_INFO lpSystemInfo);
|
||||
(
|
||||
source.getTarget().hasName("GetVersionEx") or
|
||||
source.getTarget().hasName("GetVersionExA") or
|
||||
source.getTarget().hasName("GetVersionExW") or
|
||||
source.getTarget().hasName("GetSystemInfo") or
|
||||
source.getTarget().hasName("GetNativeSystemInfo")
|
||||
source.getTarget().hasGlobalName("GetVersionEx") or
|
||||
source.getTarget().hasGlobalName("GetVersionExA") or
|
||||
source.getTarget().hasGlobalName("GetVersionExW") or
|
||||
source.getTarget().hasGlobalName("GetSystemInfo") or
|
||||
source.getTarget().hasGlobalName("GetNativeSystemInfo")
|
||||
) and
|
||||
use = source.getArgument(0)
|
||||
}
|
||||
@@ -216,9 +216,9 @@ private predicate windowsFolderPath(FunctionCall source, Element use) {
|
||||
// _In_ BOOL fCreate
|
||||
// );
|
||||
(
|
||||
source.getTarget().hasName("SHGetSpecialFolderPath") or
|
||||
source.getTarget().hasName("SHGetSpecialFolderPathA") or
|
||||
source.getTarget().hasName("SHGetSpecialFolderPathW")
|
||||
source.getTarget().hasGlobalName("SHGetSpecialFolderPath") or
|
||||
source.getTarget().hasGlobalName("SHGetSpecialFolderPathA") or
|
||||
source.getTarget().hasGlobalName("SHGetSpecialFolderPathW")
|
||||
) and
|
||||
use = source.getArgument(1)
|
||||
or
|
||||
@@ -228,7 +228,7 @@ private predicate windowsFolderPath(FunctionCall source, Element use) {
|
||||
// _In_opt_ HANDLE hToken,
|
||||
// _Out_ PWSTR *ppszPath
|
||||
// );
|
||||
source.getTarget().hasName("SHGetKnownFolderPath") and
|
||||
source.getTarget().hasGlobalName("SHGetKnownFolderPath") and
|
||||
use = source.getArgument(3)
|
||||
or
|
||||
// HRESULT SHGetFolderPath(
|
||||
@@ -239,9 +239,9 @@ private predicate windowsFolderPath(FunctionCall source, Element use) {
|
||||
// _Out_ LPTSTR pszPath
|
||||
// );
|
||||
(
|
||||
source.getTarget().hasName("SHGetFolderPath") or
|
||||
source.getTarget().hasName("SHGetFolderPathA") or
|
||||
source.getTarget().hasName("SHGetFolderPathW")
|
||||
source.getTarget().hasGlobalName("SHGetFolderPath") or
|
||||
source.getTarget().hasGlobalName("SHGetFolderPathA") or
|
||||
source.getTarget().hasGlobalName("SHGetFolderPathW")
|
||||
) and
|
||||
use = source.getArgument(4)
|
||||
or
|
||||
@@ -254,9 +254,9 @@ private predicate windowsFolderPath(FunctionCall source, Element use) {
|
||||
// _Out_ LPTSTR pszPath
|
||||
// );
|
||||
(
|
||||
source.getTarget().hasName("SHGetFolderPathAndSubDir") or
|
||||
source.getTarget().hasName("SHGetFolderPathAndSubDirA") or
|
||||
source.getTarget().hasName("SHGetFolderPathAndSubDirW")
|
||||
source.getTarget().hasGlobalName("SHGetFolderPathAndSubDir") or
|
||||
source.getTarget().hasGlobalName("SHGetFolderPathAndSubDirA") or
|
||||
source.getTarget().hasGlobalName("SHGetFolderPathAndSubDirW")
|
||||
) and
|
||||
use = source.getArgument(5)
|
||||
}
|
||||
@@ -273,9 +273,9 @@ class WindowsFolderPath extends SystemData {
|
||||
|
||||
private predicate logonUser(FunctionCall source, VariableAccess use) {
|
||||
(
|
||||
source.getTarget().hasName("LogonUser") or
|
||||
source.getTarget().hasName("LogonUserW") or
|
||||
source.getTarget().hasName("LogonUserA")
|
||||
source.getTarget().hasGlobalName("LogonUser") or
|
||||
source.getTarget().hasGlobalName("LogonUserW") or
|
||||
source.getTarget().hasGlobalName("LogonUserA")
|
||||
) and
|
||||
use = source.getAnArgument()
|
||||
}
|
||||
@@ -297,9 +297,9 @@ private predicate regQuery(FunctionCall source, VariableAccess use) {
|
||||
// _Inout_opt_ PLONG lpcbValue
|
||||
// );
|
||||
(
|
||||
source.getTarget().hasName("RegQueryValue") or
|
||||
source.getTarget().hasName("RegQueryValueA") or
|
||||
source.getTarget().hasName("RegQueryValueW")
|
||||
source.getTarget().hasGlobalName("RegQueryValue") or
|
||||
source.getTarget().hasGlobalName("RegQueryValueA") or
|
||||
source.getTarget().hasGlobalName("RegQueryValueW")
|
||||
) and
|
||||
use = source.getArgument(2)
|
||||
or
|
||||
@@ -311,9 +311,9 @@ private predicate regQuery(FunctionCall source, VariableAccess use) {
|
||||
// _Inout_opt_ LPDWORD ldwTotsize
|
||||
// );
|
||||
(
|
||||
source.getTarget().hasName("RegQueryMultipleValues") or
|
||||
source.getTarget().hasName("RegQueryMultipleValuesA") or
|
||||
source.getTarget().hasName("RegQueryMultipleValuesW")
|
||||
source.getTarget().hasGlobalName("RegQueryMultipleValues") or
|
||||
source.getTarget().hasGlobalName("RegQueryMultipleValuesA") or
|
||||
source.getTarget().hasGlobalName("RegQueryMultipleValuesW")
|
||||
) and
|
||||
use = source.getArgument(3)
|
||||
or
|
||||
@@ -326,9 +326,9 @@ private predicate regQuery(FunctionCall source, VariableAccess use) {
|
||||
// _Inout_opt_ LPDWORD lpcbData
|
||||
// );
|
||||
(
|
||||
source.getTarget().hasName("RegQueryValueEx") or
|
||||
source.getTarget().hasName("RegQueryValueExA") or
|
||||
source.getTarget().hasName("RegQueryValueExW")
|
||||
source.getTarget().hasGlobalName("RegQueryValueEx") or
|
||||
source.getTarget().hasGlobalName("RegQueryValueExA") or
|
||||
source.getTarget().hasGlobalName("RegQueryValueExW")
|
||||
) and
|
||||
use = source.getArgument(4)
|
||||
or
|
||||
@@ -342,9 +342,9 @@ private predicate regQuery(FunctionCall source, VariableAccess use) {
|
||||
// _Inout_opt_ LPDWORD pcbData
|
||||
// );
|
||||
(
|
||||
source.getTarget().hasName("RegGetValue") or
|
||||
source.getTarget().hasName("RegGetValueA") or
|
||||
source.getTarget().hasName("RegGetValueW")
|
||||
source.getTarget().hasGlobalName("RegGetValue") or
|
||||
source.getTarget().hasGlobalName("RegGetValueA") or
|
||||
source.getTarget().hasGlobalName("RegGetValueW")
|
||||
) and
|
||||
use = source.getArgument(5)
|
||||
}
|
||||
|
||||
@@ -15,5 +15,5 @@ import cpp
|
||||
from FunctionCall call, Function target
|
||||
where
|
||||
call.getTarget() = target and
|
||||
target.hasGlobalName("gets")
|
||||
target.hasGlobalOrStdName("gets")
|
||||
select call, "gets does not guard against buffer overflow"
|
||||
|
||||
@@ -22,7 +22,7 @@ predicate acquireExpr(Expr acquire, string kind) {
|
||||
exists(FunctionCall fc, Function f, string name |
|
||||
fc = acquire and
|
||||
f = fc.getTarget() and
|
||||
f.hasGlobalName(name) and
|
||||
f.hasGlobalOrStdName(name) and
|
||||
(
|
||||
name = "fopen" and
|
||||
kind = "file"
|
||||
@@ -46,7 +46,7 @@ predicate releaseExpr(Expr release, Expr resource, string kind) {
|
||||
exists(FunctionCall fc, Function f, string name |
|
||||
fc = release and
|
||||
f = fc.getTarget() and
|
||||
f.hasGlobalName(name) and
|
||||
f.hasGlobalOrStdName(name) and
|
||||
(
|
||||
name = "fclose" and
|
||||
resource = fc.getArgument(0) and
|
||||
|
||||
@@ -22,8 +22,8 @@ predicate containsArray(Type t) {
|
||||
or
|
||||
containsArray(t.getUnderlyingType()) and
|
||||
not exists(TypedefType allowed | allowed = t |
|
||||
allowed.hasGlobalName("jmp_buf") or
|
||||
allowed.hasGlobalName("va_list")
|
||||
allowed.hasGlobalOrStdName("jmp_buf") or
|
||||
allowed.hasGlobalOrStdName("va_list")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -605,15 +605,6 @@ class Class extends UserType {
|
||||
class_instantiation(underlyingElement(this), unresolveElement(c))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument used to instantiate this class from a
|
||||
* class template. When called on a class template, this will return the
|
||||
* `i`th template parameter.
|
||||
*/
|
||||
override Type getTemplateArgument(int i) {
|
||||
class_template_argument(underlyingElement(this), i, unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this class/struct is polymorphic (has a virtual function, or
|
||||
* inherits one).
|
||||
@@ -623,7 +614,7 @@ class Class extends UserType {
|
||||
}
|
||||
|
||||
override predicate involvesTemplateParameter() {
|
||||
getATemplateArgument().involvesTemplateParameter()
|
||||
getATemplateArgument().(Type).involvesTemplateParameter()
|
||||
}
|
||||
|
||||
/** Holds if this class, struct or union was declared 'final'. */
|
||||
|
||||
@@ -123,6 +123,13 @@ abstract class Declaration extends Locatable, @declaration {
|
||||
/** Holds if this declaration has the given name in the global namespace. */
|
||||
predicate hasGlobalName(string name) { this.hasQualifiedName("", "", name) }
|
||||
|
||||
/** Holds if this declaration has the given name in the global namespace or the `std` namespace. */
|
||||
predicate hasGlobalOrStdName(string name) {
|
||||
this.hasGlobalName(name)
|
||||
or
|
||||
this.hasQualifiedName("std", "", name)
|
||||
}
|
||||
|
||||
/** Gets a specifier of this declaration. */
|
||||
abstract Specifier getASpecifier();
|
||||
|
||||
@@ -193,20 +200,83 @@ abstract class Declaration extends Locatable, @declaration {
|
||||
|
||||
/**
|
||||
* Gets a template argument used to instantiate this declaration from a template.
|
||||
* When called on a template, this will return a template parameter.
|
||||
* When called on a template, this will return a template parameter type for
|
||||
* both typed and non-typed parameters.
|
||||
*/
|
||||
final Type getATemplateArgument() { result = getTemplateArgument(_) }
|
||||
final Locatable getATemplateArgument() { result = getTemplateArgument(_) }
|
||||
|
||||
/**
|
||||
* Gets a template argument used to instantiate this declaration from a template.
|
||||
* When called on a template, this will return a non-typed template
|
||||
* parameter value.
|
||||
*/
|
||||
final Locatable getATemplateArgumentKind() { result = getTemplateArgumentKind(_) }
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument used to instantiate this declaration from a
|
||||
* template. When called on a template, this will return the `i`th template parameter.
|
||||
* template.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* `template<typename T, T X> class Foo;`
|
||||
*
|
||||
* Will have `getTemplateArgument(0)` return `T`, and
|
||||
* `getTemplateArgument(1)` return `X`.
|
||||
*
|
||||
* `Foo<int, 1> bar;`
|
||||
*
|
||||
* Will have `getTemplateArgument())` return `int`, and
|
||||
* `getTemplateArgument(1)` return `1`.
|
||||
*/
|
||||
Type getTemplateArgument(int index) { none() }
|
||||
final Locatable getTemplateArgument(int index) {
|
||||
if exists(getTemplateArgumentValue(index))
|
||||
then result = getTemplateArgumentValue(index)
|
||||
else result = getTemplateArgumentType(index)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument value used to instantiate this declaration
|
||||
* from a template. When called on a template, this will return the `i`th template
|
||||
* parameter value if it exists.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* `template<typename T, T X> class Foo;`
|
||||
*
|
||||
* Will have `getTemplateArgumentKind(1)` return `T`, and no result for
|
||||
* `getTemplateArgumentKind(0)`.
|
||||
*
|
||||
* `Foo<int, 10> bar;
|
||||
*
|
||||
* Will have `getTemplateArgumentKind(1)` return `int`, and no result for
|
||||
* `getTemplateArgumentKind(0)`.
|
||||
*/
|
||||
final Locatable getTemplateArgumentKind(int index) {
|
||||
if exists(getTemplateArgumentValue(index))
|
||||
then result = getTemplateArgumentType(index)
|
||||
else none()
|
||||
}
|
||||
|
||||
/** Gets the number of template arguments for this declaration. */
|
||||
final int getNumberOfTemplateArguments() {
|
||||
result = count(int i | exists(getTemplateArgument(i)))
|
||||
}
|
||||
|
||||
private Type getTemplateArgumentType(int index) {
|
||||
class_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
or
|
||||
function_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
or
|
||||
variable_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
}
|
||||
|
||||
private Expr getTemplateArgumentValue(int index) {
|
||||
class_template_argument_value(underlyingElement(this), index, unresolveElement(result))
|
||||
or
|
||||
function_template_argument_value(underlyingElement(this), index, unresolveElement(result))
|
||||
or
|
||||
variable_template_argument_value(underlyingElement(this), index, unresolveElement(result))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -343,15 +343,6 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
function_instantiation(underlyingElement(this), unresolveElement(f))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument used to instantiate this function from a
|
||||
* function template. When called on a function template, this will return the
|
||||
* `i`th template parameter.
|
||||
*/
|
||||
override Type getTemplateArgument(int index) {
|
||||
function_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this function is defined in several files. This is illegal in
|
||||
* C (though possible in some C++ compilers), and likely indicates that
|
||||
@@ -434,7 +425,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
// ... and likewise for destructors.
|
||||
this.(Destructor).getADestruction().mayBeGloballyImpure()
|
||||
else
|
||||
not exists(string name | this.hasGlobalName(name) |
|
||||
not exists(string name | this.hasGlobalOrStdName(name) |
|
||||
// Unless it's a function that we know is side-effect-free, it may
|
||||
// have side-effects.
|
||||
name = "strcmp" or
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Gets a `Field` that is nested within the given `Struct`.
|
||||
*
|
||||
* This identifies `Field`s which are located in the same memory
|
||||
* Gets a `Field` that is within the given `Struct`, either directly or nested
|
||||
* inside one or more levels of member structs.
|
||||
*/
|
||||
private Field getANestedField(Struct s) {
|
||||
result = s.getAField()
|
||||
@@ -15,7 +14,7 @@ private Field getANestedField(Struct s) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwraps a series of field accesses to determine the inner-most qualifier.
|
||||
* Unwraps a series of field accesses to determine the outer-most qualifier.
|
||||
*/
|
||||
private Expr getUltimateQualifier(FieldAccess fa) {
|
||||
exists(Expr qualifier | qualifier = fa.getQualifier() |
|
||||
|
||||
@@ -35,6 +35,14 @@ private string getParameterTypeString(Type parameterType) {
|
||||
else result = parameterType.(DumpType).getTypeIdentityString()
|
||||
}
|
||||
|
||||
private string getTemplateArgumentString(Declaration d, int i) {
|
||||
if exists(d.getTemplateArgumentKind(i))
|
||||
then
|
||||
result = d.getTemplateArgumentKind(i).(DumpType).getTypeIdentityString() + " " +
|
||||
d.getTemplateArgument(i)
|
||||
else result = d.getTemplateArgument(i).(DumpType).getTypeIdentityString()
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Declaration` extended to add methods for generating strings useful only for dumps and debugging.
|
||||
*/
|
||||
@@ -56,7 +64,7 @@ abstract private class DumpDeclaration extends Declaration {
|
||||
strictconcat(int i |
|
||||
exists(this.getTemplateArgument(i))
|
||||
|
|
||||
this.getTemplateArgument(i).(DumpType).getTypeIdentityString(), ", " order by i
|
||||
getTemplateArgumentString(this, i), ", " order by i
|
||||
) + ">"
|
||||
else result = ""
|
||||
}
|
||||
|
||||
@@ -7,3 +7,15 @@
|
||||
|
||||
import cpp
|
||||
import PrintAST
|
||||
|
||||
/**
|
||||
* Temporarily tweak this class or make a copy to control which functions are
|
||||
* printed.
|
||||
*/
|
||||
class Cfg extends PrintASTConfiguration {
|
||||
/**
|
||||
* TWEAK THIS PREDICATE AS NEEDED.
|
||||
* Holds if the AST for `func` should be printed.
|
||||
*/
|
||||
override predicate shouldPrintFunction(Function func) { any() }
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ private import semmle.code.cpp.internal.ResolveClass
|
||||
|
||||
/**
|
||||
* A C/C++ type.
|
||||
*
|
||||
* This QL class represents the root of the C/C++ type hierarchy.
|
||||
*/
|
||||
class Type extends Locatable, @type {
|
||||
Type() { isType(underlyingElement(this)) }
|
||||
@@ -210,7 +212,7 @@ class Type extends Locatable, @type {
|
||||
// A function call that provides an explicit template argument that refers to T uses T.
|
||||
// We exclude calls within instantiations, since they do not appear directly in the source.
|
||||
exists(FunctionCall c |
|
||||
c.getAnExplicitTemplateArgument().refersTo(this) and
|
||||
c.getAnExplicitTemplateArgument().(Type).refersTo(this) and
|
||||
result = c and
|
||||
not c.getEnclosingFunction().isConstructedFrom(_)
|
||||
)
|
||||
@@ -289,6 +291,13 @@ class Type extends Locatable, @type {
|
||||
|
||||
/**
|
||||
* A C/C++ built-in primitive type (int, float, void, and so on). See 4.1.1.
|
||||
* In the following example, `unsigned int` and `double` denote primitive
|
||||
* built-in types:
|
||||
* ```
|
||||
* double a;
|
||||
* unsigned int ua[40];
|
||||
* typedef double LargeFloat;
|
||||
* ```
|
||||
*/
|
||||
class BuiltInType extends Type, @builtintype {
|
||||
override string toString() { result = this.getName() }
|
||||
@@ -301,7 +310,14 @@ class BuiltInType extends Type, @builtintype {
|
||||
}
|
||||
|
||||
/**
|
||||
* An erroneous type.
|
||||
* An erroneous type. This type has no corresponding C/C++ syntax.
|
||||
*
|
||||
* `ErroneousType` is the type of `ErrorExpr`, which in turn refers to an illegal
|
||||
* language construct. In the example below, a temporary (`0`) cannot be bound
|
||||
* to an lvalue reference (`int &`):
|
||||
* ```
|
||||
* int &intref = 0;
|
||||
* ```
|
||||
*/
|
||||
class ErroneousType extends BuiltInType {
|
||||
ErroneousType() { builtintypes(underlyingElement(this), _, 1, _, _, _) }
|
||||
@@ -310,7 +326,18 @@ class ErroneousType extends BuiltInType {
|
||||
}
|
||||
|
||||
/**
|
||||
* The unknown type.
|
||||
* The unknown type. This type has no corresponding C/C++ syntax.
|
||||
*
|
||||
* Unknown types usually occur inside _uninstantiated_ template functions.
|
||||
* In the example below, the expressions `x.a` and `x.b` have unknown type
|
||||
* in the _uninstantiated_ template.
|
||||
* ```
|
||||
* template<typename T>
|
||||
* bool check(T x) {
|
||||
* if (x.a == x.b)
|
||||
* abort();
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class UnknownType extends BuiltInType {
|
||||
UnknownType() { builtintypes(underlyingElement(this), _, 2, _, _, _) }
|
||||
@@ -326,6 +353,10 @@ private predicate isArithmeticType(@builtintype type, int kind) {
|
||||
|
||||
/**
|
||||
* The C/C++ arithmetic types. See 4.1.1.
|
||||
*
|
||||
* This includes primitive types on which arithmetic, bitwise or logical
|
||||
* operations may be performed. Examples of arithmetic types include
|
||||
* `char`, `int`, `float`, and `bool`.
|
||||
*/
|
||||
class ArithmeticType extends BuiltInType {
|
||||
ArithmeticType() { isArithmeticType(underlyingElement(this), _) }
|
||||
@@ -349,11 +380,20 @@ private predicate isIntegralType(@builtintype type, int kind) {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ integral or enum type.
|
||||
* The definition of "integral type" in the C++ Standard excludes enum types,
|
||||
* but because an enum type holds a value of its underlying integral type,
|
||||
* A C/C++ integral or `enum` type.
|
||||
*
|
||||
* The definition of "integral type" in the C++ standard excludes `enum` types,
|
||||
* but because an `enum` type holds a value of its underlying integral type,
|
||||
* it is often useful to have a common category that includes both integral
|
||||
* and enum types.
|
||||
* and `enum` types.
|
||||
*
|
||||
* In the following example, `a`, `b` and `c` are all declared with an
|
||||
* integral or `enum` type:
|
||||
* ```
|
||||
* unsigned long a;
|
||||
* enum e1 { val1, val2 } b;
|
||||
* enum class e2: short { val3, val4 } c;
|
||||
* ```
|
||||
*/
|
||||
class IntegralOrEnumType extends Type {
|
||||
IntegralOrEnumType() {
|
||||
@@ -426,7 +466,17 @@ private predicate integralTypeMapping(int original, int canonical, int unsigned,
|
||||
}
|
||||
|
||||
/**
|
||||
* The C/C++ integral types. See 4.1.1.
|
||||
* The C/C++ integral types. See 4.1.1. These are types that are represented
|
||||
* as integers of varying sizes. Both `enum` types and floating-point types
|
||||
* are excluded.
|
||||
*
|
||||
* In the following examples, `a`, `b` and `c` are declared using integral
|
||||
* types:
|
||||
* ```
|
||||
* unsigned int a;
|
||||
* long long b;
|
||||
* char c;
|
||||
* ```
|
||||
*/
|
||||
class IntegralType extends ArithmeticType, IntegralOrEnumType {
|
||||
int kind;
|
||||
@@ -497,7 +547,12 @@ class IntegralType extends ArithmeticType, IntegralOrEnumType {
|
||||
}
|
||||
|
||||
/**
|
||||
* The C/C++ boolean type. See 4.2.
|
||||
* The C/C++ boolean type. See 4.2. This is the C `_Bool` type
|
||||
* or the C++ `bool` type. For example:
|
||||
* ```
|
||||
* extern bool a, b; // C++
|
||||
* _Bool c, d; // C
|
||||
* ```
|
||||
*/
|
||||
class BoolType extends IntegralType {
|
||||
BoolType() { builtintypes(underlyingElement(this), _, 4, _, _, _) }
|
||||
@@ -506,12 +561,23 @@ class BoolType extends IntegralType {
|
||||
}
|
||||
|
||||
/**
|
||||
* The C/C++ character types. See 4.3.
|
||||
* The C/C++ character types. See 4.3. This includes the `char`,
|
||||
* `signed char` and `unsigned char` types, all of which are
|
||||
* distinct from one another. For example:
|
||||
* ```
|
||||
* char a, b;
|
||||
* signed char c, d;
|
||||
* unsigned char e, f;
|
||||
* ```
|
||||
*/
|
||||
abstract class CharType extends IntegralType { }
|
||||
|
||||
/**
|
||||
* The C/C++ char type (which is different to signed char and unsigned char).
|
||||
* The C/C++ `char` type (which is distinct from `signed char` and
|
||||
* `unsigned char`). For example:
|
||||
* ```
|
||||
* char a, b;
|
||||
* ```
|
||||
*/
|
||||
class PlainCharType extends CharType {
|
||||
PlainCharType() { builtintypes(underlyingElement(this), _, 5, _, _, _) }
|
||||
@@ -520,7 +586,11 @@ class PlainCharType extends CharType {
|
||||
}
|
||||
|
||||
/**
|
||||
* The C/C++ unsigned char type (which is different to plain char, even when chars are unsigned by default).
|
||||
* The C/C++ `unsigned char` type (which is distinct from plain `char`
|
||||
* even when `char` is `unsigned` by default).
|
||||
* ```
|
||||
* unsigned char e, f;
|
||||
* ```
|
||||
*/
|
||||
class UnsignedCharType extends CharType {
|
||||
UnsignedCharType() { builtintypes(underlyingElement(this), _, 6, _, _, _) }
|
||||
@@ -529,7 +599,11 @@ class UnsignedCharType extends CharType {
|
||||
}
|
||||
|
||||
/**
|
||||
* The C/C++ signed char type (which is different to plain char, even when chars are signed by default).
|
||||
* The C/C++ `signed char` type (which is distinct from plain `char`
|
||||
* even when `char` is `signed` by default).
|
||||
* ```
|
||||
* signed char c, d;
|
||||
* ```
|
||||
*/
|
||||
class SignedCharType extends CharType {
|
||||
SignedCharType() { builtintypes(underlyingElement(this), _, 7, _, _, _) }
|
||||
@@ -538,7 +612,11 @@ class SignedCharType extends CharType {
|
||||
}
|
||||
|
||||
/**
|
||||
* The C/C++ short types. See 4.3.
|
||||
* The C/C++ short types. See 4.3. This includes `short`, `signed short`
|
||||
* and `unsigned short`.
|
||||
* ```
|
||||
* signed short ss;
|
||||
* ```
|
||||
*/
|
||||
class ShortType extends IntegralType {
|
||||
ShortType() {
|
||||
@@ -551,7 +629,11 @@ class ShortType extends IntegralType {
|
||||
}
|
||||
|
||||
/**
|
||||
* The C/C++ integer types. See 4.4.
|
||||
* The C/C++ integer types. See 4.4. This includes `int`, `signed int`
|
||||
* and `unsigned int`.
|
||||
* ```
|
||||
* unsigned int ui;
|
||||
* ```
|
||||
*/
|
||||
class IntType extends IntegralType {
|
||||
IntType() {
|
||||
@@ -564,7 +646,11 @@ class IntType extends IntegralType {
|
||||
}
|
||||
|
||||
/**
|
||||
* The C/C++ long types. See 4.4.
|
||||
* The C/C++ long types. See 4.4. This includes `long`, `signed long`
|
||||
* and `unsigned long`.
|
||||
* ```
|
||||
* long l;
|
||||
* ```
|
||||
*/
|
||||
class LongType extends IntegralType {
|
||||
LongType() {
|
||||
@@ -577,7 +663,11 @@ class LongType extends IntegralType {
|
||||
}
|
||||
|
||||
/**
|
||||
* The C/C++ long long types. See 4.4.
|
||||
* The C/C++ long long types. See 4.4. This includes `long long`, `signed long long`
|
||||
* and `unsigned long long`.
|
||||
* ```
|
||||
* signed long long sll;
|
||||
* ```
|
||||
*/
|
||||
class LongLongType extends IntegralType {
|
||||
LongLongType() {
|
||||
@@ -590,7 +680,12 @@ class LongLongType extends IntegralType {
|
||||
}
|
||||
|
||||
/**
|
||||
* The GNU C __int128 types.
|
||||
* The GNU C __int128 primitive types. They are not part of standard C/C++.
|
||||
*
|
||||
* This includes `__int128`, `signed __int128` and `unsigned __int128`.
|
||||
* ```
|
||||
* unsigned __int128 ui128;
|
||||
* ```
|
||||
*/
|
||||
class Int128Type extends IntegralType {
|
||||
Int128Type() {
|
||||
@@ -598,10 +693,18 @@ class Int128Type extends IntegralType {
|
||||
builtintypes(underlyingElement(this), _, 36, _, _, _) or
|
||||
builtintypes(underlyingElement(this), _, 37, _, _, _)
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() { result = "Int128Type" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The C/C++ floating point types. See 4.5.
|
||||
* The C/C++ floating point types. See 4.5. This includes `float`,
|
||||
* `double` and `long double` types.
|
||||
* ```
|
||||
* float f;
|
||||
* double d;
|
||||
* long double ld;
|
||||
* ```
|
||||
*/
|
||||
class FloatingPointType extends ArithmeticType {
|
||||
FloatingPointType() {
|
||||
@@ -619,7 +722,10 @@ class FloatingPointType extends ArithmeticType {
|
||||
}
|
||||
|
||||
/**
|
||||
* The C/C++ float type.
|
||||
* The C/C++ `float` type.
|
||||
* ```
|
||||
* float f;
|
||||
* ```
|
||||
*/
|
||||
class FloatType extends FloatingPointType {
|
||||
FloatType() { builtintypes(underlyingElement(this), _, 24, _, _, _) }
|
||||
@@ -628,7 +734,10 @@ class FloatType extends FloatingPointType {
|
||||
}
|
||||
|
||||
/**
|
||||
* The C/C++ double type.
|
||||
* The C/C++ `double` type.
|
||||
* ```
|
||||
* double d;
|
||||
* ```
|
||||
*/
|
||||
class DoubleType extends FloatingPointType {
|
||||
DoubleType() { builtintypes(underlyingElement(this), _, 25, _, _, _) }
|
||||
@@ -637,7 +746,10 @@ class DoubleType extends FloatingPointType {
|
||||
}
|
||||
|
||||
/**
|
||||
* The C/C++ long double type.
|
||||
* The C/C++ `long double` type.
|
||||
* ```
|
||||
* long double ld;
|
||||
* ```
|
||||
*/
|
||||
class LongDoubleType extends FloatingPointType {
|
||||
LongDoubleType() { builtintypes(underlyingElement(this), _, 26, _, _, _) }
|
||||
@@ -646,35 +758,58 @@ class LongDoubleType extends FloatingPointType {
|
||||
}
|
||||
|
||||
/**
|
||||
* The GNU C __float128 type.
|
||||
* The GNU C `__float128` primitive type. This is not standard C/C++.
|
||||
* ```
|
||||
* __float128 f128;
|
||||
* ```
|
||||
*/
|
||||
class Float128Type extends FloatingPointType {
|
||||
Float128Type() { builtintypes(underlyingElement(this), _, 38, _, _, _) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "Float128Type" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The GNU C _Decimal32 type.
|
||||
* The GNU C `_Decimal32` primitive type. This is not standard C/C++.
|
||||
* ```
|
||||
* _Decimal32 d32;
|
||||
* ```
|
||||
*/
|
||||
class Decimal32Type extends FloatingPointType {
|
||||
Decimal32Type() { builtintypes(underlyingElement(this), _, 40, _, _, _) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "Decimal32Type" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The GNU C _Decimal64 type.
|
||||
* The GNU C `_Decimal64` primitive type. This is not standard C/C++.
|
||||
* ```
|
||||
* _Decimal64 d64;
|
||||
* ```
|
||||
*/
|
||||
class Decimal64Type extends FloatingPointType {
|
||||
Decimal64Type() { builtintypes(underlyingElement(this), _, 41, _, _, _) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "Decimal64Type" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The GNU C _Decimal128 type.
|
||||
* The GNU C `_Decimal128` primitive type. This is not standard C/C++.
|
||||
* ```
|
||||
* _Decimal128 d128;
|
||||
* ```
|
||||
*/
|
||||
class Decimal128Type extends FloatingPointType {
|
||||
Decimal128Type() { builtintypes(underlyingElement(this), _, 42, _, _, _) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "Decimal128Type" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The C/C++ void type. See 4.7.
|
||||
* The C/C++ `void` type. See 4.7.
|
||||
* ```
|
||||
* void foo();
|
||||
* ```
|
||||
*/
|
||||
class VoidType extends BuiltInType {
|
||||
VoidType() { builtintypes(underlyingElement(this), _, 3, _, _, _) }
|
||||
@@ -688,6 +823,9 @@ class VoidType extends BuiltInType {
|
||||
* Note that on some platforms `wchar_t` doesn't exist as a built-in
|
||||
* type but a typedef is provided. Consider using the `Wchar_t` QL
|
||||
* class to include these types.
|
||||
* ```
|
||||
* wchar_t wc;
|
||||
* ```
|
||||
*/
|
||||
class WideCharType extends IntegralType {
|
||||
WideCharType() { builtintypes(underlyingElement(this), _, 33, _, _, _) }
|
||||
@@ -696,7 +834,10 @@ class WideCharType extends IntegralType {
|
||||
}
|
||||
|
||||
/**
|
||||
* The C/C++ `char16_t` type.
|
||||
* The C/C++ `char16_t` type. This is available starting with C11 and C++11.
|
||||
* ```
|
||||
* char16_t c16;
|
||||
* ```
|
||||
*/
|
||||
class Char16Type extends IntegralType {
|
||||
Char16Type() { builtintypes(underlyingElement(this), _, 43, _, _, _) }
|
||||
@@ -705,7 +846,10 @@ class Char16Type extends IntegralType {
|
||||
}
|
||||
|
||||
/**
|
||||
* The C/C++ `char32_t` type.
|
||||
* The C/C++ `char32_t` type. This is available starting with C11 and C++11.
|
||||
* ```
|
||||
* char32_t c32;
|
||||
* ```
|
||||
*/
|
||||
class Char32Type extends IntegralType {
|
||||
Char32Type() { builtintypes(underlyingElement(this), _, 44, _, _, _) }
|
||||
@@ -714,13 +858,13 @@ class Char32Type extends IntegralType {
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the C++11 nullptr constant.
|
||||
*
|
||||
* Note that this is not `nullptr_t`, as `nullptr_t` is defined as:
|
||||
* The (primitive) type of the C++11 `nullptr` constant. It is a
|
||||
* distinct type, denoted by `decltype(nullptr)`, that is not itself a pointer
|
||||
* type or a pointer to member type. The `<cstddef>` header usually defines
|
||||
* the `std::nullptr_t` type as follows:
|
||||
* ```
|
||||
* typedef decltype(nullptr) nullptr_t;
|
||||
* typedef decltype(nullptr) nullptr_t;
|
||||
* ```
|
||||
* Instead, this is the unspeakable type given by `decltype(nullptr)`.
|
||||
*/
|
||||
class NullPointerType extends BuiltInType {
|
||||
NullPointerType() { builtintypes(underlyingElement(this), _, 34, _, _, _) }
|
||||
@@ -731,8 +875,13 @@ class NullPointerType extends BuiltInType {
|
||||
/**
|
||||
* A C/C++ derived type.
|
||||
*
|
||||
* These are pointer and reference types, array and vector types, and const and volatile types.
|
||||
* In all cases, the type is formed from a single base type.
|
||||
* These are pointer and reference types, array and GNU vector types, and `const` and `volatile` types.
|
||||
* In all cases, the type is formed from a single base type. For example:
|
||||
* ```
|
||||
* int *pi;
|
||||
* int &ri = *pi;
|
||||
* const float fa[40];
|
||||
* ```
|
||||
*/
|
||||
class DerivedType extends Type, @derivedtype {
|
||||
override string toString() { result = this.getName() }
|
||||
@@ -777,9 +926,15 @@ class DerivedType extends Type, @derivedtype {
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance of the C++11 decltype operator.
|
||||
* An instance of the C++11 `decltype` operator. For example:
|
||||
* ```
|
||||
* int a;
|
||||
* decltype(a) b;
|
||||
* ```
|
||||
*/
|
||||
class Decltype extends Type, @decltype {
|
||||
override string getCanonicalQLClass() { result = "Decltype" }
|
||||
|
||||
/**
|
||||
* The expression whose type is being obtained by this decltype.
|
||||
*/
|
||||
@@ -790,17 +945,17 @@ class Decltype extends Type, @decltype {
|
||||
*/
|
||||
Type getBaseType() { decltypes(underlyingElement(this), _, unresolveElement(result), _) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "Decltype" }
|
||||
|
||||
/**
|
||||
* Whether an extra pair of parentheses around the expression would change the semantics of this decltype.
|
||||
*
|
||||
* The following example shows the effect of an extra pair of parentheses:
|
||||
* struct A { double x; };
|
||||
* const A* a = new A();
|
||||
* decltype( a->x ); // type is double
|
||||
* decltype((a->x)); // type is const double&
|
||||
* Consult the C++11 standard for more details.
|
||||
* ```
|
||||
* struct A { double x; };
|
||||
* const A* a = new A();
|
||||
* decltype( a->x ); // type is double
|
||||
* decltype((a->x)); // type is const double&
|
||||
* ```
|
||||
* Please consult the C++11 standard for more details.
|
||||
*/
|
||||
predicate parenthesesWouldChangeMeaning() { decltypes(underlyingElement(this), _, _, true) }
|
||||
|
||||
@@ -843,6 +998,10 @@ class Decltype extends Type, @decltype {
|
||||
|
||||
/**
|
||||
* A C/C++ pointer type. See 4.9.1.
|
||||
* ```
|
||||
* void *ptr;
|
||||
* void **ptr2 = &ptr;
|
||||
* ```
|
||||
*/
|
||||
class PointerType extends DerivedType {
|
||||
PointerType() { derivedtypes(underlyingElement(this), _, 1, _) }
|
||||
@@ -865,8 +1024,8 @@ class PointerType extends DerivedType {
|
||||
/**
|
||||
* A C++ reference type. See 4.9.1.
|
||||
*
|
||||
* For C++11 code bases, this includes both lvalue references (&) and rvalue references (&&).
|
||||
* To distinguish between them, use the LValueReferenceType and RValueReferenceType classes.
|
||||
* For C++11 code bases, this includes both _lvalue_ references (`&`) and _rvalue_ references (`&&`).
|
||||
* To distinguish between them, use the LValueReferenceType and RValueReferenceType QL classes.
|
||||
*/
|
||||
class ReferenceType extends DerivedType {
|
||||
ReferenceType() {
|
||||
@@ -891,7 +1050,11 @@ class ReferenceType extends DerivedType {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++11 lvalue reference type (e.g. int&).
|
||||
* A C++11 lvalue reference type (e.g. `int &`).
|
||||
* ```
|
||||
* int a;
|
||||
* int& b = a;
|
||||
* ```
|
||||
*/
|
||||
class LValueReferenceType extends ReferenceType {
|
||||
LValueReferenceType() { derivedtypes(underlyingElement(this), _, 2, _) }
|
||||
@@ -900,7 +1063,14 @@ class LValueReferenceType extends ReferenceType {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++11 rvalue reference type (e.g. int&&).
|
||||
* A C++11 rvalue reference type (e.g., `int &&`). It is used to
|
||||
* implement "move" semantics for object construction and assignment.
|
||||
* ```
|
||||
* class C {
|
||||
* E e;
|
||||
* C(C&& from): e(std::move(from.e)) { }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class RValueReferenceType extends ReferenceType {
|
||||
RValueReferenceType() { derivedtypes(underlyingElement(this), _, 8, _) }
|
||||
@@ -912,6 +1082,10 @@ class RValueReferenceType extends ReferenceType {
|
||||
|
||||
/**
|
||||
* A type with specifiers.
|
||||
* ```
|
||||
* const int a;
|
||||
* volatile char v;
|
||||
* ```
|
||||
*/
|
||||
class SpecifiedType extends DerivedType {
|
||||
SpecifiedType() { derivedtypes(underlyingElement(this), _, 3, _) }
|
||||
@@ -957,6 +1131,9 @@ class SpecifiedType extends DerivedType {
|
||||
|
||||
/**
|
||||
* A C/C++ array type. See 4.9.1.
|
||||
* ```
|
||||
* char table[32];
|
||||
* ```
|
||||
*/
|
||||
class ArrayType extends DerivedType {
|
||||
ArrayType() { derivedtypes(underlyingElement(this), _, 4, _) }
|
||||
@@ -1003,10 +1180,16 @@ class ArrayType extends DerivedType {
|
||||
* A GNU/Clang vector type.
|
||||
*
|
||||
* In both Clang and GNU compilers, vector types can be introduced using the
|
||||
* __attribute__((vector_size(byte_size))) syntax. The Clang compiler also
|
||||
* allows vector types to be introduced using the ext_vector_type,
|
||||
* neon_vector_type, and neon_polyvector_type attributes (all of which take
|
||||
* an element type rather than a byte size).
|
||||
* `__attribute__((vector_size(byte_size)))` syntax. The Clang compiler also
|
||||
* allows vector types to be introduced using the `ext_vector_type`,
|
||||
* `neon_vector_type`, and `neon_polyvector_type` attributes (all of which take
|
||||
* an element count rather than a byte size).
|
||||
*
|
||||
* In the example below, both `v4si` and `float4` are GNU vector types:
|
||||
* ```
|
||||
* typedef int v4si __attribute__ (( vector_size(4*sizeof(int)) ));
|
||||
* typedef float float4 __attribute__((ext_vector_type(4)));
|
||||
* ```
|
||||
*/
|
||||
class GNUVectorType extends DerivedType {
|
||||
GNUVectorType() { derivedtypes(underlyingElement(this), _, 5, _) }
|
||||
@@ -1045,7 +1228,10 @@ class GNUVectorType extends DerivedType {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ pointer to function. See 7.7.
|
||||
* A C/C++ pointer to a function. See 7.7.
|
||||
* ```
|
||||
* int(* pointer)(const void *element1, const void *element2);
|
||||
* ```
|
||||
*/
|
||||
class FunctionPointerType extends FunctionPointerIshType {
|
||||
FunctionPointerType() { derivedtypes(underlyingElement(this), _, 6, _) }
|
||||
@@ -1060,7 +1246,10 @@ class FunctionPointerType extends FunctionPointerIshType {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ reference to function.
|
||||
* A C++ reference to a function.
|
||||
* ```
|
||||
* int(& reference)(const void *element1, const void *element2);
|
||||
* ```
|
||||
*/
|
||||
class FunctionReferenceType extends FunctionPointerIshType {
|
||||
FunctionReferenceType() { derivedtypes(underlyingElement(this), _, 7, _) }
|
||||
@@ -1075,10 +1264,14 @@ class FunctionReferenceType extends FunctionPointerIshType {
|
||||
}
|
||||
|
||||
/**
|
||||
* A block type, for example int(^)(char, float).
|
||||
* A block type, for example, `int(^)(char, float)`.
|
||||
*
|
||||
* Block types (along with blocks themselves) are a language extension
|
||||
* supported by Clang, and by Apple's branch of GCC.
|
||||
* ```
|
||||
* int(^ block)(const char *element1, const char *element2)
|
||||
* = ^int (const char *element1, const char *element2) { return element1 - element 2; }
|
||||
* ```
|
||||
*/
|
||||
class BlockType extends FunctionPointerIshType {
|
||||
BlockType() { derivedtypes(underlyingElement(this), _, 10, _) }
|
||||
@@ -1091,7 +1284,9 @@ class BlockType extends FunctionPointerIshType {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ pointer to function, or a block.
|
||||
* A C/C++ pointer to a function, a C++ function reference, or a clang/Apple block.
|
||||
*
|
||||
* See `FunctionPointerType`, `FunctionReferenceType` and `BlockType` for more information.
|
||||
*/
|
||||
class FunctionPointerIshType extends DerivedType {
|
||||
FunctionPointerIshType() {
|
||||
@@ -1136,7 +1331,13 @@ class FunctionPointerIshType extends DerivedType {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ pointer to member. See 15.5.
|
||||
* A C++ pointer to data member. See 15.5.
|
||||
* ```
|
||||
* class C { int m; };
|
||||
* int C::* p = &C::m; // pointer to data member m of class C
|
||||
* class C *;
|
||||
* int val = c.*p; // access data member
|
||||
* ```
|
||||
*/
|
||||
class PointerToMemberType extends Type, @ptrtomember {
|
||||
/** a printable representation of this named element */
|
||||
@@ -1173,7 +1374,14 @@ class PointerToMemberType extends Type, @ptrtomember {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ routine type. This is what results from stripping away the pointer from a function pointer type.
|
||||
* A C/C++ routine type. Conceptually, this is what results from stripping
|
||||
* away the pointer from a function pointer type. It can also occur in C++
|
||||
* code, for example the base type of `myRoutineType` in the following code:
|
||||
* ```
|
||||
* using myRoutineType = int(int);
|
||||
*
|
||||
* myRoutineType *fp = 0;
|
||||
* ```
|
||||
*/
|
||||
class RoutineType extends Type, @routinetype {
|
||||
/** a printable representation of this named element */
|
||||
@@ -1233,7 +1441,13 @@ class RoutineType extends Type, @routinetype {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ typename template parameter.
|
||||
* A C++ `typename` (or `class`) template parameter.
|
||||
*
|
||||
* In the example below, `T` is a template parameter:
|
||||
* ```
|
||||
* template <class T>
|
||||
* class C { };
|
||||
* ```
|
||||
*/
|
||||
class TemplateParameter extends UserType {
|
||||
TemplateParameter() {
|
||||
@@ -1245,7 +1459,16 @@ class TemplateParameter extends UserType {
|
||||
override predicate involvesTemplateParameter() { any() }
|
||||
}
|
||||
|
||||
/** A C++ template template parameter, e.g. template <template <typename,typename> class T>. */
|
||||
/**
|
||||
* A C++ template template parameter.
|
||||
*
|
||||
* In the example below, `T` is a template template parameter (although its name
|
||||
* may be omitted):
|
||||
* ```
|
||||
* template <template <typename T> class Container, class Elem>
|
||||
* void foo(const Container<Elem> &value) { }
|
||||
* ```
|
||||
*/
|
||||
class TemplateTemplateParameter extends TemplateParameter {
|
||||
TemplateTemplateParameter() { usertypes(underlyingElement(this), _, 8) }
|
||||
|
||||
@@ -1253,7 +1476,10 @@ class TemplateTemplateParameter extends TemplateParameter {
|
||||
}
|
||||
|
||||
/**
|
||||
* A type representing the use of the C++11 auto keyword.
|
||||
* A type representing the use of the C++11 `auto` keyword.
|
||||
* ```
|
||||
* auto val = some_typed_expr();
|
||||
* ```
|
||||
*/
|
||||
class AutoType extends TemplateParameter {
|
||||
AutoType() { usertypes(underlyingElement(this), "auto", 7) }
|
||||
|
||||
@@ -2,12 +2,11 @@ import semmle.code.cpp.Type
|
||||
private import semmle.code.cpp.internal.ResolveClass
|
||||
|
||||
/**
|
||||
* A C/C++ typedef type. See 4.9.1.
|
||||
*
|
||||
* Represents either of the following typedef styles:
|
||||
*
|
||||
* * CTypedefType: typedef <type> <name>;
|
||||
* * UsingAliasTypedefType: using <name> = <type>;
|
||||
* A C/C++ typedef type. See 4.9.1. For example the types declared on each line of the following code:
|
||||
* ```
|
||||
* typedef int my_int;
|
||||
* using my_int2 = int;
|
||||
* ```
|
||||
*/
|
||||
class TypedefType extends UserType {
|
||||
TypedefType() {
|
||||
@@ -48,7 +47,10 @@ class TypedefType extends UserType {
|
||||
}
|
||||
|
||||
/**
|
||||
* A traditional C/C++ typedef type. See 4.9.1.
|
||||
* A traditional C/C++ typedef type. See 4.9.1. For example the type declared in the following code:
|
||||
* ```
|
||||
* typedef int my_int;
|
||||
* ```
|
||||
*/
|
||||
class CTypedefType extends TypedefType {
|
||||
CTypedefType() { usertypes(underlyingElement(this), _, 5) }
|
||||
@@ -61,7 +63,10 @@ class CTypedefType extends TypedefType {
|
||||
}
|
||||
|
||||
/**
|
||||
* A using alias C++ typedef type.
|
||||
* A using alias C++ typedef type. For example the type declared in the following code:
|
||||
* ```
|
||||
* using my_int2 = int;
|
||||
* ```
|
||||
*/
|
||||
class UsingAliasTypedefType extends TypedefType {
|
||||
UsingAliasTypedefType() { usertypes(underlyingElement(this), _, 14) }
|
||||
@@ -74,7 +79,11 @@ class UsingAliasTypedefType extends TypedefType {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ typedef type that is directly enclosed by a function.
|
||||
* A C++ `typedef` type that is directly enclosed by a function. For example the type declared inside the function `foo` in
|
||||
* the following code:
|
||||
* ```
|
||||
* int foo(void) { typedef int local; }
|
||||
* ```
|
||||
*/
|
||||
class LocalTypedefType extends TypedefType {
|
||||
LocalTypedefType() { isLocal() }
|
||||
@@ -83,7 +92,11 @@ class LocalTypedefType extends TypedefType {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ typedef type that is directly enclosed by a class, struct or union.
|
||||
* A C++ `typedef` type that is directly enclosed by a `class`, `struct` or `union`. For example the type declared inside
|
||||
* the class `C` in the following code:
|
||||
* ```
|
||||
* class C { typedef int nested; };
|
||||
* ```
|
||||
*/
|
||||
class NestedTypedefType extends TypedefType {
|
||||
NestedTypedefType() { this.isMember() }
|
||||
|
||||
@@ -5,8 +5,14 @@ import semmle.code.cpp.Function
|
||||
private import semmle.code.cpp.internal.ResolveClass
|
||||
|
||||
/**
|
||||
* A C/C++ user-defined type. Examples include `Class`, `Struct`, `Union`,
|
||||
* `Enum`, and `TypedefType`.
|
||||
* A C/C++ user-defined type. Examples include `class`, `struct`, `union`,
|
||||
* `enum` and `typedef` types.
|
||||
* ```
|
||||
* enum e1 { val1, val2 } b;
|
||||
* enum class e2: short { val3, val4 } c;
|
||||
* typedef int my_int;
|
||||
* class C { int a, b; };
|
||||
* ```
|
||||
*/
|
||||
class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @usertype {
|
||||
/**
|
||||
@@ -88,6 +94,10 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @
|
||||
|
||||
/**
|
||||
* A particular definition or forward declaration of a C/C++ user-defined type.
|
||||
* ```
|
||||
* class C;
|
||||
* typedef int ti;
|
||||
* ```
|
||||
*/
|
||||
class TypeDeclarationEntry extends DeclarationEntry, @type_decl {
|
||||
override UserType getDeclaration() { result = getType() }
|
||||
|
||||
@@ -155,15 +155,6 @@ class Variable extends Declaration, @variable {
|
||||
variable_instantiation(underlyingElement(this), unresolveElement(v))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument used to instantiate this variable from a
|
||||
* variable template. When called on a variable template, this will return the
|
||||
* `i`th template parameter.
|
||||
*/
|
||||
override Type getTemplateArgument(int index) {
|
||||
variable_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this is a compiler-generated variable. For example, a
|
||||
* [range-based for loop](http://en.cppreference.com/w/cpp/language/range-for)
|
||||
|
||||
@@ -5,13 +5,17 @@ import cpp
|
||||
*/
|
||||
predicate allocationFunction(Function f) {
|
||||
exists(string name |
|
||||
f.hasGlobalName(name) and
|
||||
f.hasGlobalOrStdName(name) and
|
||||
(
|
||||
name = "malloc" or
|
||||
name = "calloc" or
|
||||
name = "realloc" or
|
||||
name = "strdup" or
|
||||
name = "wcsdup" or
|
||||
name = "wcsdup"
|
||||
)
|
||||
or
|
||||
f.hasGlobalName(name) and
|
||||
(
|
||||
name = "_strdup" or
|
||||
name = "_wcsdup" or
|
||||
name = "_mbsdup" or
|
||||
@@ -59,7 +63,7 @@ predicate allocationCall(FunctionCall fc) {
|
||||
allocationFunction(fc.getTarget()) and
|
||||
(
|
||||
// realloc(ptr, 0) only frees the pointer
|
||||
fc.getTarget().hasGlobalName("realloc") implies not fc.getArgument(1).getValue() = "0"
|
||||
fc.getTarget().hasGlobalOrStdName("realloc") implies not fc.getArgument(1).getValue() = "0"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -73,7 +77,10 @@ predicate freeFunction(Function f, int argNum) {
|
||||
name = "free" and argNum = 0
|
||||
or
|
||||
name = "realloc" and argNum = 0
|
||||
or
|
||||
)
|
||||
or
|
||||
f.hasGlobalOrStdName(name) and
|
||||
(
|
||||
name = "ExFreePoolWithTag" and argNum = 0
|
||||
or
|
||||
name = "ExFreeToLookasideListEx" and argNum = 1
|
||||
|
||||
@@ -44,7 +44,7 @@ predicate memberMayBeVarSize(Class c, MemberVariable v) {
|
||||
aoe.getAddressable() = v
|
||||
)
|
||||
or
|
||||
exists(BuiltInOperationOffsetOf oo |
|
||||
exists(BuiltInOperationBuiltInOffsetOf oo |
|
||||
// `offsetof(c, v)` using a builtin
|
||||
oo.getAChild().(VariableAccess).getTarget() = v
|
||||
)
|
||||
|
||||
@@ -28,7 +28,7 @@ class EnvironmentRead extends Expr {
|
||||
private predicate readsEnvironment(Expr read, string sourceDescription) {
|
||||
exists(FunctionCall call, string name |
|
||||
read = call and
|
||||
call.getTarget().hasGlobalName(name) and
|
||||
call.getTarget().hasGlobalOrStdName(name) and
|
||||
(name = "getenv" or name = "secure_getenv" or name = "_wgetenv") and
|
||||
sourceDescription = name
|
||||
)
|
||||
|
||||
@@ -79,3 +79,26 @@ predicate functionContainsPreprocCode(Function f) {
|
||||
pbdStartLine >= fBlockStartLine
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is completely or partially from a macro definition, as opposed
|
||||
* to being passed in as an argument.
|
||||
*
|
||||
* In the following example, the call to `f` is from a macro definition,
|
||||
* while `y`, `+`, `1`, and `;` are not. This assumes that no identifier apart
|
||||
* from `M` refers to a macro.
|
||||
* ```
|
||||
* #define M(x) f(x)
|
||||
* ...
|
||||
* M(y + 1);
|
||||
* ```
|
||||
*/
|
||||
predicate isFromMacroDefinition(Element e) {
|
||||
exists(MacroInvocation mi |
|
||||
// e is in mi
|
||||
mi.getAnExpandedElement() = e and
|
||||
// and e was apparently not passed in as a macro parameter
|
||||
e.getLocation().getStartLine() = mi.getLocation().getStartLine() and
|
||||
e.getLocation().getStartColumn() = mi.getLocation().getStartColumn()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import cpp
|
||||
*/
|
||||
predicate fopenCall(FunctionCall fc) {
|
||||
exists(Function f | f = fc.getTarget() |
|
||||
f.hasGlobalName("fopen") or
|
||||
f.hasGlobalOrStdName("fopen") or
|
||||
f.hasGlobalName("open") or
|
||||
f.hasGlobalName("_open") or
|
||||
f.hasGlobalName("_wopen") or
|
||||
@@ -23,7 +23,7 @@ predicate fopenCall(FunctionCall fc) {
|
||||
*/
|
||||
predicate fcloseCall(FunctionCall fc, Expr closed) {
|
||||
exists(Function f | f = fc.getTarget() |
|
||||
f.hasGlobalName("fclose") and
|
||||
f.hasGlobalOrStdName("fclose") and
|
||||
closed = fc.getArgument(0)
|
||||
or
|
||||
f.hasGlobalName("close") and
|
||||
@@ -32,7 +32,7 @@ predicate fcloseCall(FunctionCall fc, Expr closed) {
|
||||
f.hasGlobalName("_close") and
|
||||
closed = fc.getArgument(0)
|
||||
or
|
||||
f.hasGlobalName("CloseHandle") and
|
||||
f.hasGlobalOrStdName("CloseHandle") and
|
||||
closed = fc.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,25 +8,32 @@ import semmle.code.cpp.commons.StringAnalysis
|
||||
import semmle.code.cpp.models.interfaces.FormattingFunction
|
||||
import semmle.code.cpp.models.implementations.Printf
|
||||
|
||||
class PrintfFormatAttribute extends FormatAttribute {
|
||||
PrintfFormatAttribute() {
|
||||
getArchetype() = "printf" or
|
||||
getArchetype() = "__printf__"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that can be identified as a `printf` style formatting
|
||||
* function by its use of the GNU `format` attribute.
|
||||
*/
|
||||
class AttributeFormattingFunction extends FormattingFunction {
|
||||
FormatAttribute printf_attrib;
|
||||
|
||||
override string getCanonicalQLClass() { result = "AttributeFormattingFunction" }
|
||||
|
||||
AttributeFormattingFunction() {
|
||||
printf_attrib = getAnAttribute() and
|
||||
(
|
||||
printf_attrib.getArchetype() = "printf" or
|
||||
printf_attrib.getArchetype() = "__printf__"
|
||||
) and
|
||||
exists(printf_attrib.getFirstFormatArgIndex()) // exclude `vprintf` style format functions
|
||||
exists(PrintfFormatAttribute printf_attrib |
|
||||
printf_attrib = getAnAttribute() and
|
||||
exists(printf_attrib.getFirstFormatArgIndex()) // exclude `vprintf` style format functions
|
||||
)
|
||||
}
|
||||
|
||||
override int getFormatParameterIndex() { result = printf_attrib.getFormatIndex() }
|
||||
override int getFormatParameterIndex() {
|
||||
forex(PrintfFormatAttribute printf_attrib | printf_attrib = getAnAttribute() |
|
||||
result = printf_attrib.getFormatIndex()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,15 +131,17 @@ class FormattingFunctionCall extends Expr {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument corresponding to the nth conversion specifier
|
||||
* Gets the argument corresponding to the nth conversion specifier.
|
||||
*/
|
||||
Expr getConversionArgument(int n) {
|
||||
exists(FormatLiteral fl, int b, int o |
|
||||
exists(FormatLiteral fl |
|
||||
fl = this.getFormat() and
|
||||
b = sum(int i, int toSum | i < n and toSum = fl.getNumArgNeeded(i) | toSum) and
|
||||
o = fl.getNumArgNeeded(n) and
|
||||
o > 0 and
|
||||
result = this.getFormatArgument(b + o - 1)
|
||||
(
|
||||
result = this.getFormatArgument(fl.getParameterFieldValue(n))
|
||||
or
|
||||
result = this.getFormatArgument(fl.getFormatArgumentIndexFor(n, 2)) and
|
||||
not exists(fl.getParameterFieldValue(n))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -142,11 +151,14 @@ class FormattingFunctionCall extends Expr {
|
||||
* an explicit minimum field width).
|
||||
*/
|
||||
Expr getMinFieldWidthArgument(int n) {
|
||||
exists(FormatLiteral fl, int b |
|
||||
exists(FormatLiteral fl |
|
||||
fl = this.getFormat() and
|
||||
b = sum(int i, int toSum | i < n and toSum = fl.getNumArgNeeded(i) | toSum) and
|
||||
fl.hasImplicitMinFieldWidth(n) and
|
||||
result = this.getFormatArgument(b)
|
||||
(
|
||||
result = this.getFormatArgument(fl.getMinFieldWidthParameterFieldValue(n))
|
||||
or
|
||||
result = this.getFormatArgument(fl.getFormatArgumentIndexFor(n, 0)) and
|
||||
not exists(fl.getMinFieldWidthParameterFieldValue(n))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -156,12 +168,14 @@ class FormattingFunctionCall extends Expr {
|
||||
* precision).
|
||||
*/
|
||||
Expr getPrecisionArgument(int n) {
|
||||
exists(FormatLiteral fl, int b, int o |
|
||||
exists(FormatLiteral fl |
|
||||
fl = this.getFormat() and
|
||||
b = sum(int i, int toSum | i < n and toSum = fl.getNumArgNeeded(i) | toSum) and
|
||||
(if fl.hasImplicitMinFieldWidth(n) then o = 1 else o = 0) and
|
||||
fl.hasImplicitPrecision(n) and
|
||||
result = this.getFormatArgument(b + o)
|
||||
(
|
||||
result = this.getFormatArgument(fl.getPrecisionParameterFieldValue(n))
|
||||
or
|
||||
result = this.getFormatArgument(fl.getFormatArgumentIndexFor(n, 1)) and
|
||||
not exists(fl.getPrecisionParameterFieldValue(n))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -361,6 +375,14 @@ class FormatLiteral extends Literal {
|
||||
*/
|
||||
string getParameterField(int n) { this.parseConvSpec(n, _, result, _, _, _, _, _) }
|
||||
|
||||
/**
|
||||
* Gets the parameter field of the nth conversion specifier (if it has one) as a
|
||||
* zero-based number.
|
||||
*/
|
||||
int getParameterFieldValue(int n) {
|
||||
result = this.getParameterField(n).regexpCapture("([0-9]*)\\$", 1).toInt() - 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the flags of the nth conversion specifier.
|
||||
*/
|
||||
@@ -430,6 +452,14 @@ class FormatLiteral extends Literal {
|
||||
*/
|
||||
int getMinFieldWidth(int n) { result = this.getMinFieldWidthOpt(n).toInt() }
|
||||
|
||||
/**
|
||||
* Gets the zero-based parameter number of the minimum field width of the nth
|
||||
* conversion specifier, if it is implicit and uses a parameter field (such as `*1$`).
|
||||
*/
|
||||
int getMinFieldWidthParameterFieldValue(int n) {
|
||||
result = this.getMinFieldWidthOpt(n).regexpCapture("\\*([0-9]*)\\$", 1).toInt() - 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the precision of the nth conversion specifier (empty string if none is given).
|
||||
*/
|
||||
@@ -460,6 +490,14 @@ class FormatLiteral extends Literal {
|
||||
else result = this.getPrecisionOpt(n).regexpCapture("\\.([0-9]*)", 1).toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the zero-based parameter number of the precision of the nth conversion
|
||||
* specifier, if it is implicit and uses a parameter field (such as `*1$`).
|
||||
*/
|
||||
int getPrecisionParameterFieldValue(int n) {
|
||||
result = this.getPrecisionOpt(n).regexpCapture("\\.\\*([0-9]*)\\$", 1).toInt() - 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the length flag of the nth conversion specifier.
|
||||
*/
|
||||
@@ -777,19 +815,49 @@ class FormatLiteral extends Literal {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the nth conversion specifier of this format string (if `mode = 2`), it's
|
||||
* minimum field width (if `mode = 0`) or it's precision (if `mode = 1`) requires a
|
||||
* format argument.
|
||||
*
|
||||
* Most conversion specifiers require a format argument, whereas minimum field width
|
||||
* and precision only require a format argument if they are present and a `*` was
|
||||
* used for it's value in the format string.
|
||||
*/
|
||||
private predicate hasFormatArgumentIndexFor(int n, int mode) {
|
||||
mode = 0 and
|
||||
this.hasImplicitMinFieldWidth(n)
|
||||
or
|
||||
mode = 1 and
|
||||
this.hasImplicitPrecision(n)
|
||||
or
|
||||
mode = 2 and
|
||||
exists(this.getConvSpecOffset(n)) and
|
||||
not this.getConversionChar(n) = "m"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the computed format argument index for the nth conversion specifier of this
|
||||
* format string (if `mode = 2`), it's minimum field width (if `mode = 0`) or it's
|
||||
* precision (if `mode = 1`). Has no result if that element is not present. Does
|
||||
* not account for positional arguments (`$`).
|
||||
*/
|
||||
int getFormatArgumentIndexFor(int n, int mode) {
|
||||
hasFormatArgumentIndexFor(n, mode) and
|
||||
(3 * n) + mode = rank[result + 1](int n2, int mode2 |
|
||||
hasFormatArgumentIndexFor(n2, mode2)
|
||||
|
|
||||
(3 * n2) + mode2
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of arguments required by the nth conversion specifier
|
||||
* of this format string.
|
||||
*/
|
||||
int getNumArgNeeded(int n) {
|
||||
exists(this.getConvSpecOffset(n)) and
|
||||
not this.getConversionChar(n) = "%" and
|
||||
exists(int n1, int n2, int n3 |
|
||||
(if this.hasImplicitMinFieldWidth(n) then n1 = 1 else n1 = 0) and
|
||||
(if this.hasImplicitPrecision(n) then n2 = 1 else n2 = 0) and
|
||||
(if this.getConversionChar(n) = "m" then n3 = 0 else n3 = 1) and
|
||||
result = n1 + n2 + n3
|
||||
)
|
||||
result = count(int mode | hasFormatArgumentIndexFor(n, mode))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -801,7 +869,7 @@ class FormatLiteral extends Literal {
|
||||
// At least one conversion specifier has a parameter field, in which case,
|
||||
// they all should have.
|
||||
result = max(string s | this.getParameterField(_) = s + "$" | s.toInt())
|
||||
else result = sum(int n, int toSum | toSum = this.getNumArgNeeded(n) | toSum)
|
||||
else result = count(int n, int mode | hasFormatArgumentIndexFor(n, mode))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -53,8 +53,8 @@ class AnalysedString extends Expr {
|
||||
*/
|
||||
class StrlenCall extends FunctionCall {
|
||||
StrlenCall() {
|
||||
this.getTarget().hasGlobalName("strlen") or
|
||||
this.getTarget().hasGlobalName("wcslen") or
|
||||
this.getTarget().hasGlobalOrStdName("strlen") or
|
||||
this.getTarget().hasGlobalOrStdName("wcslen") or
|
||||
this.getTarget().hasGlobalName("_mbslen") or
|
||||
this.getTarget().hasGlobalName("_mbslen_l") or
|
||||
this.getTarget().hasGlobalName("_mbstrlen") or
|
||||
|
||||
@@ -6,7 +6,7 @@ import Nullness
|
||||
*/
|
||||
predicate callDereferences(FunctionCall fc, int i) {
|
||||
exists(string name |
|
||||
fc.getTarget().hasGlobalName(name) and
|
||||
fc.getTarget().hasGlobalOrStdName(name) and
|
||||
(
|
||||
name = "bcopy" and i in [0 .. 1]
|
||||
or
|
||||
|
||||
@@ -264,9 +264,9 @@ predicate callMayReturnNull(Call call) {
|
||||
* Holds if `f` may, directly or indirectly, return a null literal.
|
||||
*/
|
||||
predicate mayReturnNull(Function f) {
|
||||
f.hasGlobalName("malloc")
|
||||
f.hasGlobalOrStdName("malloc")
|
||||
or
|
||||
f.hasGlobalName("calloc")
|
||||
f.hasGlobalOrStdName("calloc")
|
||||
or
|
||||
// f.hasGlobalName("strchr")
|
||||
// or
|
||||
|
||||
@@ -132,7 +132,7 @@ private predicate excludeNodeAndNodesBelow(Expr e) {
|
||||
* control flow in them.
|
||||
*/
|
||||
private predicate excludeNodesStrictlyBelow(Node n) {
|
||||
n instanceof BuiltInOperationOffsetOf
|
||||
n instanceof BuiltInOperationBuiltInOffsetOf
|
||||
or
|
||||
n instanceof BuiltInIntAddr
|
||||
or
|
||||
|
||||
@@ -258,8 +258,8 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
|
||||
viableImpl(call) = result.getCallable() and
|
||||
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
|
||||
viableCallable(call) = result.getCallable() and
|
||||
kind = result.getKind()
|
||||
}
|
||||
|
||||
@@ -313,22 +313,23 @@ private predicate nodeCandFwd1(Node node, Configuration config) {
|
||||
viableParamArg(_, node, arg)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
nodeCandFwd1(mid, config) and
|
||||
parameterValueFlowsToUpdate(p, mid) and
|
||||
viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
|
||||
nodeCandFwd1(ret, config) and
|
||||
getReturnPosition(ret) = viableReturnPos(call, kind) and
|
||||
node = getAnOutNode(call, kind)
|
||||
exists(DataFlowCall call, ReturnPosition pos, ReturnKindExt kind |
|
||||
nodeCandFwd1ReturnPosition(pos, config) and
|
||||
pos = viableReturnPos(call, kind) and
|
||||
node = kind.getAnOutNode(call)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd1ReturnPosition(ReturnPosition pos, Configuration config) {
|
||||
exists(ReturnNodeExt ret |
|
||||
nodeCandFwd1(ret, config) and
|
||||
getReturnPosition(ret) = pos
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
@@ -403,22 +404,23 @@ private predicate nodeCand1(Node node, Configuration config) {
|
||||
nodeCand1(param, config)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node) and
|
||||
viableParamArg(_, p, mid.getPreUpdateNode()) and
|
||||
nodeCand1(mid, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind, OutNode out |
|
||||
nodeCand1(out, config) and
|
||||
getReturnPosition(node) = viableReturnPos(call, kind) and
|
||||
out = getAnOutNode(call, kind)
|
||||
exists(ReturnPosition pos |
|
||||
nodeCand1ReturnPosition(pos, config) and
|
||||
getReturnPosition(node) = pos
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate nodeCand1ReturnPosition(ReturnPosition pos, Configuration config) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind, Node out |
|
||||
nodeCand1(out, config) and
|
||||
pos = viableReturnPos(call, kind) and
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
@@ -565,28 +567,24 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable(
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnPosition getReturnPosition1(Node node, Configuration config) {
|
||||
result = getReturnPosition(node) and
|
||||
nodeCand1(node, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow out of a callable from `node1` to `node2`, either
|
||||
* through a `ReturnNode` or through an argument that has been mutated, and
|
||||
* that this step is part of a path from a source to a sink.
|
||||
*/
|
||||
private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) {
|
||||
nodeCand1(node1, unbind(config)) and
|
||||
nodeCand1(node2, config) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
(
|
||||
// flow out of an argument
|
||||
exists(ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node1) and
|
||||
viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
getReturnPosition(node1) = viableReturnPos(call, kind) and
|
||||
node2 = getAnOutNode(call, kind)
|
||||
)
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
|
||||
node2 = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1762,8 +1760,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap))
|
||||
or
|
||||
pathOutOfArgument(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
or
|
||||
pathOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
@@ -1797,9 +1793,9 @@ private predicate pathOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallCo
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
private predicate pathOutOfCallable1(
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
pathOutOfCallable0(mid, pos, innercc) and
|
||||
@@ -1816,29 +1812,9 @@ private predicate pathOutOfCallable1(
|
||||
* is a return from a callable and is recorded by `cc`, if needed.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnKind kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate pathOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
|
||||
exists(
|
||||
PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
|
||||
DataFlowCall call, ArgumentNode arg
|
||||
|
|
||||
mid.getNode() = n and
|
||||
parameterValueFlowsToUpdate(p, n) and
|
||||
innercc = mid.getCallContext() and
|
||||
p.isParameterOf(callable, i) and
|
||||
resolveReturn(innercc, callable, call) and
|
||||
node.getPreUpdateNode() = arg and
|
||||
arg.argumentOf(call, i) and
|
||||
flow(node, unbind(mid.getConfiguration()))
|
||||
|
|
||||
if reducedViableImplInReturn(callable, call)
|
||||
then cc = TReturn(callable, call)
|
||||
else cc = TAnyCallContext()
|
||||
private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
|
||||
exists(ReturnKindExt kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1900,9 +1876,9 @@ private predicate pathIntoCallable(
|
||||
/** Holds if data may flow from `p` to a return of kind `kind`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, AccessPathNil apnil, Configuration config
|
||||
ParameterNode p, ReturnKindExt kind, CallContextCall cc, AccessPathNil apnil, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
exists(PathNodeMid mid, ReturnNodeExt ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -1917,14 +1893,14 @@ private predicate paramFlowsThrough(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
private predicate pathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc, AccessPathNil apnil
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPathNil apnil
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
pathIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, kind, innercc, apnil, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc) and
|
||||
not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc) and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
@@ -1934,12 +1910,10 @@ private predicate pathThroughCallable0(
|
||||
* The context `cc` is restored to its value prior to entering the callable.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathThroughCallable(
|
||||
PathNodeMid mid, OutNode out, CallContext cc, AccessPathNil apnil
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPathNil apnil) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
pathThroughCallable0(call, mid, kind, cc, apnil) and
|
||||
out = getAnOutNode(call, kind)
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1996,16 +1970,10 @@ private module FlowExploration {
|
||||
// flow into callable
|
||||
viableParamArg(_, node2, node1)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node1) and
|
||||
viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
getReturnPosition(node1) = viableReturnPos(call, kind) and
|
||||
node2 = getAnOutNode(call, kind)
|
||||
node2 = kind.getAnOutNode(call)
|
||||
)
|
||||
|
|
||||
c1 = node1.getEnclosingCallable() and
|
||||
@@ -2250,8 +2218,6 @@ private module FlowExploration {
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
)
|
||||
or
|
||||
partialPathOutOfArgument(mid, node, cc, ap, config)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, _, ap, config)
|
||||
or
|
||||
partialPathOutOfCallable(mid, node, cc, ap, config)
|
||||
@@ -2310,7 +2276,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathOutOfCallable1(
|
||||
PartialPathNodePriv mid, DataFlowCall call, ReturnKind kind, CallContext cc,
|
||||
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
@@ -2324,36 +2290,12 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable(
|
||||
PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKind kind, DataFlowCall call |
|
||||
exists(ReturnKindExt kind, DataFlowCall call |
|
||||
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
||||
|
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfArgument(
|
||||
PartialPathNodePriv mid, PostUpdateNode node, CallContext cc, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
|
||||
DataFlowCall call, ArgumentNode arg
|
||||
|
|
||||
mid.getNode() = n and
|
||||
parameterValueFlowsToUpdate(p, n) and
|
||||
innercc = mid.getCallContext() and
|
||||
p.isParameterOf(callable, i) and
|
||||
resolveReturn(innercc, callable, call) and
|
||||
node.getPreUpdateNode() = arg and
|
||||
arg.argumentOf(call, i) and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
|
|
||||
if reducedViableImplInReturn(callable, call)
|
||||
then cc = TReturn(callable, call)
|
||||
else cc = TAnyCallContext()
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2400,10 +2342,10 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThroughInPartialPath(
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, PartialAccessPathNil apnil,
|
||||
ParameterNode p, ReturnKindExt kind, CallContextCall cc, PartialAccessPathNil apnil,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid, ReturnNode ret |
|
||||
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -2420,23 +2362,23 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodePriv mid, ReturnKind kind, CallContext cc,
|
||||
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPathNil apnil, Configuration config
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc, PartialAccessPathNil midapnil |
|
||||
partialPathIntoCallable(mid, p, cc, innercc, call, midapnil, config) and
|
||||
paramFlowsThroughInPartialPath(p, kind, innercc, apnil, config) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc)
|
||||
not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathThroughCallable(
|
||||
PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPathNil apnil,
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPathNil apnil,
|
||||
Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
partialPathThroughCallable0(call, mid, kind, cc, apnil, config) and
|
||||
out = getAnOutNode(call, kind)
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -258,8 +258,8 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
|
||||
viableImpl(call) = result.getCallable() and
|
||||
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
|
||||
viableCallable(call) = result.getCallable() and
|
||||
kind = result.getKind()
|
||||
}
|
||||
|
||||
@@ -313,22 +313,23 @@ private predicate nodeCandFwd1(Node node, Configuration config) {
|
||||
viableParamArg(_, node, arg)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
nodeCandFwd1(mid, config) and
|
||||
parameterValueFlowsToUpdate(p, mid) and
|
||||
viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
|
||||
nodeCandFwd1(ret, config) and
|
||||
getReturnPosition(ret) = viableReturnPos(call, kind) and
|
||||
node = getAnOutNode(call, kind)
|
||||
exists(DataFlowCall call, ReturnPosition pos, ReturnKindExt kind |
|
||||
nodeCandFwd1ReturnPosition(pos, config) and
|
||||
pos = viableReturnPos(call, kind) and
|
||||
node = kind.getAnOutNode(call)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd1ReturnPosition(ReturnPosition pos, Configuration config) {
|
||||
exists(ReturnNodeExt ret |
|
||||
nodeCandFwd1(ret, config) and
|
||||
getReturnPosition(ret) = pos
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
@@ -403,22 +404,23 @@ private predicate nodeCand1(Node node, Configuration config) {
|
||||
nodeCand1(param, config)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node) and
|
||||
viableParamArg(_, p, mid.getPreUpdateNode()) and
|
||||
nodeCand1(mid, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind, OutNode out |
|
||||
nodeCand1(out, config) and
|
||||
getReturnPosition(node) = viableReturnPos(call, kind) and
|
||||
out = getAnOutNode(call, kind)
|
||||
exists(ReturnPosition pos |
|
||||
nodeCand1ReturnPosition(pos, config) and
|
||||
getReturnPosition(node) = pos
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate nodeCand1ReturnPosition(ReturnPosition pos, Configuration config) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind, Node out |
|
||||
nodeCand1(out, config) and
|
||||
pos = viableReturnPos(call, kind) and
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
@@ -565,28 +567,24 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable(
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnPosition getReturnPosition1(Node node, Configuration config) {
|
||||
result = getReturnPosition(node) and
|
||||
nodeCand1(node, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow out of a callable from `node1` to `node2`, either
|
||||
* through a `ReturnNode` or through an argument that has been mutated, and
|
||||
* that this step is part of a path from a source to a sink.
|
||||
*/
|
||||
private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) {
|
||||
nodeCand1(node1, unbind(config)) and
|
||||
nodeCand1(node2, config) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
(
|
||||
// flow out of an argument
|
||||
exists(ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node1) and
|
||||
viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
getReturnPosition(node1) = viableReturnPos(call, kind) and
|
||||
node2 = getAnOutNode(call, kind)
|
||||
)
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
|
||||
node2 = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1762,8 +1760,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap))
|
||||
or
|
||||
pathOutOfArgument(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
or
|
||||
pathOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
@@ -1797,9 +1793,9 @@ private predicate pathOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallCo
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
private predicate pathOutOfCallable1(
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
pathOutOfCallable0(mid, pos, innercc) and
|
||||
@@ -1816,29 +1812,9 @@ private predicate pathOutOfCallable1(
|
||||
* is a return from a callable and is recorded by `cc`, if needed.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnKind kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate pathOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
|
||||
exists(
|
||||
PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
|
||||
DataFlowCall call, ArgumentNode arg
|
||||
|
|
||||
mid.getNode() = n and
|
||||
parameterValueFlowsToUpdate(p, n) and
|
||||
innercc = mid.getCallContext() and
|
||||
p.isParameterOf(callable, i) and
|
||||
resolveReturn(innercc, callable, call) and
|
||||
node.getPreUpdateNode() = arg and
|
||||
arg.argumentOf(call, i) and
|
||||
flow(node, unbind(mid.getConfiguration()))
|
||||
|
|
||||
if reducedViableImplInReturn(callable, call)
|
||||
then cc = TReturn(callable, call)
|
||||
else cc = TAnyCallContext()
|
||||
private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
|
||||
exists(ReturnKindExt kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1900,9 +1876,9 @@ private predicate pathIntoCallable(
|
||||
/** Holds if data may flow from `p` to a return of kind `kind`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, AccessPathNil apnil, Configuration config
|
||||
ParameterNode p, ReturnKindExt kind, CallContextCall cc, AccessPathNil apnil, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
exists(PathNodeMid mid, ReturnNodeExt ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -1917,14 +1893,14 @@ private predicate paramFlowsThrough(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
private predicate pathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc, AccessPathNil apnil
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPathNil apnil
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
pathIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, kind, innercc, apnil, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc) and
|
||||
not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc) and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
@@ -1934,12 +1910,10 @@ private predicate pathThroughCallable0(
|
||||
* The context `cc` is restored to its value prior to entering the callable.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathThroughCallable(
|
||||
PathNodeMid mid, OutNode out, CallContext cc, AccessPathNil apnil
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPathNil apnil) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
pathThroughCallable0(call, mid, kind, cc, apnil) and
|
||||
out = getAnOutNode(call, kind)
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1996,16 +1970,10 @@ private module FlowExploration {
|
||||
// flow into callable
|
||||
viableParamArg(_, node2, node1)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node1) and
|
||||
viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
getReturnPosition(node1) = viableReturnPos(call, kind) and
|
||||
node2 = getAnOutNode(call, kind)
|
||||
node2 = kind.getAnOutNode(call)
|
||||
)
|
||||
|
|
||||
c1 = node1.getEnclosingCallable() and
|
||||
@@ -2250,8 +2218,6 @@ private module FlowExploration {
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
)
|
||||
or
|
||||
partialPathOutOfArgument(mid, node, cc, ap, config)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, _, ap, config)
|
||||
or
|
||||
partialPathOutOfCallable(mid, node, cc, ap, config)
|
||||
@@ -2310,7 +2276,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathOutOfCallable1(
|
||||
PartialPathNodePriv mid, DataFlowCall call, ReturnKind kind, CallContext cc,
|
||||
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
@@ -2324,36 +2290,12 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable(
|
||||
PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKind kind, DataFlowCall call |
|
||||
exists(ReturnKindExt kind, DataFlowCall call |
|
||||
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
||||
|
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfArgument(
|
||||
PartialPathNodePriv mid, PostUpdateNode node, CallContext cc, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
|
||||
DataFlowCall call, ArgumentNode arg
|
||||
|
|
||||
mid.getNode() = n and
|
||||
parameterValueFlowsToUpdate(p, n) and
|
||||
innercc = mid.getCallContext() and
|
||||
p.isParameterOf(callable, i) and
|
||||
resolveReturn(innercc, callable, call) and
|
||||
node.getPreUpdateNode() = arg and
|
||||
arg.argumentOf(call, i) and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
|
|
||||
if reducedViableImplInReturn(callable, call)
|
||||
then cc = TReturn(callable, call)
|
||||
else cc = TAnyCallContext()
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2400,10 +2342,10 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThroughInPartialPath(
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, PartialAccessPathNil apnil,
|
||||
ParameterNode p, ReturnKindExt kind, CallContextCall cc, PartialAccessPathNil apnil,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid, ReturnNode ret |
|
||||
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -2420,23 +2362,23 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodePriv mid, ReturnKind kind, CallContext cc,
|
||||
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPathNil apnil, Configuration config
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc, PartialAccessPathNil midapnil |
|
||||
partialPathIntoCallable(mid, p, cc, innercc, call, midapnil, config) and
|
||||
paramFlowsThroughInPartialPath(p, kind, innercc, apnil, config) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc)
|
||||
not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathThroughCallable(
|
||||
PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPathNil apnil,
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPathNil apnil,
|
||||
Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
partialPathThroughCallable0(call, mid, kind, cc, apnil, config) and
|
||||
out = getAnOutNode(call, kind)
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -258,8 +258,8 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
|
||||
viableImpl(call) = result.getCallable() and
|
||||
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
|
||||
viableCallable(call) = result.getCallable() and
|
||||
kind = result.getKind()
|
||||
}
|
||||
|
||||
@@ -313,22 +313,23 @@ private predicate nodeCandFwd1(Node node, Configuration config) {
|
||||
viableParamArg(_, node, arg)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
nodeCandFwd1(mid, config) and
|
||||
parameterValueFlowsToUpdate(p, mid) and
|
||||
viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
|
||||
nodeCandFwd1(ret, config) and
|
||||
getReturnPosition(ret) = viableReturnPos(call, kind) and
|
||||
node = getAnOutNode(call, kind)
|
||||
exists(DataFlowCall call, ReturnPosition pos, ReturnKindExt kind |
|
||||
nodeCandFwd1ReturnPosition(pos, config) and
|
||||
pos = viableReturnPos(call, kind) and
|
||||
node = kind.getAnOutNode(call)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd1ReturnPosition(ReturnPosition pos, Configuration config) {
|
||||
exists(ReturnNodeExt ret |
|
||||
nodeCandFwd1(ret, config) and
|
||||
getReturnPosition(ret) = pos
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
@@ -403,22 +404,23 @@ private predicate nodeCand1(Node node, Configuration config) {
|
||||
nodeCand1(param, config)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node) and
|
||||
viableParamArg(_, p, mid.getPreUpdateNode()) and
|
||||
nodeCand1(mid, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind, OutNode out |
|
||||
nodeCand1(out, config) and
|
||||
getReturnPosition(node) = viableReturnPos(call, kind) and
|
||||
out = getAnOutNode(call, kind)
|
||||
exists(ReturnPosition pos |
|
||||
nodeCand1ReturnPosition(pos, config) and
|
||||
getReturnPosition(node) = pos
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate nodeCand1ReturnPosition(ReturnPosition pos, Configuration config) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind, Node out |
|
||||
nodeCand1(out, config) and
|
||||
pos = viableReturnPos(call, kind) and
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
@@ -565,28 +567,24 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable(
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnPosition getReturnPosition1(Node node, Configuration config) {
|
||||
result = getReturnPosition(node) and
|
||||
nodeCand1(node, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow out of a callable from `node1` to `node2`, either
|
||||
* through a `ReturnNode` or through an argument that has been mutated, and
|
||||
* that this step is part of a path from a source to a sink.
|
||||
*/
|
||||
private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) {
|
||||
nodeCand1(node1, unbind(config)) and
|
||||
nodeCand1(node2, config) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
(
|
||||
// flow out of an argument
|
||||
exists(ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node1) and
|
||||
viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
getReturnPosition(node1) = viableReturnPos(call, kind) and
|
||||
node2 = getAnOutNode(call, kind)
|
||||
)
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
|
||||
node2 = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1762,8 +1760,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap))
|
||||
or
|
||||
pathOutOfArgument(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
or
|
||||
pathOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
@@ -1797,9 +1793,9 @@ private predicate pathOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallCo
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
private predicate pathOutOfCallable1(
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
pathOutOfCallable0(mid, pos, innercc) and
|
||||
@@ -1816,29 +1812,9 @@ private predicate pathOutOfCallable1(
|
||||
* is a return from a callable and is recorded by `cc`, if needed.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnKind kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate pathOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
|
||||
exists(
|
||||
PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
|
||||
DataFlowCall call, ArgumentNode arg
|
||||
|
|
||||
mid.getNode() = n and
|
||||
parameterValueFlowsToUpdate(p, n) and
|
||||
innercc = mid.getCallContext() and
|
||||
p.isParameterOf(callable, i) and
|
||||
resolveReturn(innercc, callable, call) and
|
||||
node.getPreUpdateNode() = arg and
|
||||
arg.argumentOf(call, i) and
|
||||
flow(node, unbind(mid.getConfiguration()))
|
||||
|
|
||||
if reducedViableImplInReturn(callable, call)
|
||||
then cc = TReturn(callable, call)
|
||||
else cc = TAnyCallContext()
|
||||
private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
|
||||
exists(ReturnKindExt kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1900,9 +1876,9 @@ private predicate pathIntoCallable(
|
||||
/** Holds if data may flow from `p` to a return of kind `kind`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, AccessPathNil apnil, Configuration config
|
||||
ParameterNode p, ReturnKindExt kind, CallContextCall cc, AccessPathNil apnil, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
exists(PathNodeMid mid, ReturnNodeExt ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -1917,14 +1893,14 @@ private predicate paramFlowsThrough(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
private predicate pathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc, AccessPathNil apnil
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPathNil apnil
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
pathIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, kind, innercc, apnil, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc) and
|
||||
not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc) and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
@@ -1934,12 +1910,10 @@ private predicate pathThroughCallable0(
|
||||
* The context `cc` is restored to its value prior to entering the callable.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathThroughCallable(
|
||||
PathNodeMid mid, OutNode out, CallContext cc, AccessPathNil apnil
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPathNil apnil) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
pathThroughCallable0(call, mid, kind, cc, apnil) and
|
||||
out = getAnOutNode(call, kind)
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1996,16 +1970,10 @@ private module FlowExploration {
|
||||
// flow into callable
|
||||
viableParamArg(_, node2, node1)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node1) and
|
||||
viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
getReturnPosition(node1) = viableReturnPos(call, kind) and
|
||||
node2 = getAnOutNode(call, kind)
|
||||
node2 = kind.getAnOutNode(call)
|
||||
)
|
||||
|
|
||||
c1 = node1.getEnclosingCallable() and
|
||||
@@ -2250,8 +2218,6 @@ private module FlowExploration {
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
)
|
||||
or
|
||||
partialPathOutOfArgument(mid, node, cc, ap, config)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, _, ap, config)
|
||||
or
|
||||
partialPathOutOfCallable(mid, node, cc, ap, config)
|
||||
@@ -2310,7 +2276,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathOutOfCallable1(
|
||||
PartialPathNodePriv mid, DataFlowCall call, ReturnKind kind, CallContext cc,
|
||||
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
@@ -2324,36 +2290,12 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable(
|
||||
PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKind kind, DataFlowCall call |
|
||||
exists(ReturnKindExt kind, DataFlowCall call |
|
||||
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
||||
|
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfArgument(
|
||||
PartialPathNodePriv mid, PostUpdateNode node, CallContext cc, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
|
||||
DataFlowCall call, ArgumentNode arg
|
||||
|
|
||||
mid.getNode() = n and
|
||||
parameterValueFlowsToUpdate(p, n) and
|
||||
innercc = mid.getCallContext() and
|
||||
p.isParameterOf(callable, i) and
|
||||
resolveReturn(innercc, callable, call) and
|
||||
node.getPreUpdateNode() = arg and
|
||||
arg.argumentOf(call, i) and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
|
|
||||
if reducedViableImplInReturn(callable, call)
|
||||
then cc = TReturn(callable, call)
|
||||
else cc = TAnyCallContext()
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2400,10 +2342,10 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThroughInPartialPath(
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, PartialAccessPathNil apnil,
|
||||
ParameterNode p, ReturnKindExt kind, CallContextCall cc, PartialAccessPathNil apnil,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid, ReturnNode ret |
|
||||
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -2420,23 +2362,23 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodePriv mid, ReturnKind kind, CallContext cc,
|
||||
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPathNil apnil, Configuration config
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc, PartialAccessPathNil midapnil |
|
||||
partialPathIntoCallable(mid, p, cc, innercc, call, midapnil, config) and
|
||||
paramFlowsThroughInPartialPath(p, kind, innercc, apnil, config) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc)
|
||||
not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathThroughCallable(
|
||||
PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPathNil apnil,
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPathNil apnil,
|
||||
Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
partialPathThroughCallable0(call, mid, kind, cc, apnil, config) and
|
||||
out = getAnOutNode(call, kind)
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -258,8 +258,8 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
|
||||
viableImpl(call) = result.getCallable() and
|
||||
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
|
||||
viableCallable(call) = result.getCallable() and
|
||||
kind = result.getKind()
|
||||
}
|
||||
|
||||
@@ -313,22 +313,23 @@ private predicate nodeCandFwd1(Node node, Configuration config) {
|
||||
viableParamArg(_, node, arg)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
nodeCandFwd1(mid, config) and
|
||||
parameterValueFlowsToUpdate(p, mid) and
|
||||
viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
|
||||
nodeCandFwd1(ret, config) and
|
||||
getReturnPosition(ret) = viableReturnPos(call, kind) and
|
||||
node = getAnOutNode(call, kind)
|
||||
exists(DataFlowCall call, ReturnPosition pos, ReturnKindExt kind |
|
||||
nodeCandFwd1ReturnPosition(pos, config) and
|
||||
pos = viableReturnPos(call, kind) and
|
||||
node = kind.getAnOutNode(call)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd1ReturnPosition(ReturnPosition pos, Configuration config) {
|
||||
exists(ReturnNodeExt ret |
|
||||
nodeCandFwd1(ret, config) and
|
||||
getReturnPosition(ret) = pos
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
@@ -403,22 +404,23 @@ private predicate nodeCand1(Node node, Configuration config) {
|
||||
nodeCand1(param, config)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node) and
|
||||
viableParamArg(_, p, mid.getPreUpdateNode()) and
|
||||
nodeCand1(mid, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind, OutNode out |
|
||||
nodeCand1(out, config) and
|
||||
getReturnPosition(node) = viableReturnPos(call, kind) and
|
||||
out = getAnOutNode(call, kind)
|
||||
exists(ReturnPosition pos |
|
||||
nodeCand1ReturnPosition(pos, config) and
|
||||
getReturnPosition(node) = pos
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate nodeCand1ReturnPosition(ReturnPosition pos, Configuration config) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind, Node out |
|
||||
nodeCand1(out, config) and
|
||||
pos = viableReturnPos(call, kind) and
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
@@ -565,28 +567,24 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable(
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnPosition getReturnPosition1(Node node, Configuration config) {
|
||||
result = getReturnPosition(node) and
|
||||
nodeCand1(node, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow out of a callable from `node1` to `node2`, either
|
||||
* through a `ReturnNode` or through an argument that has been mutated, and
|
||||
* that this step is part of a path from a source to a sink.
|
||||
*/
|
||||
private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) {
|
||||
nodeCand1(node1, unbind(config)) and
|
||||
nodeCand1(node2, config) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
(
|
||||
// flow out of an argument
|
||||
exists(ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node1) and
|
||||
viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
getReturnPosition(node1) = viableReturnPos(call, kind) and
|
||||
node2 = getAnOutNode(call, kind)
|
||||
)
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
|
||||
node2 = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1762,8 +1760,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap))
|
||||
or
|
||||
pathOutOfArgument(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
or
|
||||
pathOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
@@ -1797,9 +1793,9 @@ private predicate pathOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallCo
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
private predicate pathOutOfCallable1(
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
pathOutOfCallable0(mid, pos, innercc) and
|
||||
@@ -1816,29 +1812,9 @@ private predicate pathOutOfCallable1(
|
||||
* is a return from a callable and is recorded by `cc`, if needed.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnKind kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate pathOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
|
||||
exists(
|
||||
PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
|
||||
DataFlowCall call, ArgumentNode arg
|
||||
|
|
||||
mid.getNode() = n and
|
||||
parameterValueFlowsToUpdate(p, n) and
|
||||
innercc = mid.getCallContext() and
|
||||
p.isParameterOf(callable, i) and
|
||||
resolveReturn(innercc, callable, call) and
|
||||
node.getPreUpdateNode() = arg and
|
||||
arg.argumentOf(call, i) and
|
||||
flow(node, unbind(mid.getConfiguration()))
|
||||
|
|
||||
if reducedViableImplInReturn(callable, call)
|
||||
then cc = TReturn(callable, call)
|
||||
else cc = TAnyCallContext()
|
||||
private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
|
||||
exists(ReturnKindExt kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1900,9 +1876,9 @@ private predicate pathIntoCallable(
|
||||
/** Holds if data may flow from `p` to a return of kind `kind`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, AccessPathNil apnil, Configuration config
|
||||
ParameterNode p, ReturnKindExt kind, CallContextCall cc, AccessPathNil apnil, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
exists(PathNodeMid mid, ReturnNodeExt ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -1917,14 +1893,14 @@ private predicate paramFlowsThrough(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
private predicate pathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc, AccessPathNil apnil
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPathNil apnil
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
pathIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, kind, innercc, apnil, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc) and
|
||||
not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc) and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
@@ -1934,12 +1910,10 @@ private predicate pathThroughCallable0(
|
||||
* The context `cc` is restored to its value prior to entering the callable.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathThroughCallable(
|
||||
PathNodeMid mid, OutNode out, CallContext cc, AccessPathNil apnil
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPathNil apnil) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
pathThroughCallable0(call, mid, kind, cc, apnil) and
|
||||
out = getAnOutNode(call, kind)
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1996,16 +1970,10 @@ private module FlowExploration {
|
||||
// flow into callable
|
||||
viableParamArg(_, node2, node1)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node1) and
|
||||
viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
getReturnPosition(node1) = viableReturnPos(call, kind) and
|
||||
node2 = getAnOutNode(call, kind)
|
||||
node2 = kind.getAnOutNode(call)
|
||||
)
|
||||
|
|
||||
c1 = node1.getEnclosingCallable() and
|
||||
@@ -2250,8 +2218,6 @@ private module FlowExploration {
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
)
|
||||
or
|
||||
partialPathOutOfArgument(mid, node, cc, ap, config)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, _, ap, config)
|
||||
or
|
||||
partialPathOutOfCallable(mid, node, cc, ap, config)
|
||||
@@ -2310,7 +2276,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathOutOfCallable1(
|
||||
PartialPathNodePriv mid, DataFlowCall call, ReturnKind kind, CallContext cc,
|
||||
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
@@ -2324,36 +2290,12 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable(
|
||||
PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKind kind, DataFlowCall call |
|
||||
exists(ReturnKindExt kind, DataFlowCall call |
|
||||
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
||||
|
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfArgument(
|
||||
PartialPathNodePriv mid, PostUpdateNode node, CallContext cc, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
|
||||
DataFlowCall call, ArgumentNode arg
|
||||
|
|
||||
mid.getNode() = n and
|
||||
parameterValueFlowsToUpdate(p, n) and
|
||||
innercc = mid.getCallContext() and
|
||||
p.isParameterOf(callable, i) and
|
||||
resolveReturn(innercc, callable, call) and
|
||||
node.getPreUpdateNode() = arg and
|
||||
arg.argumentOf(call, i) and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
|
|
||||
if reducedViableImplInReturn(callable, call)
|
||||
then cc = TReturn(callable, call)
|
||||
else cc = TAnyCallContext()
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2400,10 +2342,10 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThroughInPartialPath(
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, PartialAccessPathNil apnil,
|
||||
ParameterNode p, ReturnKindExt kind, CallContextCall cc, PartialAccessPathNil apnil,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid, ReturnNode ret |
|
||||
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -2420,23 +2362,23 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodePriv mid, ReturnKind kind, CallContext cc,
|
||||
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPathNil apnil, Configuration config
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc, PartialAccessPathNil midapnil |
|
||||
partialPathIntoCallable(mid, p, cc, innercc, call, midapnil, config) and
|
||||
paramFlowsThroughInPartialPath(p, kind, innercc, apnil, config) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc)
|
||||
not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathThroughCallable(
|
||||
PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPathNil apnil,
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPathNil apnil,
|
||||
Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
partialPathThroughCallable0(call, mid, kind, cc, apnil, config) and
|
||||
out = getAnOutNode(call, kind)
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -615,7 +615,7 @@ private module ImplCommon {
|
||||
|
||||
cached
|
||||
newtype TReturnPosition =
|
||||
TReturnPosition0(DataFlowCallable c, ReturnKind kind) { returnPosition(_, c, kind) }
|
||||
TReturnPosition0(DataFlowCallable c, ReturnKindExt kind) { returnPosition(_, c, kind) }
|
||||
|
||||
cached
|
||||
newtype TLocalFlowCallContext =
|
||||
@@ -624,7 +624,7 @@ private module ImplCommon {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate returnPosition(ReturnNode ret, DataFlowCallable c, ReturnKind kind) {
|
||||
private predicate returnPosition(ReturnNodeExt ret, DataFlowCallable c, ReturnKindExt kind) {
|
||||
c = returnNodeGetEnclosingCallable(ret) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
@@ -736,10 +736,80 @@ private module ImplCommon {
|
||||
else result instanceof LocalCallContextAny
|
||||
}
|
||||
|
||||
/**
|
||||
* A node from which flow can return to the caller. This is either a regular
|
||||
* `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter.
|
||||
*/
|
||||
class ReturnNodeExt extends Node {
|
||||
ReturnNodeExt() {
|
||||
this instanceof ReturnNode or
|
||||
parameterValueFlowsToUpdate(_, this)
|
||||
}
|
||||
|
||||
/** Gets the kind of this returned value. */
|
||||
ReturnKindExt getKind() {
|
||||
result = TValueReturn(this.(ReturnNode).getKind())
|
||||
or
|
||||
exists(ParameterNode p, int pos |
|
||||
parameterValueFlowsToUpdate(p, this) and
|
||||
p.isParameterOf(_, pos) and
|
||||
result = TParamUpdate(pos)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TReturnKindExt =
|
||||
TValueReturn(ReturnKind kind) or
|
||||
TParamUpdate(int pos) {
|
||||
exists(ParameterNode p | parameterValueFlowsToUpdate(p, _) and p.isParameterOf(_, pos))
|
||||
}
|
||||
|
||||
/**
|
||||
* An extended return kind. A return kind describes how data can be returned
|
||||
* from a callable. This can either be through a returned value or an updated
|
||||
* parameter.
|
||||
*/
|
||||
abstract class ReturnKindExt extends TReturnKindExt {
|
||||
/** Gets a textual representation of this return kind. */
|
||||
abstract string toString();
|
||||
|
||||
/** Gets a node corresponding to data flow out of `call`. */
|
||||
abstract Node getAnOutNode(DataFlowCall call);
|
||||
}
|
||||
|
||||
class ValueReturnKind extends ReturnKindExt, TValueReturn {
|
||||
private ReturnKind kind;
|
||||
|
||||
ValueReturnKind() { this = TValueReturn(kind) }
|
||||
|
||||
ReturnKind getKind() { result = kind }
|
||||
|
||||
override string toString() { result = kind.toString() }
|
||||
|
||||
override Node getAnOutNode(DataFlowCall call) { result = getAnOutNode(call, this.getKind()) }
|
||||
}
|
||||
|
||||
class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
|
||||
private int pos;
|
||||
|
||||
ParamUpdateReturnKind() { this = TParamUpdate(pos) }
|
||||
|
||||
int getPosition() { result = pos }
|
||||
|
||||
override string toString() { result = "param update " + pos }
|
||||
|
||||
override Node getAnOutNode(DataFlowCall call) {
|
||||
exists(ArgumentNode arg |
|
||||
result.(PostUpdateNode).getPreUpdateNode() = arg and
|
||||
arg.argumentOf(call, this.getPosition())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A callable tagged with a relevant return kind. */
|
||||
class ReturnPosition extends TReturnPosition0 {
|
||||
private DataFlowCallable c;
|
||||
private ReturnKind kind;
|
||||
private ReturnKindExt kind;
|
||||
|
||||
ReturnPosition() { this = TReturnPosition0(c, kind) }
|
||||
|
||||
@@ -747,20 +817,20 @@ private module ImplCommon {
|
||||
DataFlowCallable getCallable() { result = c }
|
||||
|
||||
/** Gets the return kind. */
|
||||
ReturnKind getKind() { result = kind }
|
||||
ReturnKindExt getKind() { result = kind }
|
||||
|
||||
/** Gets a textual representation of this return position. */
|
||||
string toString() { result = "[" + kind + "] " + c }
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
DataFlowCallable returnNodeGetEnclosingCallable(ReturnNode ret) {
|
||||
DataFlowCallable returnNodeGetEnclosingCallable(ReturnNodeExt ret) {
|
||||
result = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
ReturnPosition getReturnPosition(ReturnNode ret) {
|
||||
exists(DataFlowCallable c, ReturnKind k | returnPosition(ret, c, k) |
|
||||
ReturnPosition getReturnPosition(ReturnNodeExt ret) {
|
||||
exists(DataFlowCallable c, ReturnKindExt k | returnPosition(ret, c, k) |
|
||||
result = TReturnPosition0(c, k)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -258,8 +258,8 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
|
||||
viableImpl(call) = result.getCallable() and
|
||||
private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
|
||||
viableCallable(call) = result.getCallable() and
|
||||
kind = result.getKind()
|
||||
}
|
||||
|
||||
@@ -313,22 +313,23 @@ private predicate nodeCandFwd1(Node node, Configuration config) {
|
||||
viableParamArg(_, node, arg)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
nodeCandFwd1(mid, config) and
|
||||
parameterValueFlowsToUpdate(p, mid) and
|
||||
viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
|
||||
nodeCandFwd1(ret, config) and
|
||||
getReturnPosition(ret) = viableReturnPos(call, kind) and
|
||||
node = getAnOutNode(call, kind)
|
||||
exists(DataFlowCall call, ReturnPosition pos, ReturnKindExt kind |
|
||||
nodeCandFwd1ReturnPosition(pos, config) and
|
||||
pos = viableReturnPos(call, kind) and
|
||||
node = kind.getAnOutNode(call)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate nodeCandFwd1ReturnPosition(ReturnPosition pos, Configuration config) {
|
||||
exists(ReturnNodeExt ret |
|
||||
nodeCandFwd1(ret, config) and
|
||||
getReturnPosition(ret) = pos
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
|
||||
exists(Node mid |
|
||||
@@ -403,22 +404,23 @@ private predicate nodeCand1(Node node, Configuration config) {
|
||||
nodeCand1(param, config)
|
||||
)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(PostUpdateNode mid, ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node) and
|
||||
viableParamArg(_, p, mid.getPreUpdateNode()) and
|
||||
nodeCand1(mid, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind, OutNode out |
|
||||
nodeCand1(out, config) and
|
||||
getReturnPosition(node) = viableReturnPos(call, kind) and
|
||||
out = getAnOutNode(call, kind)
|
||||
exists(ReturnPosition pos |
|
||||
nodeCand1ReturnPosition(pos, config) and
|
||||
getReturnPosition(node) = pos
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate nodeCand1ReturnPosition(ReturnPosition pos, Configuration config) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind, Node out |
|
||||
nodeCand1(out, config) and
|
||||
pos = viableReturnPos(call, kind) and
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
|
||||
*/
|
||||
@@ -565,28 +567,24 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable(
|
||||
simpleArgumentFlowsThrough(node1, node2, _, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private ReturnPosition getReturnPosition1(Node node, Configuration config) {
|
||||
result = getReturnPosition(node) and
|
||||
nodeCand1(node, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow out of a callable from `node1` to `node2`, either
|
||||
* through a `ReturnNode` or through an argument that has been mutated, and
|
||||
* that this step is part of a path from a source to a sink.
|
||||
*/
|
||||
private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) {
|
||||
nodeCand1(node1, unbind(config)) and
|
||||
nodeCand1(node2, config) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
(
|
||||
// flow out of an argument
|
||||
exists(ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node1) and
|
||||
viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
getReturnPosition(node1) = viableReturnPos(call, kind) and
|
||||
node2 = getAnOutNode(call, kind)
|
||||
)
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
|
||||
node2 = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1762,8 +1760,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
|
||||
or
|
||||
exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap))
|
||||
or
|
||||
pathOutOfArgument(mid, node, cc) and ap = mid.getAp()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
|
||||
or
|
||||
pathOutOfCallable(mid, node, cc) and ap = mid.getAp()
|
||||
@@ -1797,9 +1793,9 @@ private predicate pathOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallCo
|
||||
not innercc instanceof CallContextCall
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
private predicate pathOutOfCallable1(
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
|
||||
PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
pathOutOfCallable0(mid, pos, innercc) and
|
||||
@@ -1816,29 +1812,9 @@ private predicate pathOutOfCallable1(
|
||||
* is a return from a callable and is recorded by `cc`, if needed.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
|
||||
exists(ReturnKind kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate pathOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
|
||||
exists(
|
||||
PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
|
||||
DataFlowCall call, ArgumentNode arg
|
||||
|
|
||||
mid.getNode() = n and
|
||||
parameterValueFlowsToUpdate(p, n) and
|
||||
innercc = mid.getCallContext() and
|
||||
p.isParameterOf(callable, i) and
|
||||
resolveReturn(innercc, callable, call) and
|
||||
node.getPreUpdateNode() = arg and
|
||||
arg.argumentOf(call, i) and
|
||||
flow(node, unbind(mid.getConfiguration()))
|
||||
|
|
||||
if reducedViableImplInReturn(callable, call)
|
||||
then cc = TReturn(callable, call)
|
||||
else cc = TAnyCallContext()
|
||||
private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
|
||||
exists(ReturnKindExt kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1900,9 +1876,9 @@ private predicate pathIntoCallable(
|
||||
/** Holds if data may flow from `p` to a return of kind `kind`. */
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThrough(
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, AccessPathNil apnil, Configuration config
|
||||
ParameterNode p, ReturnKindExt kind, CallContextCall cc, AccessPathNil apnil, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, ReturnNode ret |
|
||||
exists(PathNodeMid mid, ReturnNodeExt ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -1917,14 +1893,14 @@ private predicate paramFlowsThrough(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
private predicate pathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc, AccessPathNil apnil
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPathNil apnil
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc |
|
||||
pathIntoCallable(mid, p, cc, innercc, call) and
|
||||
paramFlowsThrough(p, kind, innercc, apnil, unbind(mid.getConfiguration())) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc) and
|
||||
not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc) and
|
||||
mid.getAp() instanceof AccessPathNil
|
||||
)
|
||||
}
|
||||
@@ -1934,12 +1910,10 @@ private predicate pathThroughCallable0(
|
||||
* The context `cc` is restored to its value prior to entering the callable.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathThroughCallable(
|
||||
PathNodeMid mid, OutNode out, CallContext cc, AccessPathNil apnil
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPathNil apnil) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
pathThroughCallable0(call, mid, kind, cc, apnil) and
|
||||
out = getAnOutNode(call, kind)
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1996,16 +1970,10 @@ private module FlowExploration {
|
||||
// flow into callable
|
||||
viableParamArg(_, node2, node1)
|
||||
or
|
||||
// flow out of an argument
|
||||
exists(ParameterNode p |
|
||||
parameterValueFlowsToUpdate(p, node1) and
|
||||
viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
getReturnPosition(node1) = viableReturnPos(call, kind) and
|
||||
node2 = getAnOutNode(call, kind)
|
||||
node2 = kind.getAnOutNode(call)
|
||||
)
|
||||
|
|
||||
c1 = node1.getEnclosingCallable() and
|
||||
@@ -2250,8 +2218,6 @@ private module FlowExploration {
|
||||
apConsFwd(ap, f, ap0, config)
|
||||
)
|
||||
or
|
||||
partialPathOutOfArgument(mid, node, cc, ap, config)
|
||||
or
|
||||
partialPathIntoCallable(mid, node, _, cc, _, ap, config)
|
||||
or
|
||||
partialPathOutOfCallable(mid, node, cc, ap, config)
|
||||
@@ -2310,7 +2276,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathOutOfCallable1(
|
||||
PartialPathNodePriv mid, DataFlowCall call, ReturnKind kind, CallContext cc,
|
||||
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
@@ -2324,36 +2290,12 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable(
|
||||
PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKind kind, DataFlowCall call |
|
||||
exists(ReturnKindExt kind, DataFlowCall call |
|
||||
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
||||
|
|
||||
out = getAnOutNode(call, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfArgument(
|
||||
PartialPathNodePriv mid, PostUpdateNode node, CallContext cc, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
|
||||
DataFlowCall call, ArgumentNode arg
|
||||
|
|
||||
mid.getNode() = n and
|
||||
parameterValueFlowsToUpdate(p, n) and
|
||||
innercc = mid.getCallContext() and
|
||||
p.isParameterOf(callable, i) and
|
||||
resolveReturn(innercc, callable, call) and
|
||||
node.getPreUpdateNode() = arg and
|
||||
arg.argumentOf(call, i) and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
|
|
||||
if reducedViableImplInReturn(callable, call)
|
||||
then cc = TReturn(callable, call)
|
||||
else cc = TAnyCallContext()
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2400,10 +2342,10 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate paramFlowsThroughInPartialPath(
|
||||
ParameterNode p, ReturnKind kind, CallContextCall cc, PartialAccessPathNil apnil,
|
||||
ParameterNode p, ReturnKindExt kind, CallContextCall cc, PartialAccessPathNil apnil,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodePriv mid, ReturnNode ret |
|
||||
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -2420,23 +2362,23 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodePriv mid, ReturnKind kind, CallContext cc,
|
||||
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPathNil apnil, Configuration config
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc, PartialAccessPathNil midapnil |
|
||||
partialPathIntoCallable(mid, p, cc, innercc, call, midapnil, config) and
|
||||
paramFlowsThroughInPartialPath(p, kind, innercc, apnil, config) and
|
||||
not parameterValueFlowsThrough(p, kind, innercc)
|
||||
not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate partialPathThroughCallable(
|
||||
PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPathNil apnil,
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPathNil apnil,
|
||||
Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKind kind |
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
partialPathThroughCallable0(call, mid, kind, cc, apnil, config) and
|
||||
out = getAnOutNode(call, kind)
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -219,7 +219,6 @@ predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
|
||||
node1.asExpr() = a and
|
||||
a.getLValue() = fa
|
||||
) and
|
||||
not fa.getTarget().isStatic() and
|
||||
node2.getPreUpdateNode().asExpr() = fa.getQualifier() and
|
||||
f.(FieldContent).getField() = fa.getTarget()
|
||||
)
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.dataflow.internal.FlowVar
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.controlflow.Guards
|
||||
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
cached
|
||||
private newtype TNode =
|
||||
@@ -180,7 +182,7 @@ class ImplicitParameterNode extends ParameterNode, TInstanceParameterNode {
|
||||
|
||||
override Type getType() { result = f.getDeclaringType() }
|
||||
|
||||
override string toString() { result = "`this` parameter in " + f.getName() }
|
||||
override string toString() { result = "this" }
|
||||
|
||||
override Location getLocation() { result = f.getLocation() }
|
||||
|
||||
@@ -680,12 +682,16 @@ VariableAccess getAnAccessToAssignedVariable(Expr assign) {
|
||||
*
|
||||
* It is important that all extending classes in scope are disjoint.
|
||||
*/
|
||||
class BarrierGuard extends Expr {
|
||||
/** NOT YET SUPPORTED. Holds if this guard validates `e` upon evaluating to `branch`. */
|
||||
abstract deprecated predicate checks(Expr e, boolean branch);
|
||||
class BarrierGuard extends GuardCondition {
|
||||
/** Override this predicate to hold if this guard validates `e` upon evaluating to `b`. */
|
||||
abstract predicate checks(Expr e, boolean b);
|
||||
|
||||
/** Gets a node guarded by this guard. */
|
||||
final Node getAGuardedNode() {
|
||||
none() // stub
|
||||
final ExprNode getAGuardedNode() {
|
||||
exists(GVN value, boolean branch |
|
||||
result.getExpr() = value.getAnExpr() and
|
||||
this.checks(value.getAnExpr(), branch) and
|
||||
this.controls(result.getExpr().getBasicBlock(), branch)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,8 +133,7 @@ private module PartialDefinitions {
|
||||
TReferenceArgument(Expr arg, VariableAccess va) { referenceArgument(va, arg) }
|
||||
|
||||
private predicate isInstanceFieldWrite(FieldAccess fa, ControlFlowNode node) {
|
||||
not fa.getTarget().isStatic() and
|
||||
assignmentLikeOperation(node, fa.getTarget(), fa, _)
|
||||
assignmentLikeOperation(node, _, fa, _)
|
||||
}
|
||||
|
||||
class PartialDefinition extends TPartialDefinition {
|
||||
|
||||
@@ -28,6 +28,19 @@ module VirtualDispatch {
|
||||
not result.hasName("IUnknown")
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper predicate for `getAViableTarget`, which computes the viable targets for
|
||||
* virtual calls based on the qualifier type.
|
||||
*/
|
||||
private Function getAViableVirtualCallTarget(Class qualifierType, MemberFunction staticTarget) {
|
||||
exists(Class qualifierSubType |
|
||||
result = getAPossibleImplementation(staticTarget) and
|
||||
qualifierType = qualifierSubType.getABaseClass*() and
|
||||
mayInherit(qualifierSubType, result) and
|
||||
not cannotInherit(qualifierSubType, result)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable target for the given function call.
|
||||
*
|
||||
@@ -42,18 +55,9 @@ module VirtualDispatch {
|
||||
* If `c` is not a virtual call, the result will be `c.getTarget()`.
|
||||
*/
|
||||
Function getAViableTarget(Call c) {
|
||||
exists(Function staticTarget | staticTarget = c.getTarget() |
|
||||
if c.(FunctionCall).isVirtual() and staticTarget instanceof MemberFunction
|
||||
then
|
||||
exists(Class qualifierType, Class qualifierSubType |
|
||||
result = getAPossibleImplementation(staticTarget) and
|
||||
qualifierType = getCallQualifierType(c) and
|
||||
qualifierType = qualifierSubType.getABaseClass*() and
|
||||
mayInherit(qualifierSubType, result) and
|
||||
not cannotInherit(qualifierSubType, result)
|
||||
)
|
||||
else result = staticTarget
|
||||
)
|
||||
if c.(FunctionCall).isVirtual() and c.getTarget() instanceof MemberFunction
|
||||
then result = getAViableVirtualCallTarget(getCallQualifierType(c), c.getTarget())
|
||||
else result = c.getTarget()
|
||||
}
|
||||
|
||||
/** Holds if `f` is declared in `c` or a transitive base class of `c`. */
|
||||
@@ -63,7 +67,7 @@ module VirtualDispatch {
|
||||
|
||||
/**
|
||||
* Holds if `c` cannot inherit the member function `f`,
|
||||
* i.e. `c` or one of its supertypes overrides `f`.
|
||||
* that is, `c` or one of its supertypes overrides `f`.
|
||||
*/
|
||||
private predicate cannotInherit(Class c, MemberFunction f) {
|
||||
exists(Class overridingType, MemberFunction override |
|
||||
@@ -1,12 +1,17 @@
|
||||
import semmle.code.cpp.exprs.Expr
|
||||
|
||||
/**
|
||||
* A C/C++ arithmetic operation.
|
||||
* A C/C++ unary arithmetic operation.
|
||||
*
|
||||
* This is an abstract base QL class.
|
||||
*/
|
||||
abstract class UnaryArithmeticOperation extends UnaryOperation { }
|
||||
|
||||
/**
|
||||
* A C/C++ unary minus expression.
|
||||
* ```
|
||||
* b = - a;
|
||||
* ```
|
||||
*/
|
||||
class UnaryMinusExpr extends UnaryArithmeticOperation, @arithnegexpr {
|
||||
override string getOperator() { result = "-" }
|
||||
@@ -18,6 +23,9 @@ class UnaryMinusExpr extends UnaryArithmeticOperation, @arithnegexpr {
|
||||
|
||||
/**
|
||||
* A C/C++ unary plus expression.
|
||||
* ```
|
||||
* b = + a;
|
||||
* ```
|
||||
*/
|
||||
class UnaryPlusExpr extends UnaryArithmeticOperation, @unaryplusexpr {
|
||||
override string getOperator() { result = "+" }
|
||||
@@ -28,7 +36,13 @@ class UnaryPlusExpr extends UnaryArithmeticOperation, @unaryplusexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ GNU conjugation expression.
|
||||
* A C/C++ GNU conjugation expression. It operates on `_Complex` or
|
||||
* `__complex__ `numbers, and is similar to the C99 `conj`, `conjf` and `conjl`
|
||||
* functions.
|
||||
* ```
|
||||
* _Complex double a = ( 1.0, 2.0 );
|
||||
* _Complex double b = ~ a; // ( 1.0, - 2.0 )
|
||||
* ```
|
||||
*/
|
||||
class ConjugationExpr extends UnaryArithmeticOperation, @conjugation {
|
||||
override string getOperator() { result = "~" }
|
||||
@@ -39,7 +53,9 @@ class ConjugationExpr extends UnaryArithmeticOperation, @conjugation {
|
||||
/**
|
||||
* A C/C++ `++` or `--` expression (either prefix or postfix).
|
||||
*
|
||||
* Note that this doesn't include calls to user-defined `operator++`
|
||||
* This is the abstract base QL class for increment and decrement operations.
|
||||
*
|
||||
* Note that this does not include calls to user-defined `operator++`
|
||||
* or `operator--`.
|
||||
*/
|
||||
abstract class CrementOperation extends UnaryArithmeticOperation {
|
||||
@@ -58,35 +74,38 @@ abstract class CrementOperation extends UnaryArithmeticOperation {
|
||||
/**
|
||||
* A C/C++ `++` expression (either prefix or postfix).
|
||||
*
|
||||
* Note that this doesn't include calls to user-defined `operator++`.
|
||||
* Note that this does not include calls to user-defined `operator++`.
|
||||
*/
|
||||
abstract class IncrementOperation extends CrementOperation { }
|
||||
|
||||
/**
|
||||
* A C/C++ `--` expression (either prefix or postfix).
|
||||
*
|
||||
* Note that this doesn't include calls to user-defined `operator--`.
|
||||
* Note that this does not include calls to user-defined `operator--`.
|
||||
*/
|
||||
abstract class DecrementOperation extends CrementOperation { }
|
||||
|
||||
/**
|
||||
* A C/C++ `++` or `--` prefix expression.
|
||||
*
|
||||
* Note that this doesn't include calls to user-defined operators.
|
||||
* Note that this does not include calls to user-defined operators.
|
||||
*/
|
||||
abstract class PrefixCrementOperation extends CrementOperation { }
|
||||
|
||||
/**
|
||||
* A C/C++ `++` or `--` postfix expression.
|
||||
*
|
||||
* Note that this doesn't include calls to user-defined operators.
|
||||
* Note that this does not include calls to user-defined operators.
|
||||
*/
|
||||
abstract class PostfixCrementOperation extends CrementOperation { }
|
||||
|
||||
/**
|
||||
* A C/C++ prefix increment expression, as in `++x`.
|
||||
*
|
||||
* Note that this doesn't include calls to user-defined `operator++`.
|
||||
* Note that this does not include calls to user-defined `operator++`.
|
||||
* ```
|
||||
* b = ++a;
|
||||
* ```
|
||||
*/
|
||||
class PrefixIncrExpr extends IncrementOperation, PrefixCrementOperation, @preincrexpr {
|
||||
override string getOperator() { result = "++" }
|
||||
@@ -99,7 +118,10 @@ class PrefixIncrExpr extends IncrementOperation, PrefixCrementOperation, @preinc
|
||||
/**
|
||||
* A C/C++ prefix decrement expression, as in `--x`.
|
||||
*
|
||||
* Note that this doesn't include calls to user-defined `operator--`.
|
||||
* Note that this does not include calls to user-defined `operator--`.
|
||||
* ```
|
||||
* b = --a;
|
||||
* ```
|
||||
*/
|
||||
class PrefixDecrExpr extends DecrementOperation, PrefixCrementOperation, @predecrexpr {
|
||||
override string getOperator() { result = "--" }
|
||||
@@ -112,7 +134,10 @@ class PrefixDecrExpr extends DecrementOperation, PrefixCrementOperation, @predec
|
||||
/**
|
||||
* A C/C++ postfix increment expression, as in `x++`.
|
||||
*
|
||||
* Note that this doesn't include calls to user-defined `operator++`.
|
||||
* Note that this does not include calls to user-defined `operator++`.
|
||||
* ```
|
||||
* b = a++;
|
||||
* ```
|
||||
*/
|
||||
class PostfixIncrExpr extends IncrementOperation, PostfixCrementOperation, @postincrexpr {
|
||||
override string getOperator() { result = "++" }
|
||||
@@ -127,7 +152,10 @@ class PostfixIncrExpr extends IncrementOperation, PostfixCrementOperation, @post
|
||||
/**
|
||||
* A C/C++ postfix decrement expression, as in `x--`.
|
||||
*
|
||||
* Note that this doesn't include calls to user-defined `operator--`.
|
||||
* Note that this does not include calls to user-defined `operator--`.
|
||||
* ```
|
||||
* b = a--;
|
||||
* ```
|
||||
*/
|
||||
class PostfixDecrExpr extends DecrementOperation, PostfixCrementOperation, @postdecrexpr {
|
||||
override string getOperator() { result = "--" }
|
||||
@@ -140,7 +168,12 @@ class PostfixDecrExpr extends DecrementOperation, PostfixCrementOperation, @post
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ GNU real part expression.
|
||||
* A C/C++ GNU real part expression. It operates on `_Complex` or
|
||||
* `__complex__` numbers.
|
||||
* ```
|
||||
* _Complex double f = { 2.0, 3.0 };
|
||||
* double d = __real(f); // 2.0
|
||||
* ```
|
||||
*/
|
||||
class RealPartExpr extends UnaryArithmeticOperation, @realpartexpr {
|
||||
override string getOperator() { result = "__real" }
|
||||
@@ -149,7 +182,12 @@ class RealPartExpr extends UnaryArithmeticOperation, @realpartexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ GNU imaginary part expression.
|
||||
* A C/C++ GNU imaginary part expression. It operates on `_Complex` or
|
||||
* `__complex__` numbers.
|
||||
* ```
|
||||
* _Complex double f = { 2.0, 3.0 };
|
||||
* double d = __imag(f); // 3.0
|
||||
* ```
|
||||
*/
|
||||
class ImaginaryPartExpr extends UnaryArithmeticOperation, @imagpartexpr {
|
||||
override string getOperator() { result = "__imag" }
|
||||
@@ -159,11 +197,16 @@ class ImaginaryPartExpr extends UnaryArithmeticOperation, @imagpartexpr {
|
||||
|
||||
/**
|
||||
* A C/C++ binary arithmetic operation.
|
||||
*
|
||||
* This is an abstract base QL class for all binary arithmetic operations.
|
||||
*/
|
||||
abstract class BinaryArithmeticOperation extends BinaryOperation { }
|
||||
|
||||
/**
|
||||
* A C/C++ add expression.
|
||||
* ```
|
||||
* c = a + b;
|
||||
* ```
|
||||
*/
|
||||
class AddExpr extends BinaryArithmeticOperation, @addexpr {
|
||||
override string getOperator() { result = "+" }
|
||||
@@ -175,6 +218,9 @@ class AddExpr extends BinaryArithmeticOperation, @addexpr {
|
||||
|
||||
/**
|
||||
* A C/C++ subtract expression.
|
||||
* ```
|
||||
* c = a - b;
|
||||
* ```
|
||||
*/
|
||||
class SubExpr extends BinaryArithmeticOperation, @subexpr {
|
||||
override string getOperator() { result = "-" }
|
||||
@@ -186,6 +232,9 @@ class SubExpr extends BinaryArithmeticOperation, @subexpr {
|
||||
|
||||
/**
|
||||
* A C/C++ multiply expression.
|
||||
* ```
|
||||
* c = a * b;
|
||||
* ```
|
||||
*/
|
||||
class MulExpr extends BinaryArithmeticOperation, @mulexpr {
|
||||
override string getOperator() { result = "*" }
|
||||
@@ -197,6 +246,9 @@ class MulExpr extends BinaryArithmeticOperation, @mulexpr {
|
||||
|
||||
/**
|
||||
* A C/C++ divide expression.
|
||||
* ```
|
||||
* c = a / b;
|
||||
* ```
|
||||
*/
|
||||
class DivExpr extends BinaryArithmeticOperation, @divexpr {
|
||||
override string getOperator() { result = "/" }
|
||||
@@ -208,6 +260,9 @@ class DivExpr extends BinaryArithmeticOperation, @divexpr {
|
||||
|
||||
/**
|
||||
* A C/C++ remainder expression.
|
||||
* ```
|
||||
* c = a % b;
|
||||
* ```
|
||||
*/
|
||||
class RemExpr extends BinaryArithmeticOperation, @remexpr {
|
||||
override string getOperator() { result = "%" }
|
||||
@@ -218,7 +273,13 @@ class RemExpr extends BinaryArithmeticOperation, @remexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ multiply expression with an imaginary number.
|
||||
* A C/C++ multiply expression with an imaginary number. This is specific to
|
||||
* C99 and later.
|
||||
* ```
|
||||
* double z;
|
||||
* _Imaginary double x, y;
|
||||
* z = x * y;
|
||||
* ```
|
||||
*/
|
||||
class ImaginaryMulExpr extends BinaryArithmeticOperation, @jmulexpr {
|
||||
override string getOperator() { result = "*" }
|
||||
@@ -229,7 +290,13 @@ class ImaginaryMulExpr extends BinaryArithmeticOperation, @jmulexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ divide expression with an imaginary number.
|
||||
* A C/C++ divide expression with an imaginary number. This is specific to
|
||||
* C99 and later.
|
||||
* ```
|
||||
* double z;
|
||||
* _Imaginary double y;
|
||||
* z = z / y;
|
||||
* ```
|
||||
*/
|
||||
class ImaginaryDivExpr extends BinaryArithmeticOperation, @jdivexpr {
|
||||
override string getOperator() { result = "/" }
|
||||
@@ -240,7 +307,14 @@ class ImaginaryDivExpr extends BinaryArithmeticOperation, @jdivexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ add expression with a real term and an imaginary term.
|
||||
* A C/C++ add expression with a real term and an imaginary term. This is
|
||||
* specific to C99 and later.
|
||||
* ```
|
||||
* double z;
|
||||
* _Imaginary double x;
|
||||
* _Complex double w;
|
||||
* w = z + x;
|
||||
* ```
|
||||
*/
|
||||
class RealImaginaryAddExpr extends BinaryArithmeticOperation, @fjaddexpr {
|
||||
override string getOperator() { result = "+" }
|
||||
@@ -251,7 +325,14 @@ class RealImaginaryAddExpr extends BinaryArithmeticOperation, @fjaddexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ add expression with an imaginary term and a real term.
|
||||
* A C/C++ add expression with an imaginary term and a real term. This is
|
||||
* specific to C99 and later.
|
||||
* ```
|
||||
* double z;
|
||||
* _Imaginary double x;
|
||||
* _Complex double w;
|
||||
* w = x + z;
|
||||
* ```
|
||||
*/
|
||||
class ImaginaryRealAddExpr extends BinaryArithmeticOperation, @jfaddexpr {
|
||||
override string getOperator() { result = "+" }
|
||||
@@ -262,7 +343,14 @@ class ImaginaryRealAddExpr extends BinaryArithmeticOperation, @jfaddexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ subtract expression with a real term and an imaginary term.
|
||||
* A C/C++ subtract expression with a real term and an imaginary term. This is
|
||||
* specific to C99 and later.
|
||||
* ```
|
||||
* double z;
|
||||
* _Imaginary double x;
|
||||
* _Complex double w;
|
||||
* w = z - x;
|
||||
* ```
|
||||
*/
|
||||
class RealImaginarySubExpr extends BinaryArithmeticOperation, @fjsubexpr {
|
||||
override string getOperator() { result = "-" }
|
||||
@@ -273,7 +361,14 @@ class RealImaginarySubExpr extends BinaryArithmeticOperation, @fjsubexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ subtract expression with an imaginary term and a real term.
|
||||
* A C/C++ subtract expression with an imaginary term and a real term. This is
|
||||
* specific to C99 and later.
|
||||
* ```
|
||||
* double z;
|
||||
* _Imaginary double x;
|
||||
* _Complex double w;
|
||||
* w = x - z;
|
||||
* ```
|
||||
*/
|
||||
class ImaginaryRealSubExpr extends BinaryArithmeticOperation, @jfsubexpr {
|
||||
override string getOperator() { result = "-" }
|
||||
@@ -285,6 +380,9 @@ class ImaginaryRealSubExpr extends BinaryArithmeticOperation, @jfsubexpr {
|
||||
|
||||
/**
|
||||
* A C/C++ GNU min expression.
|
||||
* ```
|
||||
* c = a <? b;
|
||||
* ```
|
||||
*/
|
||||
class MinExpr extends BinaryArithmeticOperation, @minexpr {
|
||||
override string getOperator() { result = "<?" }
|
||||
@@ -294,6 +392,9 @@ class MinExpr extends BinaryArithmeticOperation, @minexpr {
|
||||
|
||||
/**
|
||||
* A C/C++ GNU max expression.
|
||||
* ```
|
||||
* c = a >? b;
|
||||
* ```
|
||||
*/
|
||||
class MaxExpr extends BinaryArithmeticOperation, @maxexpr {
|
||||
override string getOperator() { result = ">?" }
|
||||
@@ -308,6 +409,10 @@ abstract class PointerArithmeticOperation extends BinaryArithmeticOperation { }
|
||||
|
||||
/**
|
||||
* A C/C++ pointer add expression.
|
||||
* ```
|
||||
* foo *ptr = &f[0];
|
||||
* ptr = ptr + 2;
|
||||
* ```
|
||||
*/
|
||||
class PointerAddExpr extends PointerArithmeticOperation, @paddexpr {
|
||||
override string getOperator() { result = "+" }
|
||||
@@ -319,6 +424,10 @@ class PointerAddExpr extends PointerArithmeticOperation, @paddexpr {
|
||||
|
||||
/**
|
||||
* A C/C++ pointer subtract expression.
|
||||
* ```
|
||||
* foo *ptr = &f[3];
|
||||
* ptr = ptr - 2;
|
||||
* ```
|
||||
*/
|
||||
class PointerSubExpr extends PointerArithmeticOperation, @psubexpr {
|
||||
override string getOperator() { result = "-" }
|
||||
@@ -330,6 +439,10 @@ class PointerSubExpr extends PointerArithmeticOperation, @psubexpr {
|
||||
|
||||
/**
|
||||
* A C/C++ pointer difference expression.
|
||||
* ```
|
||||
* foo *start = &f[0], *end = &f[4];
|
||||
* int size = end - size;
|
||||
* ```
|
||||
*/
|
||||
class PointerDiffExpr extends PointerArithmeticOperation, @pdiffexpr {
|
||||
override string getOperator() { result = "-" }
|
||||
|
||||
@@ -4,11 +4,13 @@ import semmle.code.cpp.exprs.BitwiseOperation
|
||||
|
||||
/**
|
||||
* A non-overloaded binary assignment operation, including `=`, `+=`, `&=`,
|
||||
* etc. A C++ overloaded operation looks syntactically identical but is instead
|
||||
* etc. A C++ overloaded assignment operation looks syntactically identical but is instead
|
||||
* a `FunctionCall`.
|
||||
*
|
||||
* This is an abstract root QL class for all (non-overloaded) assignments.
|
||||
*/
|
||||
abstract class Assignment extends Operation {
|
||||
/** Gets the lvalue of this assignment. */
|
||||
/** Gets the _lvalue_ of this assignment. */
|
||||
Expr getLValue() { this.hasChild(result, 0) }
|
||||
|
||||
/** Gets the rvalue of this assignment. */
|
||||
@@ -30,6 +32,9 @@ abstract class Assignment extends Operation {
|
||||
|
||||
/**
|
||||
* A non-overloaded assignment operation with the operator `=`.
|
||||
* ```
|
||||
* a = b;
|
||||
* ```
|
||||
*/
|
||||
class AssignExpr extends Assignment, @assignexpr {
|
||||
override string getOperator() { result = "=" }
|
||||
@@ -48,13 +53,16 @@ abstract class AssignOperation extends Assignment {
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-overloaded arithmetic assignment operation on a non-pointer lvalue:
|
||||
* A non-overloaded arithmetic assignment operation on a non-pointer _lvalue_:
|
||||
* `+=`, `-=`, `*=`, `/=` and `%=`.
|
||||
*/
|
||||
abstract class AssignArithmeticOperation extends AssignOperation { }
|
||||
|
||||
/**
|
||||
* A non-overloaded `+=` assignment expression on a non-pointer lvalue.
|
||||
* A non-overloaded `+=` assignment expression on a non-pointer _lvalue_.
|
||||
* ```
|
||||
* a += b;
|
||||
* ```
|
||||
*/
|
||||
class AssignAddExpr extends AssignArithmeticOperation, @assignaddexpr {
|
||||
override string getCanonicalQLClass() { result = "AssignAddExpr" }
|
||||
@@ -63,7 +71,10 @@ class AssignAddExpr extends AssignArithmeticOperation, @assignaddexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-overloaded `-=` assignment expression on a non-pointer lvalue.
|
||||
* A non-overloaded `-=` assignment expression on a non-pointer _lvalue_.
|
||||
* ```
|
||||
* a -= b;
|
||||
* ```
|
||||
*/
|
||||
class AssignSubExpr extends AssignArithmeticOperation, @assignsubexpr {
|
||||
override string getCanonicalQLClass() { result = "AssignSubExpr" }
|
||||
@@ -73,6 +84,9 @@ class AssignSubExpr extends AssignArithmeticOperation, @assignsubexpr {
|
||||
|
||||
/**
|
||||
* A non-overloaded `*=` assignment expression.
|
||||
* ```
|
||||
* a *= b;
|
||||
* ```
|
||||
*/
|
||||
class AssignMulExpr extends AssignArithmeticOperation, @assignmulexpr {
|
||||
override string getCanonicalQLClass() { result = "AssignMulExpr" }
|
||||
@@ -82,6 +96,9 @@ class AssignMulExpr extends AssignArithmeticOperation, @assignmulexpr {
|
||||
|
||||
/**
|
||||
* A non-overloaded `/=` assignment expression.
|
||||
* ```
|
||||
* a /= b;
|
||||
* ```
|
||||
*/
|
||||
class AssignDivExpr extends AssignArithmeticOperation, @assigndivexpr {
|
||||
override string getCanonicalQLClass() { result = "AssignDivExpr" }
|
||||
@@ -91,6 +108,9 @@ class AssignDivExpr extends AssignArithmeticOperation, @assigndivexpr {
|
||||
|
||||
/**
|
||||
* A non-overloaded `%=` assignment expression.
|
||||
* ```
|
||||
* a %= b;
|
||||
* ```
|
||||
*/
|
||||
class AssignRemExpr extends AssignArithmeticOperation, @assignremexpr {
|
||||
override string getCanonicalQLClass() { result = "AssignRemExpr" }
|
||||
@@ -105,7 +125,10 @@ class AssignRemExpr extends AssignArithmeticOperation, @assignremexpr {
|
||||
abstract class AssignBitwiseOperation extends AssignOperation { }
|
||||
|
||||
/**
|
||||
* A non-overloaded `&=` assignment expression.
|
||||
* A non-overloaded AND (`&=`) assignment expression.
|
||||
* ```
|
||||
* a &= b;
|
||||
* ```
|
||||
*/
|
||||
class AssignAndExpr extends AssignBitwiseOperation, @assignandexpr {
|
||||
override string getCanonicalQLClass() { result = "AssignAndExpr" }
|
||||
@@ -114,7 +137,10 @@ class AssignAndExpr extends AssignBitwiseOperation, @assignandexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-overloaded `|=` assignment expression.
|
||||
* A non-overloaded OR (`|=`) assignment expression.
|
||||
* ```
|
||||
* a |= b;
|
||||
* ```
|
||||
*/
|
||||
class AssignOrExpr extends AssignBitwiseOperation, @assignorexpr {
|
||||
override string getCanonicalQLClass() { result = "AssignOrExpr" }
|
||||
@@ -123,7 +149,10 @@ class AssignOrExpr extends AssignBitwiseOperation, @assignorexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-overloaded `^=` assignment expression.
|
||||
* A non-overloaded XOR (`^=`) assignment expression.
|
||||
* ```
|
||||
* a ^= b;
|
||||
* ```
|
||||
*/
|
||||
class AssignXorExpr extends AssignBitwiseOperation, @assignxorexpr {
|
||||
override string getCanonicalQLClass() { result = "AssignXorExpr" }
|
||||
@@ -133,6 +162,9 @@ class AssignXorExpr extends AssignBitwiseOperation, @assignxorexpr {
|
||||
|
||||
/**
|
||||
* A non-overloaded `<<=` assignment expression.
|
||||
* ```
|
||||
* a <<= b;
|
||||
* ```
|
||||
*/
|
||||
class AssignLShiftExpr extends AssignBitwiseOperation, @assignlshiftexpr {
|
||||
override string getCanonicalQLClass() { result = "AssignLShiftExpr" }
|
||||
@@ -142,6 +174,9 @@ class AssignLShiftExpr extends AssignBitwiseOperation, @assignlshiftexpr {
|
||||
|
||||
/**
|
||||
* A non-overloaded `>>=` assignment expression.
|
||||
* ```
|
||||
* a >>= b;
|
||||
* ```
|
||||
*/
|
||||
class AssignRShiftExpr extends AssignBitwiseOperation, @assignrshiftexpr {
|
||||
override string getCanonicalQLClass() { result = "AssignRShiftExpr" }
|
||||
@@ -151,6 +186,9 @@ class AssignRShiftExpr extends AssignBitwiseOperation, @assignrshiftexpr {
|
||||
|
||||
/**
|
||||
* A non-overloaded `+=` pointer assignment expression.
|
||||
* ```
|
||||
* ptr += index;
|
||||
* ```
|
||||
*/
|
||||
class AssignPointerAddExpr extends AssignOperation, @assignpaddexpr {
|
||||
override string getCanonicalQLClass() { result = "AssignPointerAddExpr" }
|
||||
@@ -160,6 +198,9 @@ class AssignPointerAddExpr extends AssignOperation, @assignpaddexpr {
|
||||
|
||||
/**
|
||||
* A non-overloaded `-=` pointer assignment expression.
|
||||
* ```
|
||||
* ptr -= index;
|
||||
* ```
|
||||
*/
|
||||
class AssignPointerSubExpr extends AssignOperation, @assignpsubexpr {
|
||||
override string getCanonicalQLClass() { result = "AssignPointerSubExpr" }
|
||||
@@ -168,11 +209,16 @@ class AssignPointerSubExpr extends AssignOperation, @assignpsubexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ variable declaration in an expression where a condition is expected.
|
||||
* For example, on the `ConditionDeclExpr` in `if (bool c = x < y)`,
|
||||
* `getVariableAccess()` is an access to `c` (with possible casts),
|
||||
* `getVariable()` is the variable `c` (which has an initializer `x < y`), and
|
||||
* `getInitializingExpr()` is `x < y`.
|
||||
* A C++ variable declaration inside the conditional expression of a `while`, `if` or
|
||||
* `for` compound statement. Declaring a variable this way narrows its lifetime and
|
||||
* scope to be strictly the compound statement itself. For example:
|
||||
* ```
|
||||
* extern int x, y;
|
||||
* if (bool c = x < y) { do_something_with(c); }
|
||||
* // c is no longer in scope
|
||||
* while (int d = x - y) { do_something_else_with(d); }
|
||||
* // d is no longer is scope
|
||||
* ```
|
||||
*/
|
||||
class ConditionDeclExpr extends Expr, @condition_decl {
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,9 @@ abstract class UnaryBitwiseOperation extends UnaryOperation { }
|
||||
|
||||
/**
|
||||
* A C/C++ complement expression.
|
||||
* ```
|
||||
* unsigned c = ~a;
|
||||
* ```
|
||||
*/
|
||||
class ComplementExpr extends UnaryBitwiseOperation, @complementexpr {
|
||||
override string getOperator() { result = "~" }
|
||||
@@ -23,6 +26,9 @@ abstract class BinaryBitwiseOperation extends BinaryOperation { }
|
||||
|
||||
/**
|
||||
* A C/C++ left shift expression.
|
||||
* ```
|
||||
* unsigned c = a << b;
|
||||
* ```
|
||||
*/
|
||||
class LShiftExpr extends BinaryBitwiseOperation, @lshiftexpr {
|
||||
override string getOperator() { result = "<<" }
|
||||
@@ -34,6 +40,9 @@ class LShiftExpr extends BinaryBitwiseOperation, @lshiftexpr {
|
||||
|
||||
/**
|
||||
* A C/C++ right shift expression.
|
||||
* ```
|
||||
* unsigned c = a >> b;
|
||||
* ```
|
||||
*/
|
||||
class RShiftExpr extends BinaryBitwiseOperation, @rshiftexpr {
|
||||
override string getOperator() { result = ">>" }
|
||||
@@ -44,7 +53,10 @@ class RShiftExpr extends BinaryBitwiseOperation, @rshiftexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ bitwise and expression.
|
||||
* A C/C++ bitwise AND expression.
|
||||
* ```
|
||||
* unsigned c = a & b;
|
||||
* ```
|
||||
*/
|
||||
class BitwiseAndExpr extends BinaryBitwiseOperation, @andexpr {
|
||||
override string getOperator() { result = "&" }
|
||||
@@ -55,7 +67,10 @@ class BitwiseAndExpr extends BinaryBitwiseOperation, @andexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ bitwise or expression.
|
||||
* A C/C++ bitwise OR expression.
|
||||
* ```
|
||||
* unsigned c = a | b;
|
||||
* ```
|
||||
*/
|
||||
class BitwiseOrExpr extends BinaryBitwiseOperation, @orexpr {
|
||||
override string getOperator() { result = "|" }
|
||||
@@ -66,7 +81,10 @@ class BitwiseOrExpr extends BinaryBitwiseOperation, @orexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ bitwise xor expression.
|
||||
* A C/C++ bitwise XOR expression.
|
||||
* ```
|
||||
* unsigned c = a ^ b;
|
||||
* ```
|
||||
*/
|
||||
class BitwiseXorExpr extends BinaryBitwiseOperation, @xorexpr {
|
||||
override string getOperator() { result = "^" }
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
import semmle.code.cpp.exprs.Expr
|
||||
|
||||
/**
|
||||
* A C/C++ builtin operation.
|
||||
* A C/C++ builtin operation. This is the root QL class encompassing
|
||||
* built-in functionality.
|
||||
*/
|
||||
abstract class BuiltInOperation extends Expr {
|
||||
override string getCanonicalQLClass() { result = "BuiltInOperation" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ `__builtin_va_start` expression (used by some implementations of `va_start`).
|
||||
* A C/C++ `__builtin_va_start` built-in operation (used by some
|
||||
* implementations of `va_start`).
|
||||
* ```
|
||||
* __builtin_va_list ap;
|
||||
* __builtin_va_start(ap, last_named_param);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInVarArgsStart extends BuiltInOperation, @vastartexpr {
|
||||
override string toString() { result = "__builtin_va_start" }
|
||||
@@ -17,7 +23,13 @@ class BuiltInVarArgsStart extends BuiltInOperation, @vastartexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ `__builtin_va_end` expression (used by some implementations of `va_end`).
|
||||
* A C/C++ `__builtin_va_end` built-in operation (used by some implementations
|
||||
* of `va_end`).
|
||||
* ```
|
||||
* __builtin_va_start(ap, last_named_param);
|
||||
* ap = __builtin_va_arg(ap, long);
|
||||
* __builtin_va_end(ap);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInVarArgsEnd extends BuiltInOperation, @vaendexpr {
|
||||
override string toString() { result = "__builtin_va_end" }
|
||||
@@ -26,7 +38,11 @@ class BuiltInVarArgsEnd extends BuiltInOperation, @vaendexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ `__builtin_va_arg` expression (used by some implementations of `va_arg`).
|
||||
* A C/C++ `__builtin_va_arg` built-in operation (used by some implementations
|
||||
* of `va_arg`).
|
||||
* ```
|
||||
* ap = __builtin_va_arg(ap, long);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInVarArg extends BuiltInOperation, @vaargexpr {
|
||||
override string toString() { result = "__builtin_va_arg" }
|
||||
@@ -35,7 +51,13 @@ class BuiltInVarArg extends BuiltInOperation, @vaargexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ `__builtin_va_copy` expression (used by some implementations of `va_copy`).
|
||||
* A C/C++ `__builtin_va_copy` built-in operation (used by some implementations
|
||||
* of `va_copy`).
|
||||
* ```
|
||||
* va_list ap, aq;
|
||||
* __builtin_va_start(ap, last_named_param);
|
||||
* va_copy(aq, ap);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInVarArgCopy extends BuiltInOperation, @vacopyexpr {
|
||||
override string toString() { result = "__builtin_va_copy" }
|
||||
@@ -45,6 +67,9 @@ class BuiltInVarArgCopy extends BuiltInOperation, @vacopyexpr {
|
||||
|
||||
/**
|
||||
* A Microsoft C/C++ `__noop` expression, which does nothing.
|
||||
* ```
|
||||
* __noop;
|
||||
* ```
|
||||
*/
|
||||
class BuiltInNoOp extends BuiltInOperation, @noopexpr {
|
||||
override string toString() { result = "__noop" }
|
||||
@@ -53,16 +78,37 @@ class BuiltInNoOp extends BuiltInOperation, @noopexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__offsetof` expression (used by some implementations of offsetof in the presence of user-defined `operator&`).
|
||||
* DEPRECATED: Use `BuiltInOperationBuiltInOffsetOf` instead.
|
||||
*/
|
||||
class BuiltInOperationOffsetOf extends BuiltInOperation, @offsetofexpr {
|
||||
override string toString() { result = "__offsetof" }
|
||||
deprecated class BuiltInOperationOffsetOf = BuiltInOperationBuiltInOffsetOf;
|
||||
|
||||
override string getCanonicalQLClass() { result = "BuiltInOperationOffsetOf" }
|
||||
/**
|
||||
* A C/C++ `__builtin_offsetof` built-in operation (used by some implementations
|
||||
* of `offsetof`). The operation retains its semantics even in the presence
|
||||
* of an overloaded `operator &`). This is a GNU/Clang extension.
|
||||
* ```
|
||||
* struct S {
|
||||
* int a, b;
|
||||
* };
|
||||
* int d = __builtin_offsetof(struct S, b); // usually 4
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationBuiltInOffsetOf extends BuiltInOperation, @offsetofexpr {
|
||||
override string toString() { result = "__builtin_offsetof" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "BuiltInOperationBuiltInOffsetOf" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ `__INTADDR__` expression, used by EDG to implement `offsetof` in the presence of user-defined `operator&`.
|
||||
* A C/C++ `__INTADDR__` built-in operation (used by some implementations
|
||||
* of `offsetof`). The operation retains its semantics even in the presence
|
||||
* of an overloaded `operator &`). This is an EDG extension.
|
||||
* ```
|
||||
* struct S {
|
||||
* int a, b;
|
||||
* };
|
||||
* int d = __INTADDR__(struct S, b); // usually 4
|
||||
* ```
|
||||
*/
|
||||
class BuiltInIntAddr extends BuiltInOperation, @intaddrexpr {
|
||||
override string toString() { result = "__INTADDR__" }
|
||||
@@ -71,7 +117,13 @@ class BuiltInIntAddr extends BuiltInOperation, @intaddrexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__has_assign` expression (used by some implementations of the type_traits header).
|
||||
* A C++ `__has_assign` built-in operation (used by some implementations of
|
||||
* the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the type has a copy assignment operator.
|
||||
* ```
|
||||
* bool v = __has_assign(MyType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationHasAssign extends BuiltInOperation, @hasassignexpr {
|
||||
override string toString() { result = "__has_assign" }
|
||||
@@ -80,7 +132,13 @@ class BuiltInOperationHasAssign extends BuiltInOperation, @hasassignexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__has_copy` expression (used by some implementations of the type_traits header).
|
||||
* A C++ `__has_copy` built-in operation (used by some implementations of the
|
||||
* `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the type has a copy constructor.
|
||||
* ```
|
||||
* std::integral_constant< bool, __has_copy(_Tp)> hc;
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationHasCopy extends BuiltInOperation, @hascopyexpr {
|
||||
override string toString() { result = "__has_copy" }
|
||||
@@ -89,7 +147,14 @@ class BuiltInOperationHasCopy extends BuiltInOperation, @hascopyexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__has_nothrow_assign` expression (used by some implementations of the type_traits header).
|
||||
* A C++ `__has_nothrow_assign` built-in operation (used by some
|
||||
* implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if a copy assignment operator has an empty exception
|
||||
* specification.
|
||||
* ```
|
||||
* std::integral_constant< bool, __has_nothrow_assign(_Tp)> hnta;
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationHasNoThrowAssign extends BuiltInOperation, @hasnothrowassign {
|
||||
override string toString() { result = "__has_nothrow_assign" }
|
||||
@@ -98,7 +163,14 @@ class BuiltInOperationHasNoThrowAssign extends BuiltInOperation, @hasnothrowassi
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__has_nothrow_constructor` expression (used by some implementations of the type_traits header).
|
||||
* A C++ `__has_nothrow_constructor` built-in operation (used by some
|
||||
* implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the default constructor has an empty exception
|
||||
* specification.
|
||||
* ```
|
||||
* bool v = __has_nothrow_constructor(MyType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationHasNoThrowConstructor extends BuiltInOperation, @hasnothrowconstr {
|
||||
override string toString() { result = "__has_nothrow_constructor" }
|
||||
@@ -107,7 +179,13 @@ class BuiltInOperationHasNoThrowConstructor extends BuiltInOperation, @hasnothro
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__has_nothrow_copy` expression (used by some implementations of the type_traits header).
|
||||
* A C++ `__has_nothrow_copy` built-in operation (used by some implementations
|
||||
* of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the copy constructor has an empty exception specification.
|
||||
* ```
|
||||
* std::integral_constant< bool, __has_nothrow_copy(MyType) >;
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationHasNoThrowCopy extends BuiltInOperation, @hasnothrowcopy {
|
||||
override string toString() { result = "__has_nothrow_copy" }
|
||||
@@ -116,7 +194,14 @@ class BuiltInOperationHasNoThrowCopy extends BuiltInOperation, @hasnothrowcopy {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__has_trivial_assign` expression (used by some implementations of the type_traits header).
|
||||
* A C++ `__has_trivial_assign` built-in operation (used by some implementations
|
||||
* of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the type has a trivial assignment
|
||||
* operator (`operator =`).
|
||||
* ```
|
||||
* bool v = __has_trivial_assign(MyType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationHasTrivialAssign extends BuiltInOperation, @hastrivialassign {
|
||||
override string toString() { result = "__has_trivial_assign" }
|
||||
@@ -125,7 +210,13 @@ class BuiltInOperationHasTrivialAssign extends BuiltInOperation, @hastrivialassi
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__has_trivial_constructor` expression (used by some implementations of the type_traits header).
|
||||
* A C++ `__has_trivial_constructor` built-in operation (used by some
|
||||
* implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the type has a trivial constructor.
|
||||
* ```
|
||||
* bool v = __has_trivial_constructor(MyType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationHasTrivialConstructor extends BuiltInOperation, @hastrivialconstr {
|
||||
override string toString() { result = "__has_trivial_constructor" }
|
||||
@@ -134,7 +225,13 @@ class BuiltInOperationHasTrivialConstructor extends BuiltInOperation, @hastrivia
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__has_trivial_copy` expression (used by some implementations of the type_traits header).
|
||||
* A C++ `__has_trivial_copy` built-in operation (used by some implementations
|
||||
* of the `<type_traits>` header).
|
||||
*
|
||||
* Returns true if the type has a trivial copy constructor.
|
||||
* ```
|
||||
* std::integral_constant< bool, __has_trivial_copy(MyType) > htc;
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationHasTrivialCopy extends BuiltInOperation, @hastrivialcopy {
|
||||
override string toString() { result = "__has_trivial_copy" }
|
||||
@@ -143,7 +240,13 @@ class BuiltInOperationHasTrivialCopy extends BuiltInOperation, @hastrivialcopy {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__has_trivial_destructor` expression (used by some implementations of the type_traits header).
|
||||
* A C++ `__has_trivial_destructor` built-in operation (used by some
|
||||
* implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the type has a trivial destructor.
|
||||
* ```
|
||||
* bool v = __has_trivial_destructor(MyType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationHasTrivialDestructor extends BuiltInOperation, @hastrivialdestructor {
|
||||
override string toString() { result = "__has_trivial_destructor" }
|
||||
@@ -152,7 +255,13 @@ class BuiltInOperationHasTrivialDestructor extends BuiltInOperation, @hastrivial
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__has_user_destructor` expression (used by some implementations of the type_traits header).
|
||||
* A C++ `__has_user_destructor` built-in operation (used by some
|
||||
* implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns true if the type has a user-declared destructor.
|
||||
* ```
|
||||
* bool v = __has_user_destructor(MyType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationHasUserDestructor extends BuiltInOperation, @hasuserdestr {
|
||||
override string toString() { result = "__has_user_destructor" }
|
||||
@@ -161,7 +270,16 @@ class BuiltInOperationHasUserDestructor extends BuiltInOperation, @hasuserdestr
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__has_virtual_destructor` expression (used by some implementations of the type_traits header).
|
||||
* A C++ `__has_virtual_destructor` built-in operation (used by some
|
||||
* implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the type has a virtual destructor.
|
||||
* ```
|
||||
* template<typename _Tp>
|
||||
* struct has_virtual_destructor
|
||||
* : public integral_constant<bool, __has_virtual_destructor(_Tp)>
|
||||
* { };
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationHasVirtualDestructor extends BuiltInOperation, @hasvirtualdestr {
|
||||
override string toString() { result = "__has_virtual_destructor" }
|
||||
@@ -170,7 +288,13 @@ class BuiltInOperationHasVirtualDestructor extends BuiltInOperation, @hasvirtual
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__is_abstract` expression (used by some implementations of the type_traits header).
|
||||
* A C++ `__is_abstract` built-in operation (used by some implementations of the
|
||||
* `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the class has at least one pure virtual function.
|
||||
* ```
|
||||
* bool v = __is_abstract(MyType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsAbstract extends BuiltInOperation, @isabstractexpr {
|
||||
override string toString() { result = "__is_abstract" }
|
||||
@@ -179,7 +303,13 @@ class BuiltInOperationIsAbstract extends BuiltInOperation, @isabstractexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__is_base_of` expression (used by some implementations of the type_traits header).
|
||||
* A C++ `__is_base_of` built-in operation (used by some implementations of the
|
||||
* `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the first type is a base class of the second type, of if both types are the same.
|
||||
* ```
|
||||
* bool v = __is_base_of(MyType, OtherType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsBaseOf extends BuiltInOperation, @isbaseofexpr {
|
||||
override string toString() { result = "__is_base_of" }
|
||||
@@ -188,7 +318,13 @@ class BuiltInOperationIsBaseOf extends BuiltInOperation, @isbaseofexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__is_class` expression (used by some implementations of the type_traits header).
|
||||
* A C++ `__is_class` built-in operation (used by some implementations of the
|
||||
* `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the type is a `class` or a `struct`.
|
||||
* ```
|
||||
* bool v = __is_class(MyType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsClass extends BuiltInOperation, @isclassexpr {
|
||||
override string toString() { result = "__is_class" }
|
||||
@@ -197,7 +333,13 @@ class BuiltInOperationIsClass extends BuiltInOperation, @isclassexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__is_convertible_to` expression (used by some implementations of the type_traits header).
|
||||
* A C++ `__is_convertible_to` built-in operation (used by some implementations
|
||||
* of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the first type can be converted to the second type.
|
||||
* ```
|
||||
* bool v = __is_convertible_to(MyType, OtherType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsConvertibleTo extends BuiltInOperation, @isconvtoexpr {
|
||||
override string toString() { result = "__is_convertible_to" }
|
||||
@@ -206,7 +348,13 @@ class BuiltInOperationIsConvertibleTo extends BuiltInOperation, @isconvtoexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__is_empty` expression (used by some implementations of the type_traits header).
|
||||
* A C++ `__is_empty` built-in operation (used by some implementations of the
|
||||
* `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the type has no instance data members.
|
||||
* ```
|
||||
* bool v = __is_empty(MyType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsEmpty extends BuiltInOperation, @isemptyexpr {
|
||||
override string toString() { result = "__is_empty" }
|
||||
@@ -215,7 +363,13 @@ class BuiltInOperationIsEmpty extends BuiltInOperation, @isemptyexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__is_enum` expression (used by some implementations of the type_traits header).
|
||||
* A C++ `__is_enum` built-in operation (used by some implementations of the
|
||||
* `<type_traits>` header).
|
||||
*
|
||||
* Returns true if the type is an `enum`.
|
||||
* ```
|
||||
* bool v = __is_enum(MyType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsEnum extends BuiltInOperation, @isenumexpr {
|
||||
override string toString() { result = "__is_enum" }
|
||||
@@ -224,7 +378,15 @@ class BuiltInOperationIsEnum extends BuiltInOperation, @isenumexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__is_pod` expression (used by some implementations of the type_traits header).
|
||||
* A C++ `__is_pod` built-in operation (used by some implementations of the
|
||||
* `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the type is a `class`, `struct` or `union`, WITHOUT
|
||||
* (1) constructors, (2) private or protected non-static members, (3) base
|
||||
* classes, or (4) virtual functions.
|
||||
* ```
|
||||
* bool v = __is_pod(MyType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsPod extends BuiltInOperation, @ispodexpr {
|
||||
override string toString() { result = "__is_pod" }
|
||||
@@ -233,7 +395,13 @@ class BuiltInOperationIsPod extends BuiltInOperation, @ispodexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__is_polymorphic` expression (used by some implementations of the type_traits header).
|
||||
* A C++ `__is_polymorphic` built-in operation (used by some implementations
|
||||
* of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the type has at least one virtual function.
|
||||
* ```
|
||||
* bool v = __is_polymorphic(MyType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsPolymorphic extends BuiltInOperation, @ispolyexpr {
|
||||
override string toString() { result = "__is_polymorphic" }
|
||||
@@ -242,7 +410,13 @@ class BuiltInOperationIsPolymorphic extends BuiltInOperation, @ispolyexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__is_union` expression (used by some implementations of the type_traits header).
|
||||
* A C++ `__is_union` built-in operation (used by some implementations of the
|
||||
* `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the type is a `union`.
|
||||
* ```
|
||||
* bool v = __is_union(MyType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsUnion extends BuiltInOperation, @isunionexpr {
|
||||
override string toString() { result = "__is_union" }
|
||||
@@ -256,7 +430,16 @@ class BuiltInOperationIsUnion extends BuiltInOperation, @isunionexpr {
|
||||
deprecated class BuiltInOperationBuiltInTypes = BuiltInOperationBuiltInTypesCompatibleP;
|
||||
|
||||
/**
|
||||
* A C++ `__builtin_types_compatible_p` expression (used by some implementations of the type_traits header).
|
||||
* A C++ `__builtin_types_compatible_p` built-in operation (used by some
|
||||
* implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the two types are the same (modulo qualifiers).
|
||||
* ```
|
||||
* template<typename _Tp1, typename _Tp2>
|
||||
* struct types_compatible
|
||||
* : public integral_constant<bool, __builtin_types_compatible_p(_Tp1, _Tp2) >
|
||||
* { };
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationBuiltInTypesCompatibleP extends BuiltInOperation, @typescompexpr {
|
||||
override string toString() { result = "__builtin_types_compatible_p" }
|
||||
@@ -264,6 +447,15 @@ class BuiltInOperationBuiltInTypesCompatibleP extends BuiltInOperation, @typesco
|
||||
|
||||
/**
|
||||
* A clang `__builtin_shufflevector` expression.
|
||||
*
|
||||
* It outputs a permutation of elements from one or two input vectors.
|
||||
* Please see
|
||||
* https://releases.llvm.org/3.7.0/tools/clang/docs/LanguageExtensions.html#langext-builtin-shufflevector
|
||||
* for more information.
|
||||
* ```
|
||||
* // Concatenate every other element of 4-element vectors V1 and V2.
|
||||
* V3 = __builtin_shufflevector(V1, V2, 0, 2, 4, 6);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationBuiltInShuffleVector extends BuiltInOperation, @builtinshufflevector {
|
||||
override string toString() { result = "__builtin_shufflevector" }
|
||||
@@ -273,6 +465,17 @@ class BuiltInOperationBuiltInShuffleVector extends BuiltInOperation, @builtinshu
|
||||
|
||||
/**
|
||||
* A clang `__builtin_convertvector` expression.
|
||||
*
|
||||
* Allows for conversion of vectors of equal element count and compatible
|
||||
* element types. Please see
|
||||
* https://releases.llvm.org/3.7.0/tools/clang/docs/LanguageExtensions.html#builtin-convertvector
|
||||
* for more information.
|
||||
* ```
|
||||
* float vf __attribute__((__vector_size__(16)));
|
||||
* typedef double vector4double __attribute__((__vector_size__(32)));
|
||||
* // convert from a vector of 4 floats to a vector of 4 doubles.
|
||||
* vector4double vd = __builtin_convertvector(vf, vector4double);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationBuiltInConvertVector extends BuiltInOperation, @builtinconvertvector {
|
||||
override string toString() { result = "__builtin_convertvector" }
|
||||
@@ -281,7 +484,14 @@ class BuiltInOperationBuiltInConvertVector extends BuiltInOperation, @builtincon
|
||||
}
|
||||
|
||||
/**
|
||||
* A clang `__builtin_addressof` expression (can be used to implement C++'s std::addressof).
|
||||
* A clang `__builtin_addressof` function (can be used to implement C++'s
|
||||
* `std::addressof`).
|
||||
*
|
||||
* This function disregards any overloads created for `operator &`.
|
||||
* ```
|
||||
* int a = 1;
|
||||
* int *b = __builtin_addressof(a);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationBuiltInAddressOf extends UnaryOperation, BuiltInOperation, @builtinaddressof {
|
||||
/** Gets the function or variable whose address is taken. */
|
||||
@@ -298,7 +508,17 @@ class BuiltInOperationBuiltInAddressOf extends UnaryOperation, BuiltInOperation,
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_trivially_constructible` type trait.
|
||||
* The `__is_trivially_constructible` built-in operation (used by some
|
||||
* implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the type has a trivial default
|
||||
* constructor, copy constructor or move constructor.
|
||||
* ```
|
||||
* template<typename T, typename... Args>
|
||||
* struct is_trivially_constructible
|
||||
* : public integral_constant<bool, __is_trivially_constructible(T, Args...) >
|
||||
* { };
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsTriviallyConstructible extends BuiltInOperation,
|
||||
@istriviallyconstructibleexpr {
|
||||
@@ -308,7 +528,15 @@ class BuiltInOperationIsTriviallyConstructible extends BuiltInOperation,
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_destructible` type trait.
|
||||
* The `__is_destructible` built-in operation (used by some implementations
|
||||
* of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the type's destructor is not `delete`d and is accessible
|
||||
* in derived `class`es, and whose base `class` and all non-static data members
|
||||
* are also destructible.
|
||||
* ```
|
||||
* bool v = __is_destructible(MyType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsDestructible extends BuiltInOperation, @isdestructibleexpr {
|
||||
override string toString() { result = "__is_destructible" }
|
||||
@@ -317,7 +545,15 @@ class BuiltInOperationIsDestructible extends BuiltInOperation, @isdestructibleex
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_nothrow_destructible` type trait.
|
||||
* The `__is_nothrow_destructible` built-in operation (used by some
|
||||
* implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the type is destructible and whose destructor, and those
|
||||
* of member data and any super`class`es all have an empty exception
|
||||
* specification.
|
||||
* ```
|
||||
* bool v = __is_nothrow_destructible(MyType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsNothrowDestructible extends BuiltInOperation, @isnothrowdestructibleexpr {
|
||||
override string toString() { result = "__is_nothrow_destructible" }
|
||||
@@ -326,7 +562,14 @@ class BuiltInOperationIsNothrowDestructible extends BuiltInOperation, @isnothrow
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_trivially_destructible` type trait.
|
||||
* The `__is_trivially_destructible` built-in operation (used by some
|
||||
* implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the type is destructible and whose destructor, and those
|
||||
* of member data and any superclasses are all trivial.
|
||||
* ```
|
||||
* bool v = __is_trivially_destructible(MyType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsTriviallyDestructible extends BuiltInOperation, @istriviallydestructibleexpr {
|
||||
override string toString() { result = "__is_trivially_destructible" }
|
||||
@@ -335,7 +578,17 @@ class BuiltInOperationIsTriviallyDestructible extends BuiltInOperation, @istrivi
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_trivially_assignable` type trait.
|
||||
* The `__is_trivially_assignable` built-in operation (used by some
|
||||
* implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the assignment operator `C::operator =(const C& c)` is
|
||||
* trivial.
|
||||
* ```
|
||||
* template<typename T>
|
||||
* struct is_trivially_assignable
|
||||
* : public integral_constant<bool, __is_trivially_assignable(T) >
|
||||
* { };
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsTriviallyAssignable extends BuiltInOperation, @istriviallyassignableexpr {
|
||||
override string toString() { result = "__is_trivially_assignable" }
|
||||
@@ -344,7 +597,14 @@ class BuiltInOperationIsTriviallyAssignable extends BuiltInOperation, @istrivial
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_nothrow_assignable` type trait.
|
||||
* The `__is_nothrow_assignable` built-in operation (used by some
|
||||
* implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns true if there exists a `C::operator =(const C& c) nothrow`
|
||||
* assignment operator (i.e, with an empty exception specification).
|
||||
* ```
|
||||
* bool v = __is_nothrow_assignable(MyType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsNothrowAssignable extends BuiltInOperation, @isnothrowassignableexpr {
|
||||
override string toString() { result = "__is_nothrow_assignable" }
|
||||
@@ -353,7 +613,18 @@ class BuiltInOperationIsNothrowAssignable extends BuiltInOperation, @isnothrowas
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_standard_layout` type trait.
|
||||
* The `__is_standard_layout` built-in operation (used by some implementations
|
||||
* of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the type is a primitive type, or a `class`, `struct` or
|
||||
* `union` WITHOUT (1) virtual functions or base classes, (2) reference member
|
||||
* variable or (3) multiple occurrences of base `class` objects, among other
|
||||
* restrictions. Please see
|
||||
* https://en.cppreference.com/w/cpp/named_req/StandardLayoutType
|
||||
* for more information.
|
||||
* ```
|
||||
* bool v = __is_standard_layout(MyType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsStandardLayout extends BuiltInOperation, @isstandardlayoutexpr {
|
||||
override string toString() { result = "__is_standard_layout" }
|
||||
@@ -362,7 +633,12 @@ class BuiltInOperationIsStandardLayout extends BuiltInOperation, @isstandardlayo
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_trivially_copyable` type trait.
|
||||
* The `__is_trivially_copyable` built-in operation (used by some
|
||||
* implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if instances of this type can be copied by trivial
|
||||
* means. The copying is done in a manner similar to the `memcpy`
|
||||
* function.
|
||||
*/
|
||||
class BuiltInOperationIsTriviallyCopyable extends BuiltInOperation, @istriviallycopyableexpr {
|
||||
override string toString() { result = "__is_trivially_copyable" }
|
||||
@@ -371,7 +647,18 @@ class BuiltInOperationIsTriviallyCopyable extends BuiltInOperation, @istrivially
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_literal_type` type trait.
|
||||
* The `__is_literal_type` built-in operation (used by some implementations of
|
||||
* the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the type is a scalar type, a reference type or an array of
|
||||
* literal types, among others. Please see
|
||||
* https://en.cppreference.com/w/cpp/named_req/LiteralType
|
||||
* for more information.
|
||||
*
|
||||
* ```
|
||||
* template <typename _Tp>
|
||||
* std::integral_constant< bool, __is_literal_type(_Tp)> ilt;
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsLiteralType extends BuiltInOperation, @isliteraltypeexpr {
|
||||
override string toString() { result = "__is_literal_type" }
|
||||
@@ -380,7 +667,15 @@ class BuiltInOperationIsLiteralType extends BuiltInOperation, @isliteraltypeexpr
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__has_trivial_move_constructor` type trait.
|
||||
* The `__has_trivial_move_constructor` built-in operation (used by some
|
||||
* implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns true if the move (`&&`) constructor can be generated by the
|
||||
* compiler, with semantics of the `memcpy` operation.
|
||||
* ```
|
||||
* template <typename _Tp>
|
||||
* std::integral_constant< bool, __has_trivial_move_constructor(_Tp)> htmc;
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationHasTrivialMoveConstructor extends BuiltInOperation,
|
||||
@hastrivialmoveconstructorexpr {
|
||||
@@ -390,7 +685,16 @@ class BuiltInOperationHasTrivialMoveConstructor extends BuiltInOperation,
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__has_trivial_move_assign` type trait.
|
||||
* The `__has_trivial_move_assign` built-in operation (used by some
|
||||
* implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns if the move-assign operator `C::operator =(C &&c)` is trivial.
|
||||
* ```
|
||||
* template<typename T>
|
||||
* struct has_trivial_move_assign
|
||||
* : public integral_constant<bool, __has_trivial_move_assign(T) >
|
||||
* { };
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationHasTrivialMoveAssign extends BuiltInOperation, @hastrivialmoveassignexpr {
|
||||
override string toString() { result = "__has_trivial_move_assign" }
|
||||
@@ -399,7 +703,14 @@ class BuiltInOperationHasTrivialMoveAssign extends BuiltInOperation, @hastrivial
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__has_nothrow_move_assign` type trait.
|
||||
* The `__has_nothrow_move_assign` built-in operation (used by some
|
||||
* implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the type has a `C::operator=(C&& c) nothrow`, that is,
|
||||
* an assignment operator with an empty exception specification.
|
||||
* ```
|
||||
* bool v = __has_nothrow_move_assign(MyType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationHasNothrowMoveAssign extends BuiltInOperation, @hasnothrowmoveassignexpr {
|
||||
override string toString() { result = "__has_nothrow_move_assign" }
|
||||
@@ -408,7 +719,17 @@ class BuiltInOperationHasNothrowMoveAssign extends BuiltInOperation, @hasnothrow
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_constructible` type trait.
|
||||
* The `__is_constructible` built-in operation (used by some implementations
|
||||
* of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the type can be constructed using specified arguments
|
||||
* (or none).
|
||||
* ```
|
||||
* template<typename T, typename... Args>
|
||||
* struct is_constructible
|
||||
* : public integral_constant<bool, __is_constructible(T, Args...) >
|
||||
* { };
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsConstructible extends BuiltInOperation, @isconstructibleexpr {
|
||||
override string toString() { result = "__is_constructible" }
|
||||
@@ -417,7 +738,14 @@ class BuiltInOperationIsConstructible extends BuiltInOperation, @isconstructible
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_nothrow_constructible` type trait.
|
||||
* The `__is_nothrow_constructible` built-in operation (used by some
|
||||
* implementations of the `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the type is constructable and all its constructors have an
|
||||
* empty exception specification (i.e., are declared with `nothrow`);
|
||||
* ```
|
||||
* bool v = __is_nothrow_constructible(MyType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsNothrowConstructible extends BuiltInOperation, @isnothrowconstructibleexpr {
|
||||
override string toString() { result = "__is_nothrow_constructible" }
|
||||
@@ -426,7 +754,13 @@ class BuiltInOperationIsNothrowConstructible extends BuiltInOperation, @isnothro
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__has_finalizer` type trait.
|
||||
* The `__has_finalizer` built-in operation. This is a Microsoft extension.
|
||||
*
|
||||
* Returns `true` if the type defines a _finalizer_ `C::!C(void)`, to be called
|
||||
* from either the regular destructor or the garbage collector.
|
||||
* ```
|
||||
* bool v = __has_finalizer(MyType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationHasFinalizer extends BuiltInOperation, @hasfinalizerexpr {
|
||||
override string toString() { result = "__has_finalizer" }
|
||||
@@ -435,7 +769,12 @@ class BuiltInOperationHasFinalizer extends BuiltInOperation, @hasfinalizerexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_delegate` type trait.
|
||||
* The `__is_delegate` built-in operation. This is a Microsoft extension.
|
||||
*
|
||||
* Returns `true` if the function has been declared as a `delegate`, used in
|
||||
* message forwarding. Please see
|
||||
* https://docs.microsoft.com/en-us/cpp/extensions/delegate-cpp-component-extensions
|
||||
* for more information.
|
||||
*/
|
||||
class BuiltInOperationIsDelegate extends BuiltInOperation, @isdelegateexpr {
|
||||
override string toString() { result = "__is_delegate" }
|
||||
@@ -444,7 +783,11 @@ class BuiltInOperationIsDelegate extends BuiltInOperation, @isdelegateexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_interface_class` type trait.
|
||||
* The `__is_interface_class` built-in operation. This is a Microsoft extension.
|
||||
*
|
||||
* Returns `true` if the type has been declared as an `interface`. Please see
|
||||
* https://docs.microsoft.com/en-us/cpp/extensions/interface-class-cpp-component-extensions
|
||||
* for more information.
|
||||
*/
|
||||
class BuiltInOperationIsInterfaceClass extends BuiltInOperation, @isinterfaceclassexpr {
|
||||
override string toString() { result = "__is_interface_class" }
|
||||
@@ -453,7 +796,15 @@ class BuiltInOperationIsInterfaceClass extends BuiltInOperation, @isinterfacecla
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_ref_array` type trait.
|
||||
* The `__is_ref_array` built-in operation. This is a Microsoft extension.
|
||||
*
|
||||
* Returns `true` if the object passed in is a _platform array_. Please see
|
||||
* https://docs.microsoft.com/en-us/cpp/extensions/arrays-cpp-component-extensions
|
||||
* for more information.
|
||||
* ```
|
||||
* array<int>^ x = gcnew array<int>(10);
|
||||
* bool b = __is_ref_array(array<int>);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsRefArray extends BuiltInOperation, @isrefarrayexpr {
|
||||
override string toString() { result = "__is_ref_array" }
|
||||
@@ -462,7 +813,15 @@ class BuiltInOperationIsRefArray extends BuiltInOperation, @isrefarrayexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_ref_class` type trait.
|
||||
* The `__is_ref_class` built-in operation. This is a Microsoft extension.
|
||||
*
|
||||
* Returns `true` if the type is a _reference class_. Please see
|
||||
* https://docs.microsoft.com/en-us/cpp/extensions/classes-and-structs-cpp-component-extensions
|
||||
* for more information.
|
||||
* ```
|
||||
* ref class R {};
|
||||
* bool b = __is_ref_class(R);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsRefClass extends BuiltInOperation, @isrefclassexpr {
|
||||
override string toString() { result = "__is_ref_class" }
|
||||
@@ -471,7 +830,16 @@ class BuiltInOperationIsRefClass extends BuiltInOperation, @isrefclassexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_sealed` type trait.
|
||||
* The `__is_sealed` built-in operation. This is a Microsoft extension.
|
||||
*
|
||||
* Returns `true` if a given class or virtual function is marked as `sealed`,
|
||||
* meaning that it cannot be extended or overridden. The `sealed` keyword
|
||||
* is similar to the C++11 `final` keyword.
|
||||
* ```
|
||||
* ref class X sealed {
|
||||
* virtual void f() sealed { }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsSealed extends BuiltInOperation, @issealedexpr {
|
||||
override string toString() { result = "__is_sealed" }
|
||||
@@ -480,7 +848,17 @@ class BuiltInOperationIsSealed extends BuiltInOperation, @issealedexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_simple_value_class` type trait.
|
||||
* The `__is_simple_value_class` built-in operation. This is a Microsoft extension.
|
||||
*
|
||||
* Returns `true` if passed a value type that contains no references to the
|
||||
* garbage-collected heap.
|
||||
* ```
|
||||
* ref class R {}; // __is_simple_value_class(R) == false
|
||||
* value struct V {}; // __is_simple_value_class(V) == true
|
||||
* value struct V2 { // __is_simple_value_class(V2) == false
|
||||
* R ^ r; // not a simple value type
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsSimpleValueClass extends BuiltInOperation, @issimplevalueclassexpr {
|
||||
override string toString() { result = "__is_simple_value_class" }
|
||||
@@ -489,7 +867,15 @@ class BuiltInOperationIsSimpleValueClass extends BuiltInOperation, @issimplevalu
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_value_class` type trait.
|
||||
* The `__is_value_class` built-in operation. This is a Microsoft extension.
|
||||
*
|
||||
* Returns `true` if passed a value type. Please see
|
||||
* https://docs.microsoft.com/en-us/cpp/extensions/classes-and-structs-cpp-component-extensions
|
||||
* For more information.
|
||||
* ```
|
||||
* value struct V {};
|
||||
* bool v = __is_value_class(V);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsValueClass extends BuiltInOperation, @isvalueclassexpr {
|
||||
override string toString() { result = "__is_value_class" }
|
||||
@@ -498,7 +884,16 @@ class BuiltInOperationIsValueClass extends BuiltInOperation, @isvalueclassexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__is_final` type trait.
|
||||
* The `__is_final` built-in operation (used by some implementations of the
|
||||
* `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if the `class` has been marked with the `final` specifier.
|
||||
* ```
|
||||
* template<typename T>
|
||||
* struct is_final
|
||||
* : public integral_constant<bool, __is_final(T) >
|
||||
* { };
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsFinal extends BuiltInOperation, @isfinalexpr {
|
||||
override string toString() { result = "__is_final" }
|
||||
@@ -507,17 +902,35 @@ class BuiltInOperationIsFinal extends BuiltInOperation, @isfinalexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* The `__builtin_choose_expr` type trait.
|
||||
* The `__builtin_choose_expr` expression. This is a GNU/Clang extension.
|
||||
*
|
||||
* The expression functions similarly to the ternary `?:` operator, except
|
||||
* that it is evaluated at compile-time.
|
||||
* ```
|
||||
* int sz = __builtin_choose_expr(__builtin_types_compatible_p(int, long), 4, 8);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInChooseExpr extends BuiltInOperation, @builtinchooseexpr {
|
||||
override string toString() { result = "__builtin_choose_expr" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "BuiltInChooseExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill operation on a GNU vector.
|
||||
* Fill operation on a vector. This is a GNU extension.
|
||||
*
|
||||
* A single scalar value is used to populate all the elements in a vector.
|
||||
* In the example below, the scalar value is `25`:
|
||||
* ```
|
||||
* typedef int v16i __attribute__((vector_size(16)));
|
||||
* v16i src, dst;
|
||||
* dst = src << 25;
|
||||
* ```
|
||||
*/
|
||||
class VectorFillOperation extends UnaryOperation, @vec_fill {
|
||||
override string getOperator() { result = "(vector fill)" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "VectorFillOperation" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,6 +4,8 @@ private import semmle.code.cpp.dataflow.EscapesTree
|
||||
|
||||
/**
|
||||
* A C/C++ call.
|
||||
*
|
||||
* This is the abstract root QL class for all types of calls.
|
||||
*/
|
||||
abstract class Call extends Expr, NameQualifiableElement {
|
||||
/**
|
||||
@@ -139,17 +141,29 @@ class FunctionCall extends Call, @funbindexpr {
|
||||
override string getCanonicalQLClass() { result = "FunctionCall" }
|
||||
|
||||
/** Gets an explicit template argument for this call. */
|
||||
Type getAnExplicitTemplateArgument() { result = getExplicitTemplateArgument(_) }
|
||||
Locatable getAnExplicitTemplateArgument() { result = getExplicitTemplateArgument(_) }
|
||||
|
||||
/** Gets an explicit template argument value for this call. */
|
||||
Locatable getAnExplicitTemplateArgumentKind() { result = getExplicitTemplateArgumentKind(_) }
|
||||
|
||||
/** Gets a template argument for this call. */
|
||||
Type getATemplateArgument() { result = getTarget().getATemplateArgument() }
|
||||
Locatable getATemplateArgument() { result = getTarget().getATemplateArgument() }
|
||||
|
||||
/** Gets a template argument value for this call. */
|
||||
Locatable getATemplateArgumentKind() { result = getTarget().getATemplateArgumentKind() }
|
||||
|
||||
/** Gets the nth explicit template argument for this call. */
|
||||
Type getExplicitTemplateArgument(int n) {
|
||||
Locatable getExplicitTemplateArgument(int n) {
|
||||
n < getNumberOfExplicitTemplateArguments() and
|
||||
result = getTemplateArgument(n)
|
||||
}
|
||||
|
||||
/** Gets the nth explicit template argument value for this call. */
|
||||
Locatable getExplicitTemplateArgumentKind(int n) {
|
||||
n < getNumberOfExplicitTemplateArguments() and
|
||||
result = getTemplateArgumentKind(n)
|
||||
}
|
||||
|
||||
/** Gets the number of explicit template arguments for this call. */
|
||||
int getNumberOfExplicitTemplateArguments() {
|
||||
if numtemplatearguments(underlyingElement(this), _)
|
||||
@@ -161,7 +175,10 @@ class FunctionCall extends Call, @funbindexpr {
|
||||
int getNumberOfTemplateArguments() { result = count(int i | exists(getTemplateArgument(i))) }
|
||||
|
||||
/** Gets the nth template argument for this call (indexed from 0). */
|
||||
Type getTemplateArgument(int n) { result = getTarget().getTemplateArgument(n) }
|
||||
Locatable getTemplateArgument(int n) { result = getTarget().getTemplateArgument(n) }
|
||||
|
||||
/** Gets the nth template argument value for this call (indexed from 0). */
|
||||
Locatable getTemplateArgumentKind(int n) { result = getTarget().getTemplateArgumentKind(n) }
|
||||
|
||||
/** Holds if any template arguments for this call are implicit / deduced. */
|
||||
predicate hasImplicitTemplateArguments() {
|
||||
@@ -213,7 +230,7 @@ class FunctionCall extends Call, @funbindexpr {
|
||||
* Gets the function called by this call.
|
||||
*
|
||||
* In the case of virtual function calls, the result is the most-specific function in the override tree (as
|
||||
* determined by the compiler) such that the target at runtime will be one of result.getAnOverridingFunction*().
|
||||
* determined by the compiler) such that the target at runtime will be one of `result.getAnOverridingFunction*()`.
|
||||
*/
|
||||
override Function getTarget() { funbind(underlyingElement(this), unresolveElement(result)) }
|
||||
|
||||
@@ -258,7 +275,11 @@ class FunctionCall extends Call, @funbindexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance of unary operator * applied to a user-defined type.
|
||||
* An instance of a _user-defined_ unary `operator*` applied to its argument.
|
||||
* ```
|
||||
* T1 operator*(const T2 &);
|
||||
* T1 a; T2 b;
|
||||
* a = *b;
|
||||
*/
|
||||
class OverloadedPointerDereferenceExpr extends FunctionCall {
|
||||
OverloadedPointerDereferenceExpr() {
|
||||
@@ -266,6 +287,8 @@ class OverloadedPointerDereferenceExpr extends FunctionCall {
|
||||
getTarget().getEffectiveNumberOfParameters() = 1
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() { result = "OverloadedPointerDereferenceExpr" }
|
||||
|
||||
/**
|
||||
* Gets the expression this operator * applies to.
|
||||
*/
|
||||
@@ -302,11 +325,18 @@ class OverloadedPointerDereferenceExpr extends FunctionCall {
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance of operator [] applied to a user-defined type.
|
||||
* An instance of a _user-defined_ binary `operator[]` applied to its arguments.
|
||||
* ```
|
||||
* struct T2 { T1 operator[](const T3 &); };
|
||||
* T1 a; T2 b; T3 c;
|
||||
* a = b[c];
|
||||
* ```
|
||||
*/
|
||||
class OverloadedArrayExpr extends FunctionCall {
|
||||
OverloadedArrayExpr() { getTarget().hasName("operator[]") }
|
||||
|
||||
override string getCanonicalQLClass() { result = "OverloadedArrayExpr" }
|
||||
|
||||
/**
|
||||
* Gets the expression being subscripted.
|
||||
*/
|
||||
@@ -324,6 +354,12 @@ class OverloadedArrayExpr extends FunctionCall {
|
||||
|
||||
/**
|
||||
* A C/C++ call which is performed through a function pointer.
|
||||
*
|
||||
* In the call below, `(*funcptr)` may be simplified to just `funcptr`.
|
||||
* ```
|
||||
* extern int (*funcptr)(int a, int b);
|
||||
* int c = (*funcptr)(1, 2);
|
||||
* ```
|
||||
*/
|
||||
class ExprCall extends Call, @callexpr {
|
||||
/**
|
||||
@@ -346,6 +382,11 @@ class ExprCall extends Call, @callexpr {
|
||||
|
||||
/**
|
||||
* A C/C++ call which is performed through a variable of function pointer type.
|
||||
* ```
|
||||
* int call_via_ptr(int (*pfn)(int)) {
|
||||
* return pfn(5);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class VariableCall extends ExprCall {
|
||||
VariableCall() { this.getExpr() instanceof VariableAccess }
|
||||
@@ -360,6 +401,10 @@ class VariableCall extends ExprCall {
|
||||
|
||||
/**
|
||||
* A call to a constructor.
|
||||
* ```
|
||||
* struct S { S(void) {} };
|
||||
* S s;
|
||||
* ```
|
||||
*/
|
||||
class ConstructorCall extends FunctionCall {
|
||||
ConstructorCall() { super.getTarget() instanceof Constructor }
|
||||
@@ -372,6 +417,9 @@ class ConstructorCall extends FunctionCall {
|
||||
|
||||
/**
|
||||
* A C++ `throw` expression.
|
||||
* ```
|
||||
* throw Exc(2);
|
||||
* ```
|
||||
*/
|
||||
class ThrowExpr extends Expr, @throw_expr {
|
||||
/**
|
||||
@@ -389,6 +437,9 @@ class ThrowExpr extends Expr, @throw_expr {
|
||||
|
||||
/**
|
||||
* A C++ `throw` expression with no argument (which causes the current exception to be re-thrown).
|
||||
* ```
|
||||
* throw;
|
||||
* ```
|
||||
*/
|
||||
class ReThrowExpr extends ThrowExpr {
|
||||
ReThrowExpr() { this.getType() instanceof VoidType }
|
||||
@@ -400,6 +451,10 @@ class ReThrowExpr extends ThrowExpr {
|
||||
|
||||
/**
|
||||
* A call to a destructor.
|
||||
* ```
|
||||
* struct S { ~S(void) {} } *s;
|
||||
* s->~S();
|
||||
* ```
|
||||
*/
|
||||
class DestructorCall extends FunctionCall {
|
||||
DestructorCall() { super.getTarget() instanceof Destructor }
|
||||
@@ -416,6 +471,11 @@ class DestructorCall extends FunctionCall {
|
||||
* For example, given a plain old data type `pod_t`, the syntax `ptr->~pod_t()` is
|
||||
* a vacuous destructor call, as `~pod_t` isn't actually a function. This can also
|
||||
* occur in instantiated templates, as `ptr->~T()` becomes vacuous when `T` is `int`.
|
||||
* ```
|
||||
* typedef int pod_t;
|
||||
* pod_t *s;
|
||||
* s->~pod_t();
|
||||
* ```
|
||||
*/
|
||||
class VacuousDestructorCall extends Expr, @vacuous_destructor_call {
|
||||
/**
|
||||
@@ -431,6 +491,9 @@ class VacuousDestructorCall extends Expr, @vacuous_destructor_call {
|
||||
/**
|
||||
* An initialization of a base class or member variable performed as part
|
||||
* of a constructor's explicit initializer list or implicit actions.
|
||||
*
|
||||
* This is a QL root class for reprenting various types of constructor
|
||||
* initializations.
|
||||
*/
|
||||
class ConstructorInit extends Expr, @ctorinit {
|
||||
override string getCanonicalQLClass() { result = "ConstructorInit" }
|
||||
@@ -447,6 +510,15 @@ class ConstructorBaseInit extends ConstructorInit, ConstructorCall {
|
||||
/**
|
||||
* A call to a constructor of a direct non-virtual base class as part of a
|
||||
* constructor's initializer list or compiler-generated actions.
|
||||
* ```
|
||||
* struct S {
|
||||
* int a;
|
||||
* S(int b): a(b) {}
|
||||
* };
|
||||
* struct T: S {
|
||||
* T(): S(33) {} // S(33) is a constructor call
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class ConstructorDirectInit extends ConstructorBaseInit, @ctordirectinit {
|
||||
override string getCanonicalQLClass() { result = "ConstructorDirectInit" }
|
||||
@@ -458,6 +530,15 @@ class ConstructorDirectInit extends ConstructorBaseInit, @ctordirectinit {
|
||||
*
|
||||
* If the virtual base class has already been initialized, then this
|
||||
* call won't be performed.
|
||||
* ```
|
||||
* struct S {
|
||||
* int a;
|
||||
* S(int b): a(b) {}
|
||||
* };
|
||||
* struct T: virtual S {
|
||||
* T(): S(33) {} // S(33) is a call to a virtual base constructor
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class ConstructorVirtualInit extends ConstructorBaseInit, @ctorvirtualinit {
|
||||
override string getCanonicalQLClass() { result = "ConstructorVirtualInit" }
|
||||
@@ -466,6 +547,13 @@ class ConstructorVirtualInit extends ConstructorBaseInit, @ctorvirtualinit {
|
||||
/**
|
||||
* A call to a constructor of the same class as part of a constructor's
|
||||
* initializer list, which delegates object construction (C++11 only).
|
||||
* ```
|
||||
* struct S {
|
||||
* int a;
|
||||
* S(int b): a(b) { }
|
||||
* S(): S(0) { } // delegation to another constructor
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class ConstructorDelegationInit extends ConstructorBaseInit, @ctordelegatinginit {
|
||||
override string getCanonicalQLClass() { result = "ConstructorDelegationInit" }
|
||||
@@ -474,6 +562,14 @@ class ConstructorDelegationInit extends ConstructorBaseInit, @ctordelegatinginit
|
||||
/**
|
||||
* An initialization of a member variable performed as part of a
|
||||
* constructor's explicit initializer list or implicit actions.
|
||||
* In the example below, member variable `b` is being initialized by
|
||||
* constructor parameter `a`:
|
||||
* ```
|
||||
* struct S {
|
||||
* int b;
|
||||
* S(int a): b(a) {}
|
||||
* } s(2);
|
||||
* ```
|
||||
*/
|
||||
class ConstructorFieldInit extends ConstructorInit, @ctorfieldinit {
|
||||
/** Gets the field being initialized. */
|
||||
@@ -515,6 +611,12 @@ class DestructorBaseDestruction extends DestructorCall, DestructorDestruction {
|
||||
/**
|
||||
* A call to a destructor of a direct non-virtual base class as part of a
|
||||
* destructor's compiler-generated actions.
|
||||
* ```
|
||||
* struct S { ~S(void) {} };
|
||||
* struct T: S {
|
||||
* ~T(void) {} // will call ~S()
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class DestructorDirectDestruction extends DestructorBaseDestruction, @dtordirectdestruct {
|
||||
override string getCanonicalQLClass() { result = "DestructorDirectDestruction" }
|
||||
@@ -526,6 +628,12 @@ class DestructorDirectDestruction extends DestructorBaseDestruction, @dtordirect
|
||||
*
|
||||
* If the virtual base class wasn't initialized by the ConstructorVirtualInit
|
||||
* in the corresponding constructor, then this call won't be performed.
|
||||
* ```
|
||||
* struct S { ~S(void) {} };
|
||||
* struct T: virtual S {
|
||||
* ~T(void) {} // will call ~S()
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class DestructorVirtualDestruction extends DestructorBaseDestruction, @dtorvirtualdestruct {
|
||||
override string getCanonicalQLClass() { result = "DestructorVirtualDestruction" }
|
||||
@@ -534,6 +642,13 @@ class DestructorVirtualDestruction extends DestructorBaseDestruction, @dtorvirtu
|
||||
/**
|
||||
* A destruction of a member variable performed as part of a
|
||||
* destructor's compiler-generated actions.
|
||||
* ```
|
||||
* struct S { ~S(void) {} };
|
||||
* struct T {
|
||||
* S s;
|
||||
* ~T(void) {} // will call s.~S()
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class DestructorFieldDestruction extends DestructorDestruction, @dtorfielddestruct {
|
||||
/** Gets the field being destructed. */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user