This commit is contained in:
Raul Garcia (MSFT)
2019-11-22 13:25:22 -08:00
1293 changed files with 62909 additions and 37423 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,3 @@
bool foo(int n1, unsigned short delta) {
return n1 + delta < n1; // BAD
}

View File

@@ -0,0 +1,4 @@
bool bar(unsigned short n1, unsigned short delta) {
// NB: Comparison is always false
return n1 + delta < n1; // GOOD (but misleading)
}

View File

@@ -0,0 +1,4 @@
#include <limits.h>
bool foo(int n1, unsigned short delta) {
return n1 > INT_MAX - delta; // GOOD
}

View File

@@ -0,0 +1,3 @@
bool bar(unsigned short n1, unsigned short delta) {
return (unsigned short)(n1 + delta) < n1; // GOOD
}

View 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 &lt; 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 &lt; i</code>,
it is possible to rewrite it as <code>(unsigned short)(i + delta)&nbsp;&lt;&nbsp;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 &lt; i</code>,
it is also possible to rewrite it as <code>USHORT_MAX - delta</code>. It must be true
that <code>delta &gt; 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 &lt; i</code>,
it is possible to rewrite it as <code>INT_MAX - delta</code>. It must be true
that <code>delta &gt; 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 &lt; i</code>,
it is also possible to rewrite it as <code>(unsigned)i + delta &lt; i</code>.
Note that program semantics are affected by this change.
</p>
<p>
Given <code>int i, delta</code> and <code>i + delta &lt; i</code>,
it is also possible to rewrite it as <code>unsigned int i, delta</code> and
<code>i + delta &lt; 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 &lt; 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>

View 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."

View File

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

View File

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

View File

@@ -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(), _)
)
}
}

View File

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

View File

@@ -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(), _)
)
}
}

View File

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

View File

@@ -0,0 +1,3 @@
bool not_in_range(T *ptr, T *ptr_end, size_t i) {
return i >= ptr_end - ptr; // GOOD
}

View File

@@ -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 &lt; 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 &lt; 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 &lt; 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 &lt; 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 &lt;
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>

View 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."

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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'. */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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&amp;
* 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 (&amp;) and rvalue references (&amp;&amp;).
* 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&amp;).
* 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&amp;&amp;).
* 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 &lt;template &lt;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) }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 {
/**

View File

@@ -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 = "^" }

View File

@@ -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" }
}
/**

View File

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