mirror of
https://github.com/github/codeql.git
synced 2025-12-21 19:26:31 +01:00
Merge remote-tracking branch 'upstream/master' into ir-copy-unloaded-result
Fixed conflicts by accepting new qltest output.
Conflicts:
cpp/ql/test/library-tests/ir/ir/raw_ir.expected
cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected
cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected
cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_sanity.expected
cpp/ql/test/library-tests/syntax-zoo/unaliased_ssa_sanity.expected
This commit is contained in:
3
.codeqlmanifest.json
Normal file
3
.codeqlmanifest.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{ "provide": [ "*/ql/src/qlpack.yml",
|
||||||
|
"misc/legacy-support/*/qlpack.yml",
|
||||||
|
"misc/suite-helpers/qlpack.yml" ] }
|
||||||
14
.github/ISSUE_TEMPLATE/ql---general.md
vendored
Normal file
14
.github/ISSUE_TEMPLATE/ql---general.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
name: General issue
|
||||||
|
about: Tell us if you think something is wrong or if you have a question
|
||||||
|
title: General issue
|
||||||
|
labels: question
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Description of the issue**
|
||||||
|
|
||||||
|
<!-- Please explain briefly what is the problem.
|
||||||
|
If it is about an LGTM project, please include its URL.-->
|
||||||
|
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -12,3 +12,6 @@
|
|||||||
# Visual studio temporaries, except a file used by QL4VS
|
# Visual studio temporaries, except a file used by QL4VS
|
||||||
.vs/*
|
.vs/*
|
||||||
!.vs/VSWorkspaceSettings.json
|
!.vs/VSWorkspaceSettings.json
|
||||||
|
|
||||||
|
# It's useful (though not required) to be able to unpack codeql in the ql checkout itself
|
||||||
|
/codeql/
|
||||||
|
|||||||
10
CODEOWNERS
10
CODEOWNERS
@@ -2,9 +2,9 @@
|
|||||||
/java/ @Semmle/java
|
/java/ @Semmle/java
|
||||||
/javascript/ @Semmle/js
|
/javascript/ @Semmle/js
|
||||||
/cpp/ @Semmle/cpp-analysis
|
/cpp/ @Semmle/cpp-analysis
|
||||||
/cpp/**/*.qhelp @semmledocs-ac
|
/cpp/**/*.qhelp @hubwriter
|
||||||
/csharp/**/*.qhelp @jf205
|
/csharp/**/*.qhelp @jf205
|
||||||
/java/**/*.qhelp @felicity-semmle
|
/java/**/*.qhelp @felicitymay
|
||||||
/javascript/**/*.qhelp @mc-semmle
|
/javascript/**/*.qhelp @mchammer01
|
||||||
/python/**/*.qhelp @felicity-semmle
|
/python/**/*.qhelp @felicitymay
|
||||||
/docs/language/ @felicity-semmle @jf205
|
/docs/language/ @shati-patel @jf205
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
| Shift out of range (`js/shift-out-of-range`| Fewer false positive results | This rule now correctly handles BigInt shift operands. |
|
| Shift out of range (`js/shift-out-of-range`| Fewer false positive results | This rule now correctly handles BigInt shift operands. |
|
||||||
| Superfluous trailing arguments (`js/superfluous-trailing-arguments`) | Fewer false-positive results. | This rule no longer flags calls to placeholder functions that trivially throw an exception. |
|
| Superfluous trailing arguments (`js/superfluous-trailing-arguments`) | Fewer false-positive results. | This rule no longer flags calls to placeholder functions that trivially throw an exception. |
|
||||||
| Undocumented parameter (`js/jsdoc/missing-parameter`) | No changes to results | This rule is now run on LGTM, although its results are still not shown by default. |
|
| Undocumented parameter (`js/jsdoc/missing-parameter`) | No changes to results | This rule is now run on LGTM, although its results are still not shown by default. |
|
||||||
|
| Missing space in string concatenation (`js/missing-space-in-concatenation`) | Fewer false positive results | The rule now requires a word-like part exists in the string concatenation. |
|
||||||
|
|
||||||
## Changes to QL libraries
|
## Changes to QL libraries
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ The following changes in version 1.23 affect C/C++ analysis in all applications.
|
|||||||
| Hard-coded Japanese era start date in call (`cpp/japanese-era/constructor-or-method-with-exact-era-date`) | Deprecated | This query has been deprecated. Use the new combined query Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) instead. |
|
| Hard-coded Japanese era start date in call (`cpp/japanese-era/constructor-or-method-with-exact-era-date`) | Deprecated | This query has been deprecated. Use the new combined query Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) instead. |
|
||||||
| Hard-coded Japanese era start date in struct (`cpp/japanese-era/struct-with-exact-era-date`) | Deprecated | This query has been deprecated. Use the new combined query Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) instead. |
|
| Hard-coded Japanese era start date in struct (`cpp/japanese-era/struct-with-exact-era-date`) | Deprecated | This query has been deprecated. Use the new combined query Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) instead. |
|
||||||
| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | More correct results | This query now checks for the beginning date of the Reiwa era (1st May 2019). |
|
| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | More correct results | This query now checks for the beginning date of the Reiwa era (1st May 2019). |
|
||||||
|
| Sign check of bitwise operation (`cpp/bitwise-sign-check`) | Fewer false positive results | Results involving `>=` or `<=` are no longer reported. |
|
||||||
|
| 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. |
|
||||||
|
|
||||||
## Changes to QL libraries
|
## Changes to QL libraries
|
||||||
|
|
||||||
@@ -28,6 +32,7 @@ The following changes in version 1.23 affect C/C++ analysis in all applications.
|
|||||||
picture of the partial flow paths from a given source. The feature is
|
picture of the partial flow paths from a given source. The feature is
|
||||||
disabled by default and can be enabled for individual configurations by
|
disabled by default and can be enabled for individual configurations by
|
||||||
overriding `int explorationLimit()`.
|
overriding `int explorationLimit()`.
|
||||||
|
* The data-flow library now supports flow out of C++ reference parameters.
|
||||||
* The data-flow library now allows flow through the address-of operator (`&`).
|
* The data-flow library now allows flow through the address-of operator (`&`).
|
||||||
* The `DataFlow::DefinitionByReferenceNode` class now considers `f(x)` to be a
|
* The `DataFlow::DefinitionByReferenceNode` class now considers `f(x)` to be a
|
||||||
definition of `x` when `x` is a variable of pointer type. It no longer
|
definition of `x` when `x` is a variable of pointer type. It no longer
|
||||||
@@ -36,3 +41,11 @@ The following changes in version 1.23 affect C/C++ analysis in all applications.
|
|||||||
* There is now a `DataFlow::localExprFlow` predicate and a
|
* There is now a `DataFlow::localExprFlow` predicate and a
|
||||||
`TaintTracking::localExprTaint` predicate to make it easy to use the most
|
`TaintTracking::localExprTaint` predicate to make it easy to use the most
|
||||||
common case of local data flow and taint: from one `Expr` to another.
|
common case of local data flow and taint: from one `Expr` to another.
|
||||||
|
* The member predicates of the `FunctionInput` and `FunctionOutput` classes have been renamed for
|
||||||
|
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 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.
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ The following changes in version 1.23 affect C# analysis in all applications.
|
|||||||
| **Query** | **Expected impact** | **Change** |
|
| **Query** | **Expected impact** | **Change** |
|
||||||
|------------------------------|------------------------|-----------------------------------|
|
|------------------------------|------------------------|-----------------------------------|
|
||||||
| Dereferenced variable may be null (`cs/dereferenced-value-may-be-null`) | Fewer false positive results | More `null` checks are now taken into account, including `null` checks for `dynamic` expressions and `null` checks such as `object alwaysNull = null; if (x != alwaysNull) ...`. |
|
| Dereferenced variable may be null (`cs/dereferenced-value-may-be-null`) | Fewer false positive results | More `null` checks are now taken into account, including `null` checks for `dynamic` expressions and `null` checks such as `object alwaysNull = null; if (x != alwaysNull) ...`. |
|
||||||
|
| Missing Dispose call on local IDisposable (`cs/local-not-disposed`) | Fewer false positive results | The query has been rewritten in order to identify more dispose patterns. For example, a local `IDisposable` that is disposed of by passing through a fluent API is no longer reported. |
|
||||||
|
|
||||||
## Removal of old queries
|
## Removal of old queries
|
||||||
|
|
||||||
@@ -38,5 +39,9 @@ The following changes in version 1.23 affect C# analysis in all applications.
|
|||||||
disabled by default and can be enabled for individual configurations by
|
disabled by default and can be enabled for individual configurations by
|
||||||
overriding `int explorationLimit()`.
|
overriding `int explorationLimit()`.
|
||||||
* `foreach` statements where the body is guaranteed to be executed at least once, such as `foreach (var x in new string[]{ "a", "b", "c" }) { ... }`, are now recognized by all analyses based on the control flow graph (such as SSA, data flow and taint tracking).
|
* `foreach` statements where the body is guaranteed to be executed at least once, such as `foreach (var x in new string[]{ "a", "b", "c" }) { ... }`, are now recognized by all analyses based on the control flow graph (such as SSA, data flow and taint tracking).
|
||||||
|
* Fixed the control flow graph for `switch` statements where the `default` case was not the last case. This had caused the remaining cases to be unreachable. `SwitchStmt.getCase(int i)` now puts the `default` case last.
|
||||||
|
* 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.
|
||||||
|
|
||||||
## Changes to autobuilder
|
## Changes to autobuilder
|
||||||
|
|||||||
@@ -6,9 +6,12 @@ The following changes in version 1.23 affect Java analysis in all applications.
|
|||||||
|
|
||||||
| **Query** | **Expected impact** | **Change** |
|
| **Query** | **Expected impact** | **Change** |
|
||||||
|------------------------------|------------------------|-----------------------------------|
|
|------------------------------|------------------------|-----------------------------------|
|
||||||
|
| Dereferenced variable may be null (`java/dereferenced-value-may-be-null`) | Fewer false positives | Certain indirect null guards involving two auxiliary variables known to be equal can now be detected. |
|
||||||
|
| Non-synchronized override of synchronized method (`java/non-sync-override`) | Fewer false positives | Results are now only reported if the immediately overridden method is synchronized. |
|
||||||
| Query built from user-controlled sources (`java/sql-injection`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as SQL expressions sinks. |
|
| Query built from user-controlled sources (`java/sql-injection`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as SQL expressions sinks. |
|
||||||
| Query built from local-user-controlled sources (`java/sql-injection-local`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as SQL expressions sinks. |
|
| Query built from local-user-controlled sources (`java/sql-injection-local`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as SQL expressions sinks. |
|
||||||
| 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. |
|
| 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 QL libraries
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
## General improvements
|
## General improvements
|
||||||
|
|
||||||
|
* Suppor for `globalThis` has been added.
|
||||||
|
|
||||||
* Support for the following frameworks and libraries has been improved:
|
* Support for the following frameworks and libraries has been improved:
|
||||||
- [firebase](https://www.npmjs.com/package/firebase)
|
- [firebase](https://www.npmjs.com/package/firebase)
|
||||||
- [mongodb](https://www.npmjs.com/package/mongodb)
|
- [mongodb](https://www.npmjs.com/package/mongodb)
|
||||||
@@ -16,22 +18,47 @@
|
|||||||
|---------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|---------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| 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. |
|
| 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. |
|
||||||
| 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 not 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 not 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.|
|
||||||
|
| 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
|
## Changes to existing queries
|
||||||
|
|
||||||
| **Query** | **Expected impact** | **Change** |
|
| **Query** | **Expected impact** | **Change** |
|
||||||
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
|
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
|
||||||
| Incomplete string escaping or encoding (`js/incomplete-sanitization`) | Fewer false-positive results | This rule now recognizes additional ways delimiters can be stripped away. |
|
| Incomplete string escaping or encoding (`js/incomplete-sanitization`) | Fewer false-positive results | This rule now recognizes additional ways delimiters can be stripped away. |
|
||||||
| Client-side cross-site scripting (`js/xss`) | More results | More potential vulnerabilities involving functions that manipulate DOM attributes are now recognized. |
|
| Client-side cross-site scripting (`js/xss`) | More results, fewer false-positive results | More potential vulnerabilities involving functions that manipulate DOM attributes are now recognized, and more sanitizers are detected. |
|
||||||
| Code injection (`js/code-injection`) | More results | More potential vulnerabilities involving functions that manipulate DOM event handler attributes are now recognized. |
|
| Code injection (`js/code-injection`) | More results | More potential vulnerabilities involving functions that manipulate DOM event handler attributes are now recognized. |
|
||||||
| Hard-coded credentials (`js/hardcoded-credentials`) | Fewer false-positive results | This rule now flags fewer password examples. |
|
| Hard-coded credentials (`js/hardcoded-credentials`) | Fewer false-positive results | This rule now flags fewer password examples. |
|
||||||
| Illegal invocation (`js/illegal-invocation`) | Fewer false-positive results | This rule now correctly handles methods named `call` and `apply`. |
|
| Illegal invocation (`js/illegal-invocation`) | Fewer false-positive results | This rule now correctly handles methods named `call` and `apply`. |
|
||||||
| Incorrect suffix check (`js/incorrect-suffix-check`) | Fewer false-positive results | The query recognizes valid checks in more cases.
|
| Incorrect suffix check (`js/incorrect-suffix-check`) | Fewer false-positive results | The query recognizes valid checks in more cases. |
|
||||||
| Network data written to file (`js/http-to-file-access`) | Fewer false-positive results | This query has been renamed to better match its intended purpose, and now only considers network data untrusted. |
|
| Network data written to file (`js/http-to-file-access`) | Fewer false-positive results | This query has been renamed to better match its intended purpose, and now only considers network data untrusted. |
|
||||||
| Password in configuration file (`js/password-in-configuration-file`) | Fewer false-positive results | This rule now flags fewer password examples. |
|
| Password in configuration file (`js/password-in-configuration-file`) | Fewer false-positive results | This rule now flags fewer password examples. |
|
||||||
| Prototype pollution (`js/prototype-pollution`) | More results | The query now highlights vulnerable uses of jQuery and Angular, and the results are shown on LGTM by default. |
|
| Prototype pollution (`js/prototype-pollution`) | More results | The query now highlights vulnerable uses of jQuery and Angular, and the results are shown on LGTM by default. |
|
||||||
|
| 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 command line (`js/command-line-injection`) | More results | This query now treats responses from servers as untrusted. |
|
||||||
|
|
||||||
## Changes to QL libraries
|
## Changes to QL libraries
|
||||||
|
|
||||||
* `Expr.getDocumentation()` now handles chain assignments.
|
* `Expr.getDocumentation()` now handles chain assignments.
|
||||||
|
|
||||||
|
## Removal of deprecated queries
|
||||||
|
|
||||||
|
The following queries (deprecated since 1.17) are no longer available in the distribution:
|
||||||
|
|
||||||
|
* Builtin redefined (js/builtin-redefinition)
|
||||||
|
* Inefficient method definition (js/method-definition-in-constructor)
|
||||||
|
* Bad parity check (js/incomplete-parity-check)
|
||||||
|
* Potentially misspelled property or variable name (js/wrong-capitalization)
|
||||||
|
* Unknown JSDoc tag (js/jsdoc/unknown-tag-type)
|
||||||
|
* Invalid JSLint directive (js/jslint/invalid-directive)
|
||||||
|
* Malformed JSLint directive (js/jslint/malformed-directive)
|
||||||
|
* Use of HTML comments (js/html-comment)
|
||||||
|
* Multi-line string literal (js/multi-line-string)
|
||||||
|
* Octal literal (js/octal-literal)
|
||||||
|
* Reserved word used as variable name (js/use-of-reserved-word)
|
||||||
|
* Trailing comma in array or object expressions (js/trailing-comma-in-array-or-object)
|
||||||
|
* Call to parseInt without radix (js/parseint-without-radix)
|
||||||
|
|||||||
@@ -11,4 +11,12 @@
|
|||||||
|-----------|----------|-------------|
|
|-----------|----------|-------------|
|
||||||
| Clear-text logging of sensitive information (`py/clear-text-logging-sensitive-data`) | security, external/cwe/cwe-312 | Finds instances where sensitive information is logged without encryption or hashing. Results are shown on LGTM by default. |
|
| Clear-text logging of sensitive information (`py/clear-text-logging-sensitive-data`) | security, external/cwe/cwe-312 | Finds instances where sensitive information is logged without encryption or hashing. Results are shown on LGTM by default. |
|
||||||
| Clear-text storage of sensitive information (`py/clear-text-storage-sensitive-data`) | security, external/cwe/cwe-312 | Finds instances where sensitive information is stored without encryption or hashing. Results are shown on LGTM by default. |
|
| Clear-text storage of sensitive information (`py/clear-text-storage-sensitive-data`) | security, external/cwe/cwe-312 | Finds instances where sensitive information is stored without encryption or hashing. Results are shown on LGTM by default. |
|
||||||
|
| Binding a socket to all network interfaces (`py/bind-socket-all-network-interfaces`) | security | Finds instances where a socket is bound to all network interfaces. Results are shown on LGTM by default. |
|
||||||
|
|
||||||
|
|
||||||
|
## Changes to existing queries
|
||||||
|
|
||||||
|
| **Query** | **Expected impact** | **Change** |
|
||||||
|
|----------------------------|------------------------|------------|
|
||||||
|
| Unreachable code | Fewer false positives | Analysis now accounts for uses of `contextlib.suppress` to suppress exceptions. |
|
||||||
|
|
||||||
|
|||||||
@@ -8,3 +8,5 @@
|
|||||||
* Recognition of CommonJS modules has improved. As a result, some files that were previously extracted as
|
* Recognition of CommonJS modules has improved. As a result, some files that were previously extracted as
|
||||||
global scripts are now extracted as modules.
|
global scripts are now extracted as modules.
|
||||||
* Top-level `await` is now supported.
|
* Top-level `await` is now supported.
|
||||||
|
* A bug was fixed in how the TypeScript extractor handles default-exported anonymous classes.
|
||||||
|
* A bug was fixed in how the TypeScript extractor handles computed instance field names.
|
||||||
|
|||||||
@@ -3,4 +3,4 @@
|
|||||||
Now that all of the QL documentation is in this repository,
|
Now that all of the QL documentation is in this repository,
|
||||||
notes on the languages, compilers, and frameworks supported have moved.
|
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:
|
They're now stored as part of the Sphinx ``support`` project with the other documentation:
|
||||||
``docs/ql-documentation/support``.
|
``docs/language/support``.
|
||||||
|
|||||||
@@ -47,31 +47,36 @@
|
|||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll",
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll",
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll",
|
||||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll"
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll",
|
||||||
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll"
|
||||||
],
|
],
|
||||||
"IR IRBlock": [
|
"IR IRBlock": [
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll",
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll",
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll",
|
||||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRBlock.qll"
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRBlock.qll",
|
||||||
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRBlock.qll"
|
||||||
],
|
],
|
||||||
"IR IRVariable": [
|
"IR IRVariable": [
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll",
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll",
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll",
|
||||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll"
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll",
|
||||||
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll"
|
||||||
],
|
],
|
||||||
"IR IRFunction": [
|
"IR IRFunction": [
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll",
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll",
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll",
|
||||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRFunction.qll"
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRFunction.qll",
|
||||||
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRFunction.qll"
|
||||||
],
|
],
|
||||||
"IR Operand": [
|
"IR Operand": [
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll",
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll",
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll",
|
||||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Operand.qll"
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Operand.qll",
|
||||||
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Operand.qll"
|
||||||
],
|
],
|
||||||
"IR Operand Tag": [
|
"IR Operand Tag": [
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
|
||||||
@@ -85,19 +90,22 @@
|
|||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll",
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll",
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll",
|
||||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IR.qll"
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IR.qll",
|
||||||
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IR.qll"
|
||||||
],
|
],
|
||||||
"IR IRSanity": [
|
"IR IRSanity": [
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll",
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll",
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll",
|
||||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRSanity.qll"
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRSanity.qll",
|
||||||
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRSanity.qll"
|
||||||
],
|
],
|
||||||
"IR PrintIR": [
|
"IR PrintIR": [
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll",
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll",
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll",
|
||||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/PrintIR.qll"
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/PrintIR.qll",
|
||||||
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/PrintIR.qll"
|
||||||
],
|
],
|
||||||
"IR IntegerConstant": [
|
"IR IntegerConstant": [
|
||||||
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll",
|
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll",
|
||||||
@@ -205,21 +213,27 @@
|
|||||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
|
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
|
||||||
],
|
],
|
||||||
"C# IR InstructionImports": [
|
"C# IR InstructionImports": [
|
||||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionImports.qll"
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionImports.qll",
|
||||||
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll"
|
||||||
],
|
],
|
||||||
"C# IR IRImports": [
|
"C# IR IRImports": [
|
||||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRImports.qll"
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRImports.qll",
|
||||||
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRImports.qll"
|
||||||
],
|
],
|
||||||
"C# IR IRBlockImports": [
|
"C# IR IRBlockImports": [
|
||||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRBlockImports.qll"
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRBlockImports.qll",
|
||||||
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll"
|
||||||
],
|
],
|
||||||
"C# IR IRVariableImports": [
|
"C# IR IRVariableImports": [
|
||||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRVariableImports.qll"
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRVariableImports.qll",
|
||||||
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll"
|
||||||
],
|
],
|
||||||
"C# IR OperandImports": [
|
"C# IR OperandImports": [
|
||||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/OperandImports.qll"
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/OperandImports.qll",
|
||||||
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/OperandImports.qll"
|
||||||
],
|
],
|
||||||
"C# IR PrintIRImports": [
|
"C# IR PrintIRImports": [
|
||||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/PrintIRImports.qll"
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/PrintIRImports.qll",
|
||||||
|
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,16 @@ import cpp
|
|||||||
|
|
||||||
from RelationalOperation e, BinaryBitwiseOperation lhs
|
from RelationalOperation e, BinaryBitwiseOperation lhs
|
||||||
where
|
where
|
||||||
lhs = e.getGreaterOperand() and
|
// `lhs > 0` (or `0 < lhs`)
|
||||||
lhs.getActualType().(IntegralType).isSigned() and
|
// (note that `lhs < 0`, `lhs >= 0` or `lhs <= 0` all imply that the signedness of
|
||||||
forall(int op | op = lhs.(BitwiseAndExpr).getAnOperand().getValue().toInt() | op < 0) and
|
// `lhs` is understood, so should not be flagged).
|
||||||
|
(e instanceof GTExpr or e instanceof LTExpr) and
|
||||||
|
e.getGreaterOperand() = lhs and
|
||||||
e.getLesserOperand().getValue() = "0" and
|
e.getLesserOperand().getValue() = "0" and
|
||||||
|
// lhs is signed
|
||||||
|
lhs.getActualType().(IntegralType).isSigned() and
|
||||||
|
// if `lhs` has the form `x & c`, with constant `c`, `c` is negative
|
||||||
|
forall(int op | op = lhs.(BitwiseAndExpr).getAnOperand().getValue().toInt() | op < 0) and
|
||||||
|
// exception for cases involving macros
|
||||||
not e.isAffectedByMacro()
|
not e.isAffectedByMacro()
|
||||||
select e, "Potential unsafe sign check of a bitwise operation."
|
select e, "Potential unsafe sign check of a bitwise operation."
|
||||||
|
|||||||
@@ -14,5 +14,8 @@
|
|||||||
import cpp
|
import cpp
|
||||||
|
|
||||||
from ComparisonOperation co, ComparisonOperation chco
|
from ComparisonOperation co, ComparisonOperation chco
|
||||||
where co.getAChild() = chco and not chco.isParenthesised()
|
where
|
||||||
|
co.getAChild() = chco and
|
||||||
|
not chco.isParenthesised() and
|
||||||
|
not co.isFromUninstantiatedTemplate(_)
|
||||||
select co, "Check the comparison operator precedence."
|
select co, "Check the comparison operator precedence."
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ private predicate additionalLogicalCheck(Expr e, string operation, int valueToCh
|
|||||||
/**
|
/**
|
||||||
* An `Operation` that seems to be checking for leap year.
|
* An `Operation` that seems to be checking for leap year.
|
||||||
*/
|
*/
|
||||||
class CheckForLeapYearOperation extends Operation {
|
class CheckForLeapYearOperation extends Expr {
|
||||||
CheckForLeapYearOperation() {
|
CheckForLeapYearOperation() {
|
||||||
exists(BinaryArithmeticOperation bo | bo = this |
|
exists(BinaryArithmeticOperation bo | bo = this |
|
||||||
bo.getAnOperand().getValue().toInt() = 4 and
|
bo.getAnOperand().getValue().toInt() = 4 and
|
||||||
@@ -39,8 +39,6 @@ class CheckForLeapYearOperation extends Operation {
|
|||||||
additionalLogicalCheck(this.getEnclosingElement(), "%", 400)
|
additionalLogicalCheck(this.getEnclosingElement(), "%", 400)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override string getOperator() { result = "LeapYearCheck" }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<!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>
|
||||||
|
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
/**
|
||||||
|
* @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
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
<overview>
|
||||||
|
<p>Using boost::asio library but specifying a deprecated hardcoded protocol.</p>
|
||||||
|
<p>Using a deprecated hardcoded protocol instead of negotiting would lock your application to a protocol that has known vulnerabilities or weaknesses.</p>
|
||||||
|
</overview>
|
||||||
|
|
||||||
|
<references>
|
||||||
|
<li>
|
||||||
|
<a href="https://www.boost.org/doc/libs/1_71_0/doc/html/boost_asio.html">Boost.Asio documentation</a>.
|
||||||
|
</li>
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* @name boost::asio Use of deprecated hardcoded Protocol
|
||||||
|
* @description Using a deprecated hard-coded protocol using the boost::asio library.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity error
|
||||||
|
* @id cpp/boost/use-of-deprecated-hardcoded-security-protocol
|
||||||
|
* @tags security
|
||||||
|
*/
|
||||||
|
|
||||||
|
import cpp
|
||||||
|
import semmle.code.cpp.security.boostorg.asio.protocols
|
||||||
|
|
||||||
|
from
|
||||||
|
BoostorgAsio::SslContextCallConfig config, Expr protocolSource, Expr protocolSink,
|
||||||
|
ConstructorCall cc
|
||||||
|
where
|
||||||
|
config.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink)) and
|
||||||
|
not exists(BoostorgAsio::SslContextCallTlsProtocolConfig tlsConfig |
|
||||||
|
tlsConfig.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink))
|
||||||
|
) and
|
||||||
|
cc.getArgument(0) = protocolSink and
|
||||||
|
exists(BoostorgAsio::SslContextCallBannedProtocolConfig bannedConfig |
|
||||||
|
bannedConfig.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink))
|
||||||
|
)
|
||||||
|
select protocolSink, "Usage of $@ specifying a deprecated hardcoded protocol $@ in function $@.",
|
||||||
|
cc, "boost::asio::ssl::context::context", protocolSource, protocolSource.toString(),
|
||||||
|
cc.getEnclosingFunction(), cc.getEnclosingFunction().toString()
|
||||||
14
cpp/ql/src/codeql-suites/cpp-lgtm-full.qls
Normal file
14
cpp/ql/src/codeql-suites/cpp-lgtm-full.qls
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
- description: Standard LGTM queries for C/C++, including ones not displayed by default
|
||||||
|
- qlpack: codeql-cpp
|
||||||
|
- apply: lgtm-selectors.yml
|
||||||
|
from: codeql-suite-helpers
|
||||||
|
# These queries are infeasible to compute on large projects:
|
||||||
|
- exclude:
|
||||||
|
query path:
|
||||||
|
- Security/CWE/CWE-497/ExposedSystemData.ql
|
||||||
|
- Critical/DescriptorMayNotBeClosed.ql
|
||||||
|
- Critical/DescriptorNeverClosed.ql
|
||||||
|
- Critical/FileMayNotBeClosed.ql
|
||||||
|
- Critical/FileNeverClosed.ql
|
||||||
|
- Critical/MemoryMayNotBeFreed.ql
|
||||||
|
- Critical/MemoryNeverFreed.ql
|
||||||
4
cpp/ql/src/codeql-suites/cpp-lgtm.qls
Normal file
4
cpp/ql/src/codeql-suites/cpp-lgtm.qls
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
- description: Standard LGTM queries for C/C++
|
||||||
|
- apply: codeql-suites/cpp-lgtm-full.qls
|
||||||
|
- apply: lgtm-displayed-only.yml
|
||||||
|
from: codeql-suite-helpers
|
||||||
@@ -51,7 +51,7 @@ class ReferenceCopyAssignmentOperator extends MemberFunction {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A call to a function called swap. Note: could be a member,
|
* A call to a function called swap. Note: could be a member,
|
||||||
* std::swap or a function overloading std::swap (not in std::)
|
* `std::swap` or a function overloading `std::swap` (not in `std::`)
|
||||||
* so keep it simple
|
* so keep it simple
|
||||||
*/
|
*/
|
||||||
FunctionCall getASwapCall() {
|
FunctionCall getASwapCall() {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* See More Effective C++ item 7.
|
* See More Effective C++ item 7.
|
||||||
* Note: Meyers allows unary & to be overloaded but not comma
|
* Note: Meyers allows unary `&` to be overloaded but not comma.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import cpp
|
import cpp
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ import cpp
|
|||||||
/*
|
/*
|
||||||
* Interpretation and deviations:
|
* Interpretation and deviations:
|
||||||
* - if the higher operator has precedence > arithmetic then it is fine
|
* - if the higher operator has precedence > arithmetic then it is fine
|
||||||
* RATIONALE: exprs like f(), *x, &x are easily understood to bind tightly
|
* RATIONALE: exprs like `f()`, `*x`, `&x` are easily understood to bind tightly
|
||||||
* - if the higher operator is the RHS of an assign then it is fine
|
* - if the higher operator is the RHS of an assign then it is fine
|
||||||
* RATIONALE: cf. MISRA, too many cases excluded otherwise
|
* RATIONALE: cf. MISRA, too many cases excluded otherwise
|
||||||
* - comparison operators can be mixed with arithmetic
|
* - comparison operators can be mixed with arithmetic
|
||||||
* RATIONALE: x==y+z is common and unambiguous
|
* RATIONALE: `x==y+z` is common and unambiguous
|
||||||
*/
|
*/
|
||||||
|
|
||||||
predicate arithmeticPrecedence(int p) { p = 12 or p = 13 }
|
predicate arithmeticPrecedence(int p) { p = 12 or p = 13 }
|
||||||
|
|||||||
4
cpp/ql/src/qlpack.yml
Normal file
4
cpp/ql/src/qlpack.yml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
name: codeql-cpp
|
||||||
|
version: 0.0.0
|
||||||
|
dbscheme: semmlecode.cpp.dbscheme
|
||||||
|
suites: codeql-suites
|
||||||
@@ -14,8 +14,12 @@ private import semmle.code.cpp.internal.QualifiedName as Q
|
|||||||
* ```
|
* ```
|
||||||
* extern int myglobal;
|
* extern int myglobal;
|
||||||
* ```
|
* ```
|
||||||
* Each of these declarations is given its own distinct `DeclarationEntry`,
|
* and defined in one:
|
||||||
* but they all share the same `Declaration`.
|
* ```
|
||||||
|
* int myglobal;
|
||||||
|
* ```
|
||||||
|
* Each of these declarations (including the definition) is given its own
|
||||||
|
* distinct `DeclarationEntry`, but they all share the same `Declaration`.
|
||||||
*
|
*
|
||||||
* Some derived class of `Declaration` do not have a corresponding
|
* Some derived class of `Declaration` do not have a corresponding
|
||||||
* `DeclarationEntry`, because they always have a unique source location.
|
* `DeclarationEntry`, because they always have a unique source location.
|
||||||
@@ -206,9 +210,19 @@ abstract class Declaration extends Locatable, @declaration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A C/C++ declaration entry. See the comment above `Declaration` for an
|
* A C/C++ declaration entry. For example the following code contains five
|
||||||
* explanation of the relationship between `Declaration` and
|
* declaration entries:
|
||||||
* `DeclarationEntry`.
|
* ```
|
||||||
|
* extern int myGlobal;
|
||||||
|
* int myVariable;
|
||||||
|
* typedef char MyChar;
|
||||||
|
* void myFunction();
|
||||||
|
* void myFunction() {
|
||||||
|
* // ...
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
* See the comment above `Declaration` for an explanation of the relationship
|
||||||
|
* between `Declaration` and `DeclarationEntry`.
|
||||||
*/
|
*/
|
||||||
abstract class DeclarationEntry extends Locatable {
|
abstract class DeclarationEntry extends Locatable {
|
||||||
/** Gets a specifier associated with this declaration entry. */
|
/** Gets a specifier associated with this declaration entry. */
|
||||||
@@ -281,8 +295,19 @@ abstract class DeclarationEntry extends Locatable {
|
|||||||
* A declaration that can potentially have more C++ access rights than its
|
* A declaration that can potentially have more C++ access rights than its
|
||||||
* enclosing element. This comprises `Class` (they have access to their own
|
* enclosing element. This comprises `Class` (they have access to their own
|
||||||
* private members) along with other `UserType`s and `Function` (they can be
|
* private members) along with other `UserType`s and `Function` (they can be
|
||||||
* the target of `friend` declarations).
|
* the target of `friend` declarations). For example `MyClass` and
|
||||||
|
* `myFunction` in the following code:
|
||||||
|
* ```
|
||||||
|
* class MyClass
|
||||||
|
* {
|
||||||
|
* public:
|
||||||
|
* ...
|
||||||
|
* };
|
||||||
*
|
*
|
||||||
|
* void myFunction() {
|
||||||
|
* // ...
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
* In the C++ standard (N4140 11.2), rules for access control revolve around
|
* In the C++ standard (N4140 11.2), rules for access control revolve around
|
||||||
* the informal phrase "_R_ occurs in a member or friend of class C", where
|
* the informal phrase "_R_ occurs in a member or friend of class C", where
|
||||||
* `AccessHolder` corresponds to this _R_.
|
* `AccessHolder` corresponds to this _R_.
|
||||||
@@ -416,8 +441,19 @@ abstract class AccessHolder extends Declaration {
|
|||||||
/**
|
/**
|
||||||
* A declaration that very likely has more C++ access rights than its
|
* A declaration that very likely has more C++ access rights than its
|
||||||
* enclosing element. This comprises `Class` (they have access to their own
|
* enclosing element. This comprises `Class` (they have access to their own
|
||||||
* private members) along with any target of a `friend` declaration.
|
* private members) along with any target of a `friend` declaration. For
|
||||||
|
* example `MyClass` and `friendFunction` in the following code:
|
||||||
|
* ```
|
||||||
|
* class MyClass
|
||||||
|
* {
|
||||||
|
* public:
|
||||||
|
* friend void friendFunction();
|
||||||
|
* };
|
||||||
*
|
*
|
||||||
|
* void friendFunction() {
|
||||||
|
* // ...
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
* Most access rights are computed for `DirectAccessHolder` instead of
|
* Most access rights are computed for `DirectAccessHolder` instead of
|
||||||
* `AccessHolder` -- that's more efficient because there are fewer
|
* `AccessHolder` -- that's more efficient because there are fewer
|
||||||
* `DirectAccessHolder`s. If a `DirectAccessHolder` contains an `AccessHolder`,
|
* `DirectAccessHolder`s. If a `DirectAccessHolder` contains an `AccessHolder`,
|
||||||
|
|||||||
@@ -158,3 +158,10 @@ class Parameter extends LocalScopeVariable, @parameter {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An `int` that is a parameter index for some function. This is needed for binding in certain cases.
|
||||||
|
*/
|
||||||
|
class ParameterIndex extends int {
|
||||||
|
ParameterIndex() { exists(Parameter p | this = p.getIndex()) }
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,11 +3,17 @@ private import semmle.code.cpp.internal.ResolveClass
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A C/C++ typedef type. See 4.9.1.
|
* A C/C++ typedef type. See 4.9.1.
|
||||||
|
*
|
||||||
|
* Represents either of the following typedef styles:
|
||||||
|
*
|
||||||
|
* * CTypedefType: typedef <type> <name>;
|
||||||
|
* * UsingAliasTypedefType: using <name> = <type>;
|
||||||
*/
|
*/
|
||||||
class TypedefType extends UserType {
|
class TypedefType extends UserType {
|
||||||
TypedefType() { usertypes(underlyingElement(this), _, 5) }
|
TypedefType() {
|
||||||
|
usertypes(underlyingElement(this), _, 5) or
|
||||||
override string getCanonicalQLClass() { result = "TypedefType" }
|
usertypes(underlyingElement(this), _, 14)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the base type of this typedef type.
|
* Gets the base type of this typedef type.
|
||||||
@@ -26,10 +32,6 @@ class TypedefType extends UserType {
|
|||||||
result = this.getBaseType().getPointerIndirectionLevel()
|
result = this.getBaseType().getPointerIndirectionLevel()
|
||||||
}
|
}
|
||||||
|
|
||||||
override string explain() {
|
|
||||||
result = "typedef {" + this.getBaseType().explain() + "} as \"" + this.getName() + "\""
|
|
||||||
}
|
|
||||||
|
|
||||||
override predicate isDeeplyConst() { this.getBaseType().isDeeplyConst() } // Just an alias
|
override predicate isDeeplyConst() { this.getBaseType().isDeeplyConst() } // Just an alias
|
||||||
|
|
||||||
override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConstBelow() } // Just an alias
|
override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConstBelow() } // Just an alias
|
||||||
@@ -45,6 +47,32 @@ class TypedefType extends UserType {
|
|||||||
override Type stripType() { result = getBaseType().stripType() }
|
override Type stripType() { result = getBaseType().stripType() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A traditional C/C++ typedef type. See 4.9.1.
|
||||||
|
*/
|
||||||
|
class CTypedefType extends TypedefType {
|
||||||
|
CTypedefType() { usertypes(underlyingElement(this), _, 5) }
|
||||||
|
|
||||||
|
override string getCanonicalQLClass() { result = "CTypedefType" }
|
||||||
|
|
||||||
|
override string explain() {
|
||||||
|
result = "typedef {" + this.getBaseType().explain() + "} as \"" + this.getName() + "\""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A using alias C++ typedef type.
|
||||||
|
*/
|
||||||
|
class UsingAliasTypedefType extends TypedefType {
|
||||||
|
UsingAliasTypedefType() { usertypes(underlyingElement(this), _, 14) }
|
||||||
|
|
||||||
|
override string getCanonicalQLClass() { result = "UsingAliasTypedefType" }
|
||||||
|
|
||||||
|
override string explain() {
|
||||||
|
result = "using {" + this.getBaseType().explain() + "} as \"" + this.getName() + "\""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A C++ typedef type that is directly enclosed by a function.
|
* A C++ typedef type that is directly enclosed by a function.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -169,7 +169,11 @@ class FormattingFunctionCall extends Expr {
|
|||||||
* Gets the number of arguments to this call that are parameters to the
|
* Gets the number of arguments to this call that are parameters to the
|
||||||
* format string.
|
* format string.
|
||||||
*/
|
*/
|
||||||
int getNumFormatArgument() { result = count(this.getFormatArgument(_)) }
|
int getNumFormatArgument() {
|
||||||
|
result = count(this.getFormatArgument(_)) and
|
||||||
|
// format arguments must be known
|
||||||
|
exists(getTarget().(FormattingFunction).getFirstFormatArgumentIndex())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import cpp
|
import cpp
|
||||||
import BasicBlocks
|
import BasicBlocks
|
||||||
private import semmle.code.cpp.controlflow.internal.ConstantExprs
|
private import semmle.code.cpp.controlflow.internal.ConstantExprs
|
||||||
|
private import semmle.code.cpp.controlflow.internal.CFG
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A control-flow node is either a statement or an expression; in addition,
|
* A control-flow node is either a statement or an expression; in addition,
|
||||||
@@ -86,11 +87,11 @@ import ControlFlowGraphPublic
|
|||||||
class ControlFlowNodeBase extends ElementBase, @cfgnode { }
|
class ControlFlowNodeBase extends ElementBase, @cfgnode { }
|
||||||
|
|
||||||
predicate truecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
|
predicate truecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
|
||||||
truecond(unresolveElement(n1), unresolveElement(n2))
|
qlCFGTrueSuccessor(n1, n2)
|
||||||
}
|
}
|
||||||
|
|
||||||
predicate falsecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
|
predicate falsecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
|
||||||
falsecond(unresolveElement(n1), unresolveElement(n2))
|
qlCFGFalseSuccessor(n1, n2)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -120,7 +121,7 @@ abstract class AdditionalControlFlowEdge extends ControlFlowNodeBase {
|
|||||||
* `AdditionalControlFlowEdge`. Use this relation instead of `successors`.
|
* `AdditionalControlFlowEdge`. Use this relation instead of `successors`.
|
||||||
*/
|
*/
|
||||||
predicate successors_extended(ControlFlowNodeBase source, ControlFlowNodeBase target) {
|
predicate successors_extended(ControlFlowNodeBase source, ControlFlowNodeBase target) {
|
||||||
successors(unresolveElement(source), unresolveElement(target))
|
qlCFGSuccessor(source, target)
|
||||||
or
|
or
|
||||||
source.(AdditionalControlFlowEdge).getAnEdgeTarget() = target
|
source.(AdditionalControlFlowEdge).getAnEdgeTarget() = target
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -362,7 +362,7 @@ private predicate add_lt(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The int value of integer constant expression. */
|
/** The `int` value of integer constant expression. */
|
||||||
private int int_value(Expr e) {
|
private int int_value(Expr e) {
|
||||||
e.getUnderlyingType() instanceof IntegralType and
|
e.getUnderlyingType() instanceof IntegralType and
|
||||||
result = e.getValue().toInt()
|
result = e.getValue().toInt()
|
||||||
|
|||||||
@@ -29,11 +29,11 @@ class SsaDefinition extends ControlFlowNodeBase {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a string representation of the SSA variable represented by the pair
|
* Gets a string representation of the SSA variable represented by the pair
|
||||||
* (this, v).
|
* `(this, v)`.
|
||||||
*/
|
*/
|
||||||
string toString(LocalScopeVariable v) { exists(StandardSSA x | result = x.toString(this, v)) }
|
string toString(LocalScopeVariable v) { exists(StandardSSA x | result = x.toString(this, v)) }
|
||||||
|
|
||||||
/** Gets a use of the SSA variable represented by the pair (this, v). */
|
/** Gets a use of the SSA variable represented by the pair `(this, v)`. */
|
||||||
VariableAccess getAUse(LocalScopeVariable v) {
|
VariableAccess getAUse(LocalScopeVariable v) {
|
||||||
exists(StandardSSA x | result = x.getAUse(this, v))
|
exists(StandardSSA x | result = x.getAUse(this, v))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,49 +173,36 @@ predicate excludeNode(Node n) {
|
|||||||
excludeNode(n.getParentNode())
|
excludeNode(n.getParentNode())
|
||||||
}
|
}
|
||||||
|
|
||||||
private newtype TPos =
|
|
||||||
PosBefore() or
|
|
||||||
PosAt() or
|
|
||||||
PosAfter() or
|
|
||||||
PosBeforeDestructors() or
|
|
||||||
PosAfterDestructors()
|
|
||||||
|
|
||||||
/** A `Pos` without a `bindingset` requirement on the constructor. */
|
|
||||||
private class AnyPos extends TPos {
|
|
||||||
string toString() { result = "Pos" }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A constant that indicates the type of sub-node in a pair of `(Node, Pos)`.
|
* A constant that indicates the type of sub-node in a pair of `(Node, Pos)`.
|
||||||
* See the comment block at the top of this file.
|
* See the comment block at the top of this file.
|
||||||
*/
|
*/
|
||||||
private class Pos extends AnyPos {
|
private class Pos extends int {
|
||||||
// This is to make sure we get compile errors in code that forgets to restrict a `Pos`.
|
|
||||||
bindingset[this]
|
bindingset[this]
|
||||||
Pos() { any() }
|
Pos() { any() }
|
||||||
|
|
||||||
/** Holds if this is the position just _before_ the associated `Node`. */
|
/** Holds if this is the position just _before_ the associated `Node`. */
|
||||||
predicate isBefore() { this = PosBefore() }
|
predicate isBefore() { this = 0 }
|
||||||
|
|
||||||
/** Holds if `(n, this)` is the sub-node that represents `n` itself. */
|
/** Holds if `(n, this)` is the sub-node that represents `n` itself. */
|
||||||
predicate isAt() { this = PosAt() }
|
predicate isAt() { this = 1 }
|
||||||
|
|
||||||
/** Holds if this is the position just _after_ the associated `Node`. */
|
/** Holds if this is the position just _after_ the associated `Node`. */
|
||||||
predicate isAfter() { this = PosAfter() }
|
predicate isAfter() { this = 2 }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `(n, this)` is the virtual sub-node that comes just _before_ any
|
* Holds if `(n, this)` is the virtual sub-node that comes just _before_ any
|
||||||
* implicit destructor calls following `n`. The node `n` will be some node
|
* implicit destructor calls following `n`. The node `n` will be some node
|
||||||
* that may be followed by local variables going out of scope.
|
* that may be followed by local variables going out of scope.
|
||||||
*/
|
*/
|
||||||
predicate isBeforeDestructors() { this = PosBeforeDestructors() }
|
predicate isBeforeDestructors() { this = 3 }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `(n, this)` is the virtual sub-node that comes just _after_ any
|
* Holds if `(n, this)` is the virtual sub-node that comes just _after_ any
|
||||||
* implicit destructor calls following `n`. The node `n` will be some node
|
* implicit destructor calls following `n`. The node `n` will be some node
|
||||||
* that may be followed by local variables going out of scope.
|
* that may be followed by local variables going out of scope.
|
||||||
*/
|
*/
|
||||||
predicate isAfterDestructors() { this = PosAfterDestructors() }
|
predicate isAfterDestructors() { this = 4 }
|
||||||
|
|
||||||
pragma[inline]
|
pragma[inline]
|
||||||
predicate nodeBefore(Node n, Node nEq) { this.isBefore() and n = nEq }
|
predicate nodeBefore(Node n, Node nEq) { this.isBefore() and n = nEq }
|
||||||
@@ -489,17 +476,6 @@ private Node getLastControlOrderChild(Node n) {
|
|||||||
result = getControlOrderChildDense(n, max(int i | exists(getControlOrderChildDense(n, i))))
|
result = getControlOrderChildDense(n, max(int i | exists(getControlOrderChildDense(n, i))))
|
||||||
}
|
}
|
||||||
|
|
||||||
private newtype TSpec =
|
|
||||||
SpecPos(AnyPos p) or
|
|
||||||
SpecAround() or
|
|
||||||
SpecAroundDestructors() or
|
|
||||||
SpecBarrier()
|
|
||||||
|
|
||||||
/** A `Spec` without a `bindingset` requirement on the constructor. */
|
|
||||||
private class AnySpec extends TSpec {
|
|
||||||
string toString() { result = "Spec" }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A constant that represents two positions: one position for when it's used as
|
* A constant that represents two positions: one position for when it's used as
|
||||||
* the _source_ of a sub-edge, and another position for when it's used as the
|
* the _source_ of a sub-edge, and another position for when it's used as the
|
||||||
@@ -507,25 +483,10 @@ private class AnySpec extends TSpec {
|
|||||||
* themselves as both source and target, as well as two _around_ values and a
|
* themselves as both source and target, as well as two _around_ values and a
|
||||||
* _barrier_ value.
|
* _barrier_ value.
|
||||||
*/
|
*/
|
||||||
private class Spec extends AnySpec {
|
private class Spec extends Pos {
|
||||||
bindingset[this]
|
bindingset[this]
|
||||||
Spec() { any() }
|
Spec() { any() }
|
||||||
|
|
||||||
/** See Pos.isBefore. */
|
|
||||||
predicate isBefore() { this = SpecPos(PosBefore()) }
|
|
||||||
|
|
||||||
/** See Pos.isAt. */
|
|
||||||
predicate isAt() { this = SpecPos(PosAt()) }
|
|
||||||
|
|
||||||
/** See Pos.isAfter. */
|
|
||||||
predicate isAfter() { this = SpecPos(PosAfter()) }
|
|
||||||
|
|
||||||
/** See Pos.isBeforeDestructors. */
|
|
||||||
predicate isBeforeDestructors() { this = SpecPos(PosBeforeDestructors()) }
|
|
||||||
|
|
||||||
/** See Pos.isAfterDestructors. */
|
|
||||||
predicate isAfterDestructors() { this = SpecPos(PosAfterDestructors()) }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this spec, when used on a node `n` between `(n1, p1)` and
|
* Holds if this spec, when used on a node `n` between `(n1, p1)` and
|
||||||
* `(n2, p2)`, should add the following sub-edges.
|
* `(n2, p2)`, should add the following sub-edges.
|
||||||
@@ -533,7 +494,7 @@ private class Spec extends AnySpec {
|
|||||||
* (n1, p1) ----> before(n)
|
* (n1, p1) ----> before(n)
|
||||||
* after(n) ----> (n2, p2)
|
* after(n) ----> (n2, p2)
|
||||||
*/
|
*/
|
||||||
predicate isAround() { this = SpecAround() }
|
predicate isAround() { this = 5 }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this spec, when used on a node `n` between `(n1, p1)` and
|
* Holds if this spec, when used on a node `n` between `(n1, p1)` and
|
||||||
@@ -542,16 +503,17 @@ private class Spec extends AnySpec {
|
|||||||
* (n1, p1) ----> beforeDestructors(n)
|
* (n1, p1) ----> beforeDestructors(n)
|
||||||
* afterDestructors(n) ----> (n2, p2)
|
* afterDestructors(n) ----> (n2, p2)
|
||||||
*/
|
*/
|
||||||
predicate isAroundDestructors() { this = SpecAroundDestructors() }
|
predicate isAroundDestructors() { this = 6 }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this node is a _barrier_. A barrier resolves to no positions and
|
* Holds if this node is a _barrier_. A barrier resolves to no positions and
|
||||||
* can be inserted between nodes that should have no sub-edges between them.
|
* can be inserted between nodes that should have no sub-edges between them.
|
||||||
*/
|
*/
|
||||||
predicate isBarrier() { this = SpecBarrier() }
|
predicate isBarrier() { this = 7 }
|
||||||
|
|
||||||
Pos getSourcePos() {
|
Pos getSourcePos() {
|
||||||
this = SpecPos(result)
|
this = [0 .. 4] and
|
||||||
|
result = this
|
||||||
or
|
or
|
||||||
this.isAround() and
|
this.isAround() and
|
||||||
result.isAfter()
|
result.isAfter()
|
||||||
@@ -561,7 +523,8 @@ private class Spec extends AnySpec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Pos getTargetPos() {
|
Pos getTargetPos() {
|
||||||
this = SpecPos(result)
|
this = [0 .. 4] and
|
||||||
|
result = this
|
||||||
or
|
or
|
||||||
this.isAround() and
|
this.isAround() and
|
||||||
result.isBefore()
|
result.isBefore()
|
||||||
@@ -888,6 +851,21 @@ private predicate subEdge(Pos p1, Node n1, Node n2, Pos p2) {
|
|||||||
p2.nodeAfter(n2, s)
|
p2.nodeAfter(n2, s)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
|
// ConstexprIfStmt -> condition ; { then, else } -> // same as IfStmt
|
||||||
|
exists(ConstexprIfStmt s |
|
||||||
|
p1.nodeAt(n1, s) and
|
||||||
|
p2.nodeBefore(n2, s.getCondition())
|
||||||
|
or
|
||||||
|
p1.nodeAfter(n1, s.getThen()) and
|
||||||
|
p2.nodeBeforeDestructors(n2, s)
|
||||||
|
or
|
||||||
|
p1.nodeAfter(n1, s.getElse()) and
|
||||||
|
p2.nodeBeforeDestructors(n2, s)
|
||||||
|
or
|
||||||
|
p1.nodeAfterDestructors(n1, s) and
|
||||||
|
p2.nodeAfter(n2, s)
|
||||||
|
)
|
||||||
|
or
|
||||||
// WhileStmt -> condition ; body -> condition ; after dtors -> after
|
// WhileStmt -> condition ; body -> condition ; after dtors -> after
|
||||||
exists(WhileStmt s |
|
exists(WhileStmt s |
|
||||||
p1.nodeAt(n1, s) and
|
p1.nodeAt(n1, s) and
|
||||||
@@ -1175,9 +1153,8 @@ private class ExceptionSource extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `test` is the test of a control-flow construct that will always
|
* Holds if `test` is the test of a control-flow construct where the `truth`
|
||||||
* have true/false sub-edges out of it, where the `truth`-sub-edge goes to
|
* sub-edge goes to `(n2, p2)`.
|
||||||
* `(n2, p2)`.
|
|
||||||
*/
|
*/
|
||||||
private predicate conditionJumpsTop(Expr test, boolean truth, Node n2, Pos p2) {
|
private predicate conditionJumpsTop(Expr test, boolean truth, Node n2, Pos p2) {
|
||||||
exists(IfStmt s | test = s.getCondition() |
|
exists(IfStmt s | test = s.getCondition() |
|
||||||
@@ -1192,6 +1169,24 @@ private predicate conditionJumpsTop(Expr test, boolean truth, Node n2, Pos p2) {
|
|||||||
p2.nodeBeforeDestructors(n2, s)
|
p2.nodeBeforeDestructors(n2, s)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
|
exists(ConstexprIfStmt s, string cond |
|
||||||
|
test = s.getCondition() and
|
||||||
|
cond = test.getFullyConverted().getValue()
|
||||||
|
|
|
||||||
|
truth = true and
|
||||||
|
cond != "0" and
|
||||||
|
p2.nodeBefore(n2, s.getThen())
|
||||||
|
or
|
||||||
|
truth = false and
|
||||||
|
cond = "0" and
|
||||||
|
p2.nodeBefore(n2, s.getElse())
|
||||||
|
or
|
||||||
|
not exists(s.getElse()) and
|
||||||
|
truth = false and
|
||||||
|
cond = "0" and
|
||||||
|
p2.nodeBeforeDestructors(n2, s)
|
||||||
|
)
|
||||||
|
or
|
||||||
exists(Loop l |
|
exists(Loop l |
|
||||||
(
|
(
|
||||||
l instanceof WhileStmt
|
l instanceof WhileStmt
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* on each other without introducing mutual recursion among those configurations.
|
* on each other without introducing mutual recursion among those configurations.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private import DataFlowImplCommon
|
private import DataFlowImplCommon::Public
|
||||||
private import DataFlowImplSpecific::Private
|
private import DataFlowImplSpecific::Private
|
||||||
import DataFlowImplSpecific::Public
|
import DataFlowImplSpecific::Public
|
||||||
|
|
||||||
@@ -905,8 +905,10 @@ private predicate localFlowExit(Node node, Configuration config) {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate localFlowStepPlus(
|
private predicate localFlowStepPlus(
|
||||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||||
) {
|
) {
|
||||||
|
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||||
|
(
|
||||||
localFlowEntry(node1, config) and
|
localFlowEntry(node1, config) and
|
||||||
(
|
(
|
||||||
localFlowStep(node1, node2, config) and preservesValue = true
|
localFlowStep(node1, node2, config) and preservesValue = true
|
||||||
@@ -914,33 +916,36 @@ private predicate localFlowStepPlus(
|
|||||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||||
) and
|
) and
|
||||||
node1 != node2 and
|
node1 != node2 and
|
||||||
|
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||||
|
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||||
localFlowStep(mid, node2, config) and
|
localFlowStep(mid, node2, config) and
|
||||||
not mid instanceof CastNode and
|
not mid instanceof CastNode and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowStepPlus(node1, mid, _, config) and
|
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||||
additionalLocalFlowStep(mid, node2, config) and
|
additionalLocalFlowStep(mid, node2, config) and
|
||||||
not mid instanceof CastNode and
|
not mid instanceof CastNode and
|
||||||
preservesValue = false and
|
preservesValue = false and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
)
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node1` can step to `node2` in one or more local steps and this
|
* Holds if `node1` can step to `node2` in one or more local steps and this
|
||||||
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
||||||
*/
|
*/
|
||||||
pragma[noinline]
|
pragma[nomagic]
|
||||||
private predicate localFlowBigStep(
|
private predicate localFlowBigStep(
|
||||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
|
||||||
) {
|
) {
|
||||||
localFlowStepPlus(node1, node2, preservesValue, config) and
|
localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
|
||||||
localFlowExit(node2, config)
|
localFlowExit(node2, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
|||||||
(
|
(
|
||||||
any(Configuration c).isSource(this)
|
any(Configuration c).isSource(this)
|
||||||
or
|
or
|
||||||
localFlowBigStep(_, this, false, _)
|
localFlowBigStep(_, this, false, _, _)
|
||||||
or
|
or
|
||||||
additionalJumpStep(_, this, _)
|
additionalJumpStep(_, this, _)
|
||||||
)
|
)
|
||||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
|||||||
(
|
(
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
flowCandFwd(mid, fromArg, apf, config) and
|
flowCandFwd(mid, fromArg, apf, config) and
|
||||||
localFlowBigStep(mid, node, true, config)
|
localFlowBigStep(mid, node, true, config, _)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathFrontNil nil |
|
exists(Node mid, AccessPathFrontNil nil |
|
||||||
flowCandFwd(mid, fromArg, nil, config) and
|
flowCandFwd(mid, fromArg, nil, config) and
|
||||||
localFlowBigStep(mid, node, false, config) and
|
localFlowBigStep(mid, node, false, config, _) and
|
||||||
apf = node.(AccessPathFrontNilNode).getApf()
|
apf = node.(AccessPathFrontNilNode).getApf()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
|||||||
flowCandFwd(mid, fromArg, _, config) and
|
flowCandFwd(mid, fromArg, _, config) and
|
||||||
store(mid, f, node) and
|
store(mid, f, node) and
|
||||||
nodeCand(node, unbind(config)) and
|
nodeCand(node, unbind(config)) and
|
||||||
|
readStoreCand(f, unbind(config)) and
|
||||||
apf.headUsesContent(f)
|
apf.headUsesContent(f)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
|||||||
apf instanceof AccessPathFrontNil
|
apf instanceof AccessPathFrontNil
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowBigStep(node, mid, true, config) and
|
localFlowBigStep(node, mid, true, config, _) and
|
||||||
flowCand(mid, toReturn, apf, config)
|
flowCand(mid, toReturn, apf, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathFrontNil nil |
|
exists(Node mid, AccessPathFrontNil nil |
|
||||||
flowCandFwd(node, _, apf, config) and
|
flowCandFwd(node, _, apf, config) and
|
||||||
localFlowBigStep(node, mid, false, config) and
|
localFlowBigStep(node, mid, false, config, _) and
|
||||||
flowCand(mid, toReturn, nil, config) and
|
flowCand(mid, toReturn, nil, config) and
|
||||||
apf instanceof AccessPathFrontNil
|
apf instanceof AccessPathFrontNil
|
||||||
)
|
)
|
||||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
|||||||
exists(Content f, AccessPathFront apf0 |
|
exists(Content f, AccessPathFront apf0 |
|
||||||
flowCandStore(node, f, toReturn, apf0, config) and
|
flowCandStore(node, f, toReturn, apf0, config) and
|
||||||
apf0.headUsesContent(f) and
|
apf0.headUsesContent(f) and
|
||||||
consCand(f, apf, unbind(config))
|
consCand(f, apf, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Content f, AccessPathFront apf0 |
|
exists(Content f, AccessPathFront apf0 |
|
||||||
flowCandRead(node, f, toReturn, apf0, config) and
|
flowCandRead(node, f, toReturn, apf0, config) and
|
||||||
consCandFwd(f, apf0, unbind(config)) and
|
consCandFwd(f, apf0, config) and
|
||||||
apf.headUsesContent(f)
|
apf.headUsesContent(f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1221,8 +1227,8 @@ private newtype TAccessPath =
|
|||||||
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||||
* element of the list and its length are tracked. If data flows from a source to
|
* elements of the list and its length are tracked. If data flows from a source to
|
||||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||||
* dereference operations needed to get from the value in the node to the
|
* dereference operations needed to get from the value in the node to the
|
||||||
* tracked object. The final type indicates the type of the tracked object.
|
* tracked object. The final type indicates the type of the tracked object.
|
||||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
|||||||
|
|
||||||
private class AccessPathNil extends AccessPath, TNil {
|
private class AccessPathNil extends AccessPath, TNil {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
|
exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override AccessPathFront getFront() {
|
override AccessPathFront getFront() {
|
||||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||||
// The `concat` becomes "" if `ppReprType` has no result.
|
// The `concat` becomes "" if `ppReprType` has no result.
|
||||||
result = f.toString() + concat(" : " + ppReprType(t))
|
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1293,8 +1299,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||||
if len = 2
|
if len = 2
|
||||||
then result = f1.toString() + ", " + f2.toString()
|
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
|||||||
(
|
(
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
flowFwd(mid, fromArg, apf, ap, config) and
|
flowFwd(mid, fromArg, apf, ap, config) and
|
||||||
localFlowBigStep(mid, node, true, config)
|
localFlowBigStep(mid, node, true, config, _)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathNil nil |
|
exists(Node mid, AccessPathNil nil |
|
||||||
flowFwd(mid, fromArg, _, nil, config) and
|
flowFwd(mid, fromArg, _, nil, config) and
|
||||||
localFlowBigStep(mid, node, false, config) and
|
localFlowBigStep(mid, node, false, config, _) and
|
||||||
ap = node.(AccessPathNilNode).getAp() and
|
ap = node.(AccessPathNilNode).getAp() and
|
||||||
apf = ap.(AccessPathNil).getFront()
|
apf = ap.(AccessPathNil).getFront()
|
||||||
)
|
)
|
||||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
|||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowBigStep(node, mid, true, config) and
|
localFlowBigStep(node, mid, true, config, _) and
|
||||||
flow(mid, toReturn, ap, config)
|
flow(mid, toReturn, ap, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathNil nil |
|
exists(Node mid, AccessPathNil nil |
|
||||||
flowFwd(node, _, _, ap, config) and
|
flowFwd(node, _, _, ap, config) and
|
||||||
localFlowBigStep(node, mid, false, config) and
|
localFlowBigStep(node, mid, false, config, _) and
|
||||||
flow(mid, toReturn, nil, config) and
|
flow(mid, toReturn, nil, config) and
|
||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
)
|
)
|
||||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
|||||||
this instanceof PathNodeSink and result = ""
|
this instanceof PathNodeSink and result = ""
|
||||||
or
|
or
|
||||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||||
if s = "" then result = "" else result = " [" + s + "]"
|
if s = "" then result = "" else result = " " + s
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1728,14 +1734,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
|||||||
* a callable is recorded by `cc`.
|
* a callable is recorded by `cc`.
|
||||||
*/
|
*/
|
||||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||||
|
midnode = mid.getNode() and
|
||||||
|
conf = mid.getConfiguration() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
ap = mid.getAp()
|
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||||
|
ap0 = mid.getAp()
|
||||||
|
|
|
||||||
|
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||||
|
ap = ap0
|
||||||
or
|
or
|
||||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||||
cc = mid.getCallContext() and
|
ap0 instanceof AccessPathNil and
|
||||||
mid.getAp() instanceof AccessPathNil and
|
|
||||||
ap = node.(AccessPathNilNode).getAp()
|
ap = node.(AccessPathNilNode).getAp()
|
||||||
|
)
|
||||||
or
|
or
|
||||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||||
cc instanceof CallContextAny and
|
cc instanceof CallContextAny and
|
||||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
|||||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||||
p.isParameterOf(callable, i)
|
p.isParameterOf(callable, i)
|
||||||
|
|
|
|
||||||
if reducedViableImplInCallContext(_, callable, call)
|
if recordDataFlowCallSite(call, callable)
|
||||||
then innercc = TSpecificCall(call, i, emptyAp)
|
then innercc = TSpecificCall(call, i, emptyAp)
|
||||||
else innercc = TSomeCall(p, emptyAp)
|
else innercc = TSomeCall(p, emptyAp)
|
||||||
)
|
)
|
||||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
|||||||
|
|
||||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
|
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override AccessPathFront getFront() {
|
override AccessPathFront getFront() {
|
||||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||||
if len = 1
|
if len = 1
|
||||||
then result = f.toString()
|
then result = "[" + f.toString() + "]"
|
||||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
|||||||
|
|
||||||
private string ppAp() {
|
private string ppAp() {
|
||||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||||
if s = "" then result = "" else result = " [" + s + "]"
|
if s = "" then result = "" else result = " " + s
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2204,6 +2216,8 @@ private module FlowExploration {
|
|||||||
private predicate partialPathStep(
|
private predicate partialPathStep(
|
||||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
|
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||||
|
(
|
||||||
localFlowStep(mid.getNode(), node, config) and
|
localFlowStep(mid.getNode(), node, config) and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
@@ -2214,6 +2228,7 @@ private module FlowExploration {
|
|||||||
mid.getAp() instanceof PartialAccessPathNil and
|
mid.getAp() instanceof PartialAccessPathNil and
|
||||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||||
config = mid.getConfiguration()
|
config = mid.getConfiguration()
|
||||||
|
)
|
||||||
or
|
or
|
||||||
jumpStep(mid.getNode(), node, config) and
|
jumpStep(mid.getNode(), node, config) and
|
||||||
cc instanceof CallContextAny and
|
cc instanceof CallContextAny and
|
||||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
|||||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||||
p.isParameterOf(callable, i)
|
p.isParameterOf(callable, i)
|
||||||
|
|
|
|
||||||
if reducedViableImplInCallContext(_, callable, call)
|
if recordDataFlowCallSite(call, callable)
|
||||||
then innercc = TSpecificCall(call, i, emptyAp)
|
then innercc = TSpecificCall(call, i, emptyAp)
|
||||||
else innercc = TSomeCall(p, emptyAp)
|
else innercc = TSomeCall(p, emptyAp)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* on each other without introducing mutual recursion among those configurations.
|
* on each other without introducing mutual recursion among those configurations.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private import DataFlowImplCommon
|
private import DataFlowImplCommon::Public
|
||||||
private import DataFlowImplSpecific::Private
|
private import DataFlowImplSpecific::Private
|
||||||
import DataFlowImplSpecific::Public
|
import DataFlowImplSpecific::Public
|
||||||
|
|
||||||
@@ -905,8 +905,10 @@ private predicate localFlowExit(Node node, Configuration config) {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate localFlowStepPlus(
|
private predicate localFlowStepPlus(
|
||||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||||
) {
|
) {
|
||||||
|
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||||
|
(
|
||||||
localFlowEntry(node1, config) and
|
localFlowEntry(node1, config) and
|
||||||
(
|
(
|
||||||
localFlowStep(node1, node2, config) and preservesValue = true
|
localFlowStep(node1, node2, config) and preservesValue = true
|
||||||
@@ -914,33 +916,36 @@ private predicate localFlowStepPlus(
|
|||||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||||
) and
|
) and
|
||||||
node1 != node2 and
|
node1 != node2 and
|
||||||
|
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||||
|
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||||
localFlowStep(mid, node2, config) and
|
localFlowStep(mid, node2, config) and
|
||||||
not mid instanceof CastNode and
|
not mid instanceof CastNode and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowStepPlus(node1, mid, _, config) and
|
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||||
additionalLocalFlowStep(mid, node2, config) and
|
additionalLocalFlowStep(mid, node2, config) and
|
||||||
not mid instanceof CastNode and
|
not mid instanceof CastNode and
|
||||||
preservesValue = false and
|
preservesValue = false and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
)
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node1` can step to `node2` in one or more local steps and this
|
* Holds if `node1` can step to `node2` in one or more local steps and this
|
||||||
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
||||||
*/
|
*/
|
||||||
pragma[noinline]
|
pragma[nomagic]
|
||||||
private predicate localFlowBigStep(
|
private predicate localFlowBigStep(
|
||||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
|
||||||
) {
|
) {
|
||||||
localFlowStepPlus(node1, node2, preservesValue, config) and
|
localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
|
||||||
localFlowExit(node2, config)
|
localFlowExit(node2, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
|||||||
(
|
(
|
||||||
any(Configuration c).isSource(this)
|
any(Configuration c).isSource(this)
|
||||||
or
|
or
|
||||||
localFlowBigStep(_, this, false, _)
|
localFlowBigStep(_, this, false, _, _)
|
||||||
or
|
or
|
||||||
additionalJumpStep(_, this, _)
|
additionalJumpStep(_, this, _)
|
||||||
)
|
)
|
||||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
|||||||
(
|
(
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
flowCandFwd(mid, fromArg, apf, config) and
|
flowCandFwd(mid, fromArg, apf, config) and
|
||||||
localFlowBigStep(mid, node, true, config)
|
localFlowBigStep(mid, node, true, config, _)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathFrontNil nil |
|
exists(Node mid, AccessPathFrontNil nil |
|
||||||
flowCandFwd(mid, fromArg, nil, config) and
|
flowCandFwd(mid, fromArg, nil, config) and
|
||||||
localFlowBigStep(mid, node, false, config) and
|
localFlowBigStep(mid, node, false, config, _) and
|
||||||
apf = node.(AccessPathFrontNilNode).getApf()
|
apf = node.(AccessPathFrontNilNode).getApf()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
|||||||
flowCandFwd(mid, fromArg, _, config) and
|
flowCandFwd(mid, fromArg, _, config) and
|
||||||
store(mid, f, node) and
|
store(mid, f, node) and
|
||||||
nodeCand(node, unbind(config)) and
|
nodeCand(node, unbind(config)) and
|
||||||
|
readStoreCand(f, unbind(config)) and
|
||||||
apf.headUsesContent(f)
|
apf.headUsesContent(f)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
|||||||
apf instanceof AccessPathFrontNil
|
apf instanceof AccessPathFrontNil
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowBigStep(node, mid, true, config) and
|
localFlowBigStep(node, mid, true, config, _) and
|
||||||
flowCand(mid, toReturn, apf, config)
|
flowCand(mid, toReturn, apf, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathFrontNil nil |
|
exists(Node mid, AccessPathFrontNil nil |
|
||||||
flowCandFwd(node, _, apf, config) and
|
flowCandFwd(node, _, apf, config) and
|
||||||
localFlowBigStep(node, mid, false, config) and
|
localFlowBigStep(node, mid, false, config, _) and
|
||||||
flowCand(mid, toReturn, nil, config) and
|
flowCand(mid, toReturn, nil, config) and
|
||||||
apf instanceof AccessPathFrontNil
|
apf instanceof AccessPathFrontNil
|
||||||
)
|
)
|
||||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
|||||||
exists(Content f, AccessPathFront apf0 |
|
exists(Content f, AccessPathFront apf0 |
|
||||||
flowCandStore(node, f, toReturn, apf0, config) and
|
flowCandStore(node, f, toReturn, apf0, config) and
|
||||||
apf0.headUsesContent(f) and
|
apf0.headUsesContent(f) and
|
||||||
consCand(f, apf, unbind(config))
|
consCand(f, apf, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Content f, AccessPathFront apf0 |
|
exists(Content f, AccessPathFront apf0 |
|
||||||
flowCandRead(node, f, toReturn, apf0, config) and
|
flowCandRead(node, f, toReturn, apf0, config) and
|
||||||
consCandFwd(f, apf0, unbind(config)) and
|
consCandFwd(f, apf0, config) and
|
||||||
apf.headUsesContent(f)
|
apf.headUsesContent(f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1221,8 +1227,8 @@ private newtype TAccessPath =
|
|||||||
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||||
* element of the list and its length are tracked. If data flows from a source to
|
* elements of the list and its length are tracked. If data flows from a source to
|
||||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||||
* dereference operations needed to get from the value in the node to the
|
* dereference operations needed to get from the value in the node to the
|
||||||
* tracked object. The final type indicates the type of the tracked object.
|
* tracked object. The final type indicates the type of the tracked object.
|
||||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
|||||||
|
|
||||||
private class AccessPathNil extends AccessPath, TNil {
|
private class AccessPathNil extends AccessPath, TNil {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
|
exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override AccessPathFront getFront() {
|
override AccessPathFront getFront() {
|
||||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||||
// The `concat` becomes "" if `ppReprType` has no result.
|
// The `concat` becomes "" if `ppReprType` has no result.
|
||||||
result = f.toString() + concat(" : " + ppReprType(t))
|
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1293,8 +1299,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||||
if len = 2
|
if len = 2
|
||||||
then result = f1.toString() + ", " + f2.toString()
|
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
|||||||
(
|
(
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
flowFwd(mid, fromArg, apf, ap, config) and
|
flowFwd(mid, fromArg, apf, ap, config) and
|
||||||
localFlowBigStep(mid, node, true, config)
|
localFlowBigStep(mid, node, true, config, _)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathNil nil |
|
exists(Node mid, AccessPathNil nil |
|
||||||
flowFwd(mid, fromArg, _, nil, config) and
|
flowFwd(mid, fromArg, _, nil, config) and
|
||||||
localFlowBigStep(mid, node, false, config) and
|
localFlowBigStep(mid, node, false, config, _) and
|
||||||
ap = node.(AccessPathNilNode).getAp() and
|
ap = node.(AccessPathNilNode).getAp() and
|
||||||
apf = ap.(AccessPathNil).getFront()
|
apf = ap.(AccessPathNil).getFront()
|
||||||
)
|
)
|
||||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
|||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowBigStep(node, mid, true, config) and
|
localFlowBigStep(node, mid, true, config, _) and
|
||||||
flow(mid, toReturn, ap, config)
|
flow(mid, toReturn, ap, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathNil nil |
|
exists(Node mid, AccessPathNil nil |
|
||||||
flowFwd(node, _, _, ap, config) and
|
flowFwd(node, _, _, ap, config) and
|
||||||
localFlowBigStep(node, mid, false, config) and
|
localFlowBigStep(node, mid, false, config, _) and
|
||||||
flow(mid, toReturn, nil, config) and
|
flow(mid, toReturn, nil, config) and
|
||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
)
|
)
|
||||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
|||||||
this instanceof PathNodeSink and result = ""
|
this instanceof PathNodeSink and result = ""
|
||||||
or
|
or
|
||||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||||
if s = "" then result = "" else result = " [" + s + "]"
|
if s = "" then result = "" else result = " " + s
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1728,14 +1734,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
|||||||
* a callable is recorded by `cc`.
|
* a callable is recorded by `cc`.
|
||||||
*/
|
*/
|
||||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||||
|
midnode = mid.getNode() and
|
||||||
|
conf = mid.getConfiguration() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
ap = mid.getAp()
|
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||||
|
ap0 = mid.getAp()
|
||||||
|
|
|
||||||
|
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||||
|
ap = ap0
|
||||||
or
|
or
|
||||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||||
cc = mid.getCallContext() and
|
ap0 instanceof AccessPathNil and
|
||||||
mid.getAp() instanceof AccessPathNil and
|
|
||||||
ap = node.(AccessPathNilNode).getAp()
|
ap = node.(AccessPathNilNode).getAp()
|
||||||
|
)
|
||||||
or
|
or
|
||||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||||
cc instanceof CallContextAny and
|
cc instanceof CallContextAny and
|
||||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
|||||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||||
p.isParameterOf(callable, i)
|
p.isParameterOf(callable, i)
|
||||||
|
|
|
|
||||||
if reducedViableImplInCallContext(_, callable, call)
|
if recordDataFlowCallSite(call, callable)
|
||||||
then innercc = TSpecificCall(call, i, emptyAp)
|
then innercc = TSpecificCall(call, i, emptyAp)
|
||||||
else innercc = TSomeCall(p, emptyAp)
|
else innercc = TSomeCall(p, emptyAp)
|
||||||
)
|
)
|
||||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
|||||||
|
|
||||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
|
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override AccessPathFront getFront() {
|
override AccessPathFront getFront() {
|
||||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||||
if len = 1
|
if len = 1
|
||||||
then result = f.toString()
|
then result = "[" + f.toString() + "]"
|
||||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
|||||||
|
|
||||||
private string ppAp() {
|
private string ppAp() {
|
||||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||||
if s = "" then result = "" else result = " [" + s + "]"
|
if s = "" then result = "" else result = " " + s
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2204,6 +2216,8 @@ private module FlowExploration {
|
|||||||
private predicate partialPathStep(
|
private predicate partialPathStep(
|
||||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
|
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||||
|
(
|
||||||
localFlowStep(mid.getNode(), node, config) and
|
localFlowStep(mid.getNode(), node, config) and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
@@ -2214,6 +2228,7 @@ private module FlowExploration {
|
|||||||
mid.getAp() instanceof PartialAccessPathNil and
|
mid.getAp() instanceof PartialAccessPathNil and
|
||||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||||
config = mid.getConfiguration()
|
config = mid.getConfiguration()
|
||||||
|
)
|
||||||
or
|
or
|
||||||
jumpStep(mid.getNode(), node, config) and
|
jumpStep(mid.getNode(), node, config) and
|
||||||
cc instanceof CallContextAny and
|
cc instanceof CallContextAny and
|
||||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
|||||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||||
p.isParameterOf(callable, i)
|
p.isParameterOf(callable, i)
|
||||||
|
|
|
|
||||||
if reducedViableImplInCallContext(_, callable, call)
|
if recordDataFlowCallSite(call, callable)
|
||||||
then innercc = TSpecificCall(call, i, emptyAp)
|
then innercc = TSpecificCall(call, i, emptyAp)
|
||||||
else innercc = TSomeCall(p, emptyAp)
|
else innercc = TSomeCall(p, emptyAp)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* on each other without introducing mutual recursion among those configurations.
|
* on each other without introducing mutual recursion among those configurations.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private import DataFlowImplCommon
|
private import DataFlowImplCommon::Public
|
||||||
private import DataFlowImplSpecific::Private
|
private import DataFlowImplSpecific::Private
|
||||||
import DataFlowImplSpecific::Public
|
import DataFlowImplSpecific::Public
|
||||||
|
|
||||||
@@ -905,8 +905,10 @@ private predicate localFlowExit(Node node, Configuration config) {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate localFlowStepPlus(
|
private predicate localFlowStepPlus(
|
||||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||||
) {
|
) {
|
||||||
|
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||||
|
(
|
||||||
localFlowEntry(node1, config) and
|
localFlowEntry(node1, config) and
|
||||||
(
|
(
|
||||||
localFlowStep(node1, node2, config) and preservesValue = true
|
localFlowStep(node1, node2, config) and preservesValue = true
|
||||||
@@ -914,33 +916,36 @@ private predicate localFlowStepPlus(
|
|||||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||||
) and
|
) and
|
||||||
node1 != node2 and
|
node1 != node2 and
|
||||||
|
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||||
|
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||||
localFlowStep(mid, node2, config) and
|
localFlowStep(mid, node2, config) and
|
||||||
not mid instanceof CastNode and
|
not mid instanceof CastNode and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowStepPlus(node1, mid, _, config) and
|
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||||
additionalLocalFlowStep(mid, node2, config) and
|
additionalLocalFlowStep(mid, node2, config) and
|
||||||
not mid instanceof CastNode and
|
not mid instanceof CastNode and
|
||||||
preservesValue = false and
|
preservesValue = false and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
)
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node1` can step to `node2` in one or more local steps and this
|
* Holds if `node1` can step to `node2` in one or more local steps and this
|
||||||
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
||||||
*/
|
*/
|
||||||
pragma[noinline]
|
pragma[nomagic]
|
||||||
private predicate localFlowBigStep(
|
private predicate localFlowBigStep(
|
||||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
|
||||||
) {
|
) {
|
||||||
localFlowStepPlus(node1, node2, preservesValue, config) and
|
localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
|
||||||
localFlowExit(node2, config)
|
localFlowExit(node2, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
|||||||
(
|
(
|
||||||
any(Configuration c).isSource(this)
|
any(Configuration c).isSource(this)
|
||||||
or
|
or
|
||||||
localFlowBigStep(_, this, false, _)
|
localFlowBigStep(_, this, false, _, _)
|
||||||
or
|
or
|
||||||
additionalJumpStep(_, this, _)
|
additionalJumpStep(_, this, _)
|
||||||
)
|
)
|
||||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
|||||||
(
|
(
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
flowCandFwd(mid, fromArg, apf, config) and
|
flowCandFwd(mid, fromArg, apf, config) and
|
||||||
localFlowBigStep(mid, node, true, config)
|
localFlowBigStep(mid, node, true, config, _)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathFrontNil nil |
|
exists(Node mid, AccessPathFrontNil nil |
|
||||||
flowCandFwd(mid, fromArg, nil, config) and
|
flowCandFwd(mid, fromArg, nil, config) and
|
||||||
localFlowBigStep(mid, node, false, config) and
|
localFlowBigStep(mid, node, false, config, _) and
|
||||||
apf = node.(AccessPathFrontNilNode).getApf()
|
apf = node.(AccessPathFrontNilNode).getApf()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
|||||||
flowCandFwd(mid, fromArg, _, config) and
|
flowCandFwd(mid, fromArg, _, config) and
|
||||||
store(mid, f, node) and
|
store(mid, f, node) and
|
||||||
nodeCand(node, unbind(config)) and
|
nodeCand(node, unbind(config)) and
|
||||||
|
readStoreCand(f, unbind(config)) and
|
||||||
apf.headUsesContent(f)
|
apf.headUsesContent(f)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
|||||||
apf instanceof AccessPathFrontNil
|
apf instanceof AccessPathFrontNil
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowBigStep(node, mid, true, config) and
|
localFlowBigStep(node, mid, true, config, _) and
|
||||||
flowCand(mid, toReturn, apf, config)
|
flowCand(mid, toReturn, apf, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathFrontNil nil |
|
exists(Node mid, AccessPathFrontNil nil |
|
||||||
flowCandFwd(node, _, apf, config) and
|
flowCandFwd(node, _, apf, config) and
|
||||||
localFlowBigStep(node, mid, false, config) and
|
localFlowBigStep(node, mid, false, config, _) and
|
||||||
flowCand(mid, toReturn, nil, config) and
|
flowCand(mid, toReturn, nil, config) and
|
||||||
apf instanceof AccessPathFrontNil
|
apf instanceof AccessPathFrontNil
|
||||||
)
|
)
|
||||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
|||||||
exists(Content f, AccessPathFront apf0 |
|
exists(Content f, AccessPathFront apf0 |
|
||||||
flowCandStore(node, f, toReturn, apf0, config) and
|
flowCandStore(node, f, toReturn, apf0, config) and
|
||||||
apf0.headUsesContent(f) and
|
apf0.headUsesContent(f) and
|
||||||
consCand(f, apf, unbind(config))
|
consCand(f, apf, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Content f, AccessPathFront apf0 |
|
exists(Content f, AccessPathFront apf0 |
|
||||||
flowCandRead(node, f, toReturn, apf0, config) and
|
flowCandRead(node, f, toReturn, apf0, config) and
|
||||||
consCandFwd(f, apf0, unbind(config)) and
|
consCandFwd(f, apf0, config) and
|
||||||
apf.headUsesContent(f)
|
apf.headUsesContent(f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1221,8 +1227,8 @@ private newtype TAccessPath =
|
|||||||
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||||
* element of the list and its length are tracked. If data flows from a source to
|
* elements of the list and its length are tracked. If data flows from a source to
|
||||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||||
* dereference operations needed to get from the value in the node to the
|
* dereference operations needed to get from the value in the node to the
|
||||||
* tracked object. The final type indicates the type of the tracked object.
|
* tracked object. The final type indicates the type of the tracked object.
|
||||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
|||||||
|
|
||||||
private class AccessPathNil extends AccessPath, TNil {
|
private class AccessPathNil extends AccessPath, TNil {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
|
exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override AccessPathFront getFront() {
|
override AccessPathFront getFront() {
|
||||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||||
// The `concat` becomes "" if `ppReprType` has no result.
|
// The `concat` becomes "" if `ppReprType` has no result.
|
||||||
result = f.toString() + concat(" : " + ppReprType(t))
|
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1293,8 +1299,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||||
if len = 2
|
if len = 2
|
||||||
then result = f1.toString() + ", " + f2.toString()
|
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
|||||||
(
|
(
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
flowFwd(mid, fromArg, apf, ap, config) and
|
flowFwd(mid, fromArg, apf, ap, config) and
|
||||||
localFlowBigStep(mid, node, true, config)
|
localFlowBigStep(mid, node, true, config, _)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathNil nil |
|
exists(Node mid, AccessPathNil nil |
|
||||||
flowFwd(mid, fromArg, _, nil, config) and
|
flowFwd(mid, fromArg, _, nil, config) and
|
||||||
localFlowBigStep(mid, node, false, config) and
|
localFlowBigStep(mid, node, false, config, _) and
|
||||||
ap = node.(AccessPathNilNode).getAp() and
|
ap = node.(AccessPathNilNode).getAp() and
|
||||||
apf = ap.(AccessPathNil).getFront()
|
apf = ap.(AccessPathNil).getFront()
|
||||||
)
|
)
|
||||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
|||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowBigStep(node, mid, true, config) and
|
localFlowBigStep(node, mid, true, config, _) and
|
||||||
flow(mid, toReturn, ap, config)
|
flow(mid, toReturn, ap, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathNil nil |
|
exists(Node mid, AccessPathNil nil |
|
||||||
flowFwd(node, _, _, ap, config) and
|
flowFwd(node, _, _, ap, config) and
|
||||||
localFlowBigStep(node, mid, false, config) and
|
localFlowBigStep(node, mid, false, config, _) and
|
||||||
flow(mid, toReturn, nil, config) and
|
flow(mid, toReturn, nil, config) and
|
||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
)
|
)
|
||||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
|||||||
this instanceof PathNodeSink and result = ""
|
this instanceof PathNodeSink and result = ""
|
||||||
or
|
or
|
||||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||||
if s = "" then result = "" else result = " [" + s + "]"
|
if s = "" then result = "" else result = " " + s
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1728,14 +1734,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
|||||||
* a callable is recorded by `cc`.
|
* a callable is recorded by `cc`.
|
||||||
*/
|
*/
|
||||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||||
|
midnode = mid.getNode() and
|
||||||
|
conf = mid.getConfiguration() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
ap = mid.getAp()
|
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||||
|
ap0 = mid.getAp()
|
||||||
|
|
|
||||||
|
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||||
|
ap = ap0
|
||||||
or
|
or
|
||||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||||
cc = mid.getCallContext() and
|
ap0 instanceof AccessPathNil and
|
||||||
mid.getAp() instanceof AccessPathNil and
|
|
||||||
ap = node.(AccessPathNilNode).getAp()
|
ap = node.(AccessPathNilNode).getAp()
|
||||||
|
)
|
||||||
or
|
or
|
||||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||||
cc instanceof CallContextAny and
|
cc instanceof CallContextAny and
|
||||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
|||||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||||
p.isParameterOf(callable, i)
|
p.isParameterOf(callable, i)
|
||||||
|
|
|
|
||||||
if reducedViableImplInCallContext(_, callable, call)
|
if recordDataFlowCallSite(call, callable)
|
||||||
then innercc = TSpecificCall(call, i, emptyAp)
|
then innercc = TSpecificCall(call, i, emptyAp)
|
||||||
else innercc = TSomeCall(p, emptyAp)
|
else innercc = TSomeCall(p, emptyAp)
|
||||||
)
|
)
|
||||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
|||||||
|
|
||||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
|
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override AccessPathFront getFront() {
|
override AccessPathFront getFront() {
|
||||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||||
if len = 1
|
if len = 1
|
||||||
then result = f.toString()
|
then result = "[" + f.toString() + "]"
|
||||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
|||||||
|
|
||||||
private string ppAp() {
|
private string ppAp() {
|
||||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||||
if s = "" then result = "" else result = " [" + s + "]"
|
if s = "" then result = "" else result = " " + s
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2204,6 +2216,8 @@ private module FlowExploration {
|
|||||||
private predicate partialPathStep(
|
private predicate partialPathStep(
|
||||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
|
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||||
|
(
|
||||||
localFlowStep(mid.getNode(), node, config) and
|
localFlowStep(mid.getNode(), node, config) and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
@@ -2214,6 +2228,7 @@ private module FlowExploration {
|
|||||||
mid.getAp() instanceof PartialAccessPathNil and
|
mid.getAp() instanceof PartialAccessPathNil and
|
||||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||||
config = mid.getConfiguration()
|
config = mid.getConfiguration()
|
||||||
|
)
|
||||||
or
|
or
|
||||||
jumpStep(mid.getNode(), node, config) and
|
jumpStep(mid.getNode(), node, config) and
|
||||||
cc instanceof CallContextAny and
|
cc instanceof CallContextAny and
|
||||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
|||||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||||
p.isParameterOf(callable, i)
|
p.isParameterOf(callable, i)
|
||||||
|
|
|
|
||||||
if reducedViableImplInCallContext(_, callable, call)
|
if recordDataFlowCallSite(call, callable)
|
||||||
then innercc = TSpecificCall(call, i, emptyAp)
|
then innercc = TSpecificCall(call, i, emptyAp)
|
||||||
else innercc = TSomeCall(p, emptyAp)
|
else innercc = TSomeCall(p, emptyAp)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* on each other without introducing mutual recursion among those configurations.
|
* on each other without introducing mutual recursion among those configurations.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private import DataFlowImplCommon
|
private import DataFlowImplCommon::Public
|
||||||
private import DataFlowImplSpecific::Private
|
private import DataFlowImplSpecific::Private
|
||||||
import DataFlowImplSpecific::Public
|
import DataFlowImplSpecific::Public
|
||||||
|
|
||||||
@@ -905,8 +905,10 @@ private predicate localFlowExit(Node node, Configuration config) {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate localFlowStepPlus(
|
private predicate localFlowStepPlus(
|
||||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||||
) {
|
) {
|
||||||
|
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||||
|
(
|
||||||
localFlowEntry(node1, config) and
|
localFlowEntry(node1, config) and
|
||||||
(
|
(
|
||||||
localFlowStep(node1, node2, config) and preservesValue = true
|
localFlowStep(node1, node2, config) and preservesValue = true
|
||||||
@@ -914,33 +916,36 @@ private predicate localFlowStepPlus(
|
|||||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||||
) and
|
) and
|
||||||
node1 != node2 and
|
node1 != node2 and
|
||||||
|
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||||
|
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||||
localFlowStep(mid, node2, config) and
|
localFlowStep(mid, node2, config) and
|
||||||
not mid instanceof CastNode and
|
not mid instanceof CastNode and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowStepPlus(node1, mid, _, config) and
|
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||||
additionalLocalFlowStep(mid, node2, config) and
|
additionalLocalFlowStep(mid, node2, config) and
|
||||||
not mid instanceof CastNode and
|
not mid instanceof CastNode and
|
||||||
preservesValue = false and
|
preservesValue = false and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
)
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node1` can step to `node2` in one or more local steps and this
|
* Holds if `node1` can step to `node2` in one or more local steps and this
|
||||||
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
||||||
*/
|
*/
|
||||||
pragma[noinline]
|
pragma[nomagic]
|
||||||
private predicate localFlowBigStep(
|
private predicate localFlowBigStep(
|
||||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
|
||||||
) {
|
) {
|
||||||
localFlowStepPlus(node1, node2, preservesValue, config) and
|
localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
|
||||||
localFlowExit(node2, config)
|
localFlowExit(node2, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
|||||||
(
|
(
|
||||||
any(Configuration c).isSource(this)
|
any(Configuration c).isSource(this)
|
||||||
or
|
or
|
||||||
localFlowBigStep(_, this, false, _)
|
localFlowBigStep(_, this, false, _, _)
|
||||||
or
|
or
|
||||||
additionalJumpStep(_, this, _)
|
additionalJumpStep(_, this, _)
|
||||||
)
|
)
|
||||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
|||||||
(
|
(
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
flowCandFwd(mid, fromArg, apf, config) and
|
flowCandFwd(mid, fromArg, apf, config) and
|
||||||
localFlowBigStep(mid, node, true, config)
|
localFlowBigStep(mid, node, true, config, _)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathFrontNil nil |
|
exists(Node mid, AccessPathFrontNil nil |
|
||||||
flowCandFwd(mid, fromArg, nil, config) and
|
flowCandFwd(mid, fromArg, nil, config) and
|
||||||
localFlowBigStep(mid, node, false, config) and
|
localFlowBigStep(mid, node, false, config, _) and
|
||||||
apf = node.(AccessPathFrontNilNode).getApf()
|
apf = node.(AccessPathFrontNilNode).getApf()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
|||||||
flowCandFwd(mid, fromArg, _, config) and
|
flowCandFwd(mid, fromArg, _, config) and
|
||||||
store(mid, f, node) and
|
store(mid, f, node) and
|
||||||
nodeCand(node, unbind(config)) and
|
nodeCand(node, unbind(config)) and
|
||||||
|
readStoreCand(f, unbind(config)) and
|
||||||
apf.headUsesContent(f)
|
apf.headUsesContent(f)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
|||||||
apf instanceof AccessPathFrontNil
|
apf instanceof AccessPathFrontNil
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowBigStep(node, mid, true, config) and
|
localFlowBigStep(node, mid, true, config, _) and
|
||||||
flowCand(mid, toReturn, apf, config)
|
flowCand(mid, toReturn, apf, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathFrontNil nil |
|
exists(Node mid, AccessPathFrontNil nil |
|
||||||
flowCandFwd(node, _, apf, config) and
|
flowCandFwd(node, _, apf, config) and
|
||||||
localFlowBigStep(node, mid, false, config) and
|
localFlowBigStep(node, mid, false, config, _) and
|
||||||
flowCand(mid, toReturn, nil, config) and
|
flowCand(mid, toReturn, nil, config) and
|
||||||
apf instanceof AccessPathFrontNil
|
apf instanceof AccessPathFrontNil
|
||||||
)
|
)
|
||||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
|||||||
exists(Content f, AccessPathFront apf0 |
|
exists(Content f, AccessPathFront apf0 |
|
||||||
flowCandStore(node, f, toReturn, apf0, config) and
|
flowCandStore(node, f, toReturn, apf0, config) and
|
||||||
apf0.headUsesContent(f) and
|
apf0.headUsesContent(f) and
|
||||||
consCand(f, apf, unbind(config))
|
consCand(f, apf, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Content f, AccessPathFront apf0 |
|
exists(Content f, AccessPathFront apf0 |
|
||||||
flowCandRead(node, f, toReturn, apf0, config) and
|
flowCandRead(node, f, toReturn, apf0, config) and
|
||||||
consCandFwd(f, apf0, unbind(config)) and
|
consCandFwd(f, apf0, config) and
|
||||||
apf.headUsesContent(f)
|
apf.headUsesContent(f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1221,8 +1227,8 @@ private newtype TAccessPath =
|
|||||||
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||||
* element of the list and its length are tracked. If data flows from a source to
|
* elements of the list and its length are tracked. If data flows from a source to
|
||||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||||
* dereference operations needed to get from the value in the node to the
|
* dereference operations needed to get from the value in the node to the
|
||||||
* tracked object. The final type indicates the type of the tracked object.
|
* tracked object. The final type indicates the type of the tracked object.
|
||||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
|||||||
|
|
||||||
private class AccessPathNil extends AccessPath, TNil {
|
private class AccessPathNil extends AccessPath, TNil {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
|
exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override AccessPathFront getFront() {
|
override AccessPathFront getFront() {
|
||||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||||
// The `concat` becomes "" if `ppReprType` has no result.
|
// The `concat` becomes "" if `ppReprType` has no result.
|
||||||
result = f.toString() + concat(" : " + ppReprType(t))
|
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1293,8 +1299,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||||
if len = 2
|
if len = 2
|
||||||
then result = f1.toString() + ", " + f2.toString()
|
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
|||||||
(
|
(
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
flowFwd(mid, fromArg, apf, ap, config) and
|
flowFwd(mid, fromArg, apf, ap, config) and
|
||||||
localFlowBigStep(mid, node, true, config)
|
localFlowBigStep(mid, node, true, config, _)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathNil nil |
|
exists(Node mid, AccessPathNil nil |
|
||||||
flowFwd(mid, fromArg, _, nil, config) and
|
flowFwd(mid, fromArg, _, nil, config) and
|
||||||
localFlowBigStep(mid, node, false, config) and
|
localFlowBigStep(mid, node, false, config, _) and
|
||||||
ap = node.(AccessPathNilNode).getAp() and
|
ap = node.(AccessPathNilNode).getAp() and
|
||||||
apf = ap.(AccessPathNil).getFront()
|
apf = ap.(AccessPathNil).getFront()
|
||||||
)
|
)
|
||||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
|||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowBigStep(node, mid, true, config) and
|
localFlowBigStep(node, mid, true, config, _) and
|
||||||
flow(mid, toReturn, ap, config)
|
flow(mid, toReturn, ap, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathNil nil |
|
exists(Node mid, AccessPathNil nil |
|
||||||
flowFwd(node, _, _, ap, config) and
|
flowFwd(node, _, _, ap, config) and
|
||||||
localFlowBigStep(node, mid, false, config) and
|
localFlowBigStep(node, mid, false, config, _) and
|
||||||
flow(mid, toReturn, nil, config) and
|
flow(mid, toReturn, nil, config) and
|
||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
)
|
)
|
||||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
|||||||
this instanceof PathNodeSink and result = ""
|
this instanceof PathNodeSink and result = ""
|
||||||
or
|
or
|
||||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||||
if s = "" then result = "" else result = " [" + s + "]"
|
if s = "" then result = "" else result = " " + s
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1728,14 +1734,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
|||||||
* a callable is recorded by `cc`.
|
* a callable is recorded by `cc`.
|
||||||
*/
|
*/
|
||||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||||
|
midnode = mid.getNode() and
|
||||||
|
conf = mid.getConfiguration() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
ap = mid.getAp()
|
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||||
|
ap0 = mid.getAp()
|
||||||
|
|
|
||||||
|
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||||
|
ap = ap0
|
||||||
or
|
or
|
||||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||||
cc = mid.getCallContext() and
|
ap0 instanceof AccessPathNil and
|
||||||
mid.getAp() instanceof AccessPathNil and
|
|
||||||
ap = node.(AccessPathNilNode).getAp()
|
ap = node.(AccessPathNilNode).getAp()
|
||||||
|
)
|
||||||
or
|
or
|
||||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||||
cc instanceof CallContextAny and
|
cc instanceof CallContextAny and
|
||||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
|||||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||||
p.isParameterOf(callable, i)
|
p.isParameterOf(callable, i)
|
||||||
|
|
|
|
||||||
if reducedViableImplInCallContext(_, callable, call)
|
if recordDataFlowCallSite(call, callable)
|
||||||
then innercc = TSpecificCall(call, i, emptyAp)
|
then innercc = TSpecificCall(call, i, emptyAp)
|
||||||
else innercc = TSomeCall(p, emptyAp)
|
else innercc = TSomeCall(p, emptyAp)
|
||||||
)
|
)
|
||||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
|||||||
|
|
||||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
|
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override AccessPathFront getFront() {
|
override AccessPathFront getFront() {
|
||||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||||
if len = 1
|
if len = 1
|
||||||
then result = f.toString()
|
then result = "[" + f.toString() + "]"
|
||||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
|||||||
|
|
||||||
private string ppAp() {
|
private string ppAp() {
|
||||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||||
if s = "" then result = "" else result = " [" + s + "]"
|
if s = "" then result = "" else result = " " + s
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2204,6 +2216,8 @@ private module FlowExploration {
|
|||||||
private predicate partialPathStep(
|
private predicate partialPathStep(
|
||||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
|
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||||
|
(
|
||||||
localFlowStep(mid.getNode(), node, config) and
|
localFlowStep(mid.getNode(), node, config) and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
@@ -2214,6 +2228,7 @@ private module FlowExploration {
|
|||||||
mid.getAp() instanceof PartialAccessPathNil and
|
mid.getAp() instanceof PartialAccessPathNil and
|
||||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||||
config = mid.getConfiguration()
|
config = mid.getConfiguration()
|
||||||
|
)
|
||||||
or
|
or
|
||||||
jumpStep(mid.getNode(), node, config) and
|
jumpStep(mid.getNode(), node, config) and
|
||||||
cc instanceof CallContextAny and
|
cc instanceof CallContextAny and
|
||||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
|||||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||||
p.isParameterOf(callable, i)
|
p.isParameterOf(callable, i)
|
||||||
|
|
|
|
||||||
if reducedViableImplInCallContext(_, callable, call)
|
if recordDataFlowCallSite(call, callable)
|
||||||
then innercc = TSpecificCall(call, i, emptyAp)
|
then innercc = TSpecificCall(call, i, emptyAp)
|
||||||
else innercc = TSomeCall(p, emptyAp)
|
else innercc = TSomeCall(p, emptyAp)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,8 +3,16 @@ import DataFlowImplSpecific::Public
|
|||||||
|
|
||||||
private ReturnNode getAReturnNodeOfKind(ReturnKind kind) { result.getKind() = kind }
|
private ReturnNode getAReturnNodeOfKind(ReturnKind kind) { result.getKind() = kind }
|
||||||
|
|
||||||
cached
|
module Public {
|
||||||
|
import ImplCommon
|
||||||
|
import FlowThrough_v2
|
||||||
|
}
|
||||||
|
|
||||||
private module ImplCommon {
|
private module ImplCommon {
|
||||||
|
import Cached
|
||||||
|
|
||||||
|
cached
|
||||||
|
private module Cached {
|
||||||
/**
|
/**
|
||||||
* Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
|
* Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
|
||||||
* The instance parameter is considered to have index `-1`.
|
* The instance parameter is considered to have index `-1`.
|
||||||
@@ -26,23 +34,36 @@ private module ImplCommon {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The `FlowThrough_*` modules take a `step` relation as input and provide
|
||||||
|
* an `argumentValueFlowsThrough` relation as output.
|
||||||
|
*
|
||||||
|
* `FlowThrough_v1` includes just `simpleLocalFlowStep`, which is then used
|
||||||
|
* to detect getters and setters.
|
||||||
|
* `FlowThrough_v2` then includes a little bit of local field flow on top
|
||||||
|
* of `simpleLocalFlowStep`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
private module FlowThrough_v1 {
|
||||||
|
private predicate step = simpleLocalFlowStep/2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `p` can flow to `node` in the same callable using only
|
* Holds if `p` can flow to `node` in the same callable using only
|
||||||
* value-preserving steps, not taking call contexts into account.
|
* value-preserving steps, not taking call contexts into account.
|
||||||
*/
|
*/
|
||||||
private predicate parameterValueFlowNoCtx(ParameterNode p, Node node) {
|
private predicate parameterValueFlowCand(ParameterNode p, Node node) {
|
||||||
p = node
|
p = node
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
parameterValueFlowNoCtx(p, mid) and
|
parameterValueFlowCand(p, mid) and
|
||||||
simpleLocalFlowStep(mid, node) and
|
step(mid, node) and
|
||||||
compatibleTypes(p.getType(), node.getType())
|
compatibleTypes(p.getType(), node.getType())
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// flow through a callable
|
// flow through a callable
|
||||||
exists(Node arg |
|
exists(Node arg |
|
||||||
parameterValueFlowNoCtx(p, arg) and
|
parameterValueFlowCand(p, arg) and
|
||||||
argumentValueFlowsThroughNoCtx(arg, node) and
|
argumentValueFlowsThroughCand(arg, node) and
|
||||||
compatibleTypes(p.getType(), node.getType())
|
compatibleTypes(p.getType(), node.getType())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -52,16 +73,16 @@ private module ImplCommon {
|
|||||||
* callable using only value-preserving steps, not taking call contexts
|
* callable using only value-preserving steps, not taking call contexts
|
||||||
* into account.
|
* into account.
|
||||||
*/
|
*/
|
||||||
private predicate parameterValueFlowsThroughNoCtx(ParameterNode p, ReturnKind kind) {
|
private predicate parameterValueFlowsThroughCand(ParameterNode p, ReturnKind kind) {
|
||||||
parameterValueFlowNoCtx(p, getAReturnNodeOfKind(kind))
|
parameterValueFlowCand(p, getAReturnNodeOfKind(kind))
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate argumentValueFlowsThroughNoCtx0(
|
private predicate argumentValueFlowsThroughCand0(
|
||||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind
|
DataFlowCall call, ArgumentNode arg, ReturnKind kind
|
||||||
) {
|
) {
|
||||||
exists(ParameterNode param | viableParamArg(call, param, arg) |
|
exists(ParameterNode param | viableParamArg(call, param, arg) |
|
||||||
parameterValueFlowsThroughNoCtx(param, kind)
|
parameterValueFlowsThroughCand(param, kind)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,8 +90,10 @@ private module ImplCommon {
|
|||||||
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
|
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
|
||||||
* not taking call contexts into account.
|
* not taking call contexts into account.
|
||||||
*/
|
*/
|
||||||
private predicate argumentValueFlowsThroughNoCtx(ArgumentNode arg, OutNode out) {
|
private predicate argumentValueFlowsThroughCand(ArgumentNode arg, OutNode out) {
|
||||||
exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughNoCtx0(call, arg, kind) |
|
exists(DataFlowCall call, ReturnKind kind |
|
||||||
|
argumentValueFlowsThroughCand0(call, arg, kind)
|
||||||
|
|
|
||||||
out = getAnOutNode(call, kind) and
|
out = getAnOutNode(call, kind) and
|
||||||
compatibleTypes(arg.getType(), out.getType())
|
compatibleTypes(arg.getType(), out.getType())
|
||||||
)
|
)
|
||||||
@@ -85,7 +108,7 @@ private module ImplCommon {
|
|||||||
DataFlowCall call, int i, ArgumentNode arg, DataFlowCallable enclosing
|
DataFlowCall call, int i, ArgumentNode arg, DataFlowCallable enclosing
|
||||||
) {
|
) {
|
||||||
arg.argumentOf(call, i) and
|
arg.argumentOf(call, i) and
|
||||||
argumentValueFlowsThroughNoCtx(arg, _) and
|
argumentValueFlowsThroughCand(arg, _) and
|
||||||
enclosing = arg.getEnclosingCallable()
|
enclosing = arg.getEnclosingCallable()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,15 +116,20 @@ private module ImplCommon {
|
|||||||
private ParameterNode getAParameter(DataFlowCallable c) { result.getEnclosingCallable() = c }
|
private ParameterNode getAParameter(DataFlowCallable c) { result.getEnclosingCallable() = c }
|
||||||
|
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
private predicate viableParamArg0(int i, ArgumentNode arg, CallContext outercc, DataFlowCall call) {
|
private predicate viableParamArg0(
|
||||||
|
int i, ArgumentNode arg, CallContext outercc, DataFlowCall call
|
||||||
|
) {
|
||||||
exists(DataFlowCallable c | argumentOf(call, i, arg, c) |
|
exists(DataFlowCallable c | argumentOf(call, i, arg, c) |
|
||||||
|
(
|
||||||
outercc = TAnyCallContext()
|
outercc = TAnyCallContext()
|
||||||
or
|
or
|
||||||
outercc = TSomeCall(getAParameter(c), _)
|
outercc = TSomeCall(getAParameter(c), _)
|
||||||
or
|
or
|
||||||
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
|
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
|
||||||
reducedViableImplInCallContext(_, c, other)
|
recordDataFlowCallSite(other, c)
|
||||||
)
|
)
|
||||||
|
) and
|
||||||
|
not isUnreachableInCall(arg, outercc.(CallContextSpecificCall).getCall())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,8 +152,10 @@ private module ImplCommon {
|
|||||||
DataFlowCall call, ParameterNode p, ArgumentNode arg, CallContext outercc,
|
DataFlowCall call, ParameterNode p, ArgumentNode arg, CallContext outercc,
|
||||||
CallContextCall innercc
|
CallContextCall innercc
|
||||||
) {
|
) {
|
||||||
exists(int i, DataFlowCallable callable | viableParamArg1(p, callable, i, arg, outercc, call) |
|
exists(int i, DataFlowCallable callable |
|
||||||
if reducedViableImplInCallContext(_, callable, call)
|
viableParamArg1(p, callable, i, arg, outercc, call)
|
||||||
|
|
|
||||||
|
if recordDataFlowCallSite(call, callable)
|
||||||
then innercc = TSpecificCall(call, i, true)
|
then innercc = TSpecificCall(call, i, true)
|
||||||
else innercc = TSomeCall(p, true)
|
else innercc = TSomeCall(p, true)
|
||||||
)
|
)
|
||||||
@@ -137,7 +167,7 @@ private module ImplCommon {
|
|||||||
exists(DataFlowCall call, int i, DataFlowCallable callable |
|
exists(DataFlowCall call, int i, DataFlowCallable callable |
|
||||||
result = TSpecificCall(call, i, _) and
|
result = TSpecificCall(call, i, _) and
|
||||||
p.isParameterOf(callable, i) and
|
p.isParameterOf(callable, i) and
|
||||||
reducedViableImplInCallContext(_, callable, call)
|
recordDataFlowCallSite(call, callable)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,20 +177,22 @@ private module ImplCommon {
|
|||||||
*/
|
*/
|
||||||
private predicate parameterValueFlow(ParameterNode p, Node node, CallContextCall cc) {
|
private predicate parameterValueFlow(ParameterNode p, Node node, CallContextCall cc) {
|
||||||
p = node and
|
p = node and
|
||||||
parameterValueFlowsThroughNoCtx(p, _) and
|
parameterValueFlowsThroughCand(p, _) and
|
||||||
cc = getAValidCallContextForParameter(p)
|
cc = getAValidCallContextForParameter(p)
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
parameterValueFlow(p, mid, cc) and
|
parameterValueFlow(p, mid, cc) and
|
||||||
simpleLocalFlowStep(mid, node) and
|
step(mid, node) and
|
||||||
compatibleTypes(p.getType(), node.getType())
|
compatibleTypes(p.getType(), node.getType()) and
|
||||||
|
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// flow through a callable
|
// flow through a callable
|
||||||
exists(Node arg |
|
exists(Node arg |
|
||||||
parameterValueFlow(p, arg, cc) and
|
parameterValueFlow(p, arg, cc) and
|
||||||
argumentValueFlowsThrough(arg, node, cc) and
|
argumentValueFlowsThrough(arg, node, cc) and
|
||||||
compatibleTypes(p.getType(), node.getType())
|
compatibleTypes(p.getType(), node.getType()) and
|
||||||
|
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,8 +200,9 @@ private module ImplCommon {
|
|||||||
* Holds if `p` can flow to a return node of kind `kind` in the same
|
* Holds if `p` can flow to a return node of kind `kind` in the same
|
||||||
* callable using only value-preserving steps, in call context `cc`.
|
* callable using only value-preserving steps, in call context `cc`.
|
||||||
*/
|
*/
|
||||||
cached
|
private predicate parameterValueFlowsThrough(
|
||||||
predicate parameterValueFlowsThrough(ParameterNode p, ReturnKind kind, CallContextCall cc) {
|
ParameterNode p, ReturnKind kind, CallContextCall cc
|
||||||
|
) {
|
||||||
parameterValueFlow(p, getAReturnNodeOfKind(kind), cc)
|
parameterValueFlow(p, getAReturnNodeOfKind(kind), cc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,13 +220,16 @@ private module ImplCommon {
|
|||||||
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
|
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
|
||||||
* in call context cc.
|
* in call context cc.
|
||||||
*/
|
*/
|
||||||
cached
|
|
||||||
predicate argumentValueFlowsThrough(ArgumentNode arg, OutNode out, CallContext cc) {
|
predicate argumentValueFlowsThrough(ArgumentNode arg, OutNode out, CallContext cc) {
|
||||||
exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, cc) |
|
exists(DataFlowCall call, ReturnKind kind |
|
||||||
|
argumentValueFlowsThrough0(call, arg, kind, cc)
|
||||||
|
|
|
||||||
out = getAnOutNode(call, kind) and
|
out = getAnOutNode(call, kind) and
|
||||||
|
not isUnreachableInCall(out, cc.(CallContextSpecificCall).getCall()) and
|
||||||
compatibleTypes(arg.getType(), out.getType())
|
compatibleTypes(arg.getType(), out.getType())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `p` can flow to the pre-update node of `n` in the same callable
|
* Holds if `p` can flow to the pre-update node of `n` in the same callable
|
||||||
@@ -210,7 +246,22 @@ private module ImplCommon {
|
|||||||
*/
|
*/
|
||||||
private predicate localValueStep(Node node1, Node node2) {
|
private predicate localValueStep(Node node1, Node node2) {
|
||||||
simpleLocalFlowStep(node1, node2) or
|
simpleLocalFlowStep(node1, node2) or
|
||||||
argumentValueFlowsThrough(node1, node2, _)
|
FlowThrough_v1::argumentValueFlowsThrough(node1, node2, _)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `p` can flow to `node` in the same callable allowing local flow
|
||||||
|
* steps and value flow through methods. Call contexts are only accounted
|
||||||
|
* for in the nested calls.
|
||||||
|
*/
|
||||||
|
private predicate parameterValueFlowNoCtx(ParameterNode p, Node node) {
|
||||||
|
p = node
|
||||||
|
or
|
||||||
|
exists(Node mid |
|
||||||
|
parameterValueFlowNoCtx(p, mid) and
|
||||||
|
localValueStep(mid, node) and
|
||||||
|
compatibleTypes(p.getType(), node.getType())
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -332,10 +383,202 @@ private module ImplCommon {
|
|||||||
exists(Node mid1, Node mid2, Content f |
|
exists(Node mid1, Node mid2, Content f |
|
||||||
store(node1, f, mid1) and
|
store(node1, f, mid1) and
|
||||||
localValueStep*(mid1, mid2) and
|
localValueStep*(mid1, mid2) and
|
||||||
read(mid2, f, node2)
|
read(mid2, f, node2) and
|
||||||
|
compatibleTypes(node1.getTypeBound(), node2.getTypeBound())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cached
|
||||||
|
module FlowThrough_v2 {
|
||||||
|
private predicate step(Node node1, Node node2) {
|
||||||
|
simpleLocalFlowStep(node1, node2) or
|
||||||
|
localStoreReadStep(node1, node2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `p` can flow to `node` in the same callable using only
|
||||||
|
* value-preserving steps, not taking call contexts into account.
|
||||||
|
*/
|
||||||
|
private predicate parameterValueFlowCand(ParameterNode p, Node node) {
|
||||||
|
p = node
|
||||||
|
or
|
||||||
|
exists(Node mid |
|
||||||
|
parameterValueFlowCand(p, mid) and
|
||||||
|
step(mid, node) and
|
||||||
|
compatibleTypes(p.getType(), node.getType())
|
||||||
|
)
|
||||||
|
or
|
||||||
|
// flow through a callable
|
||||||
|
exists(Node arg |
|
||||||
|
parameterValueFlowCand(p, arg) and
|
||||||
|
argumentValueFlowsThroughCand(arg, node) and
|
||||||
|
compatibleTypes(p.getType(), node.getType())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `p` can flow to a return node of kind `kind` in the same
|
||||||
|
* callable using only value-preserving steps, not taking call contexts
|
||||||
|
* into account.
|
||||||
|
*/
|
||||||
|
private predicate parameterValueFlowsThroughCand(ParameterNode p, ReturnKind kind) {
|
||||||
|
parameterValueFlowCand(p, getAReturnNodeOfKind(kind))
|
||||||
|
}
|
||||||
|
|
||||||
|
pragma[nomagic]
|
||||||
|
private predicate argumentValueFlowsThroughCand0(
|
||||||
|
DataFlowCall call, ArgumentNode arg, ReturnKind kind
|
||||||
|
) {
|
||||||
|
exists(ParameterNode param | viableParamArg(call, param, arg) |
|
||||||
|
parameterValueFlowsThroughCand(param, kind)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
|
||||||
|
* not taking call contexts into account.
|
||||||
|
*/
|
||||||
|
private predicate argumentValueFlowsThroughCand(ArgumentNode arg, OutNode out) {
|
||||||
|
exists(DataFlowCall call, ReturnKind kind |
|
||||||
|
argumentValueFlowsThroughCand0(call, arg, kind)
|
||||||
|
|
|
||||||
|
out = getAnOutNode(call, kind) and
|
||||||
|
compatibleTypes(arg.getType(), out.getType())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `arg` is the `i`th argument of `call` inside the callable
|
||||||
|
* `enclosing`, and `arg` may flow through `call`.
|
||||||
|
*/
|
||||||
|
pragma[noinline]
|
||||||
|
private predicate argumentOf(
|
||||||
|
DataFlowCall call, int i, ArgumentNode arg, DataFlowCallable enclosing
|
||||||
|
) {
|
||||||
|
arg.argumentOf(call, i) and
|
||||||
|
argumentValueFlowsThroughCand(arg, _) and
|
||||||
|
enclosing = arg.getEnclosingCallable()
|
||||||
|
}
|
||||||
|
|
||||||
|
pragma[noinline]
|
||||||
|
private ParameterNode getAParameter(DataFlowCallable c) { result.getEnclosingCallable() = c }
|
||||||
|
|
||||||
|
pragma[noinline]
|
||||||
|
private predicate viableParamArg0(
|
||||||
|
int i, ArgumentNode arg, CallContext outercc, DataFlowCall call
|
||||||
|
) {
|
||||||
|
exists(DataFlowCallable c | argumentOf(call, i, arg, c) |
|
||||||
|
(
|
||||||
|
outercc = TAnyCallContext()
|
||||||
|
or
|
||||||
|
outercc = TSomeCall(getAParameter(c), _)
|
||||||
|
or
|
||||||
|
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
|
||||||
|
recordDataFlowCallSite(other, c)
|
||||||
|
)
|
||||||
|
) and
|
||||||
|
not isUnreachableInCall(arg, outercc.(CallContextSpecificCall).getCall())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pragma[noinline]
|
||||||
|
private predicate viableParamArg1(
|
||||||
|
ParameterNode p, DataFlowCallable callable, int i, ArgumentNode arg, CallContext outercc,
|
||||||
|
DataFlowCall call
|
||||||
|
) {
|
||||||
|
viableParamArg0(i, arg, outercc, call) and
|
||||||
|
callable = resolveCall(call, outercc) and
|
||||||
|
p.isParameterOf(callable, any(int j | j <= i and j >= i))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `arg` is a possible argument to `p`, in the call `call`, and
|
||||||
|
* `arg` may flow through `call`. The possible contexts before and after
|
||||||
|
* entering the callable are `outercc` and `innercc`, respectively.
|
||||||
|
*/
|
||||||
|
private predicate viableParamArg(
|
||||||
|
DataFlowCall call, ParameterNode p, ArgumentNode arg, CallContext outercc,
|
||||||
|
CallContextCall innercc
|
||||||
|
) {
|
||||||
|
exists(int i, DataFlowCallable callable |
|
||||||
|
viableParamArg1(p, callable, i, arg, outercc, call)
|
||||||
|
|
|
||||||
|
if recordDataFlowCallSite(call, callable)
|
||||||
|
then innercc = TSpecificCall(call, i, true)
|
||||||
|
else innercc = TSomeCall(p, true)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private CallContextCall getAValidCallContextForParameter(ParameterNode p) {
|
||||||
|
result = TSomeCall(p, _)
|
||||||
|
or
|
||||||
|
exists(DataFlowCall call, int i, DataFlowCallable callable |
|
||||||
|
result = TSpecificCall(call, i, _) and
|
||||||
|
p.isParameterOf(callable, i) and
|
||||||
|
recordDataFlowCallSite(call, callable)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `p` can flow to `node` in the same callable using only
|
||||||
|
* value-preserving steps, in call context `cc`.
|
||||||
|
*/
|
||||||
|
private predicate parameterValueFlow(ParameterNode p, Node node, CallContextCall cc) {
|
||||||
|
p = node and
|
||||||
|
parameterValueFlowsThroughCand(p, _) and
|
||||||
|
cc = getAValidCallContextForParameter(p)
|
||||||
|
or
|
||||||
|
exists(Node mid |
|
||||||
|
parameterValueFlow(p, mid, cc) and
|
||||||
|
step(mid, node) and
|
||||||
|
compatibleTypes(p.getType(), node.getType()) and
|
||||||
|
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
|
||||||
|
)
|
||||||
|
or
|
||||||
|
// flow through a callable
|
||||||
|
exists(Node arg |
|
||||||
|
parameterValueFlow(p, arg, cc) and
|
||||||
|
argumentValueFlowsThrough(arg, node, cc) and
|
||||||
|
compatibleTypes(p.getType(), node.getType()) and
|
||||||
|
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `p` can flow to a return node of kind `kind` in the same
|
||||||
|
* callable using only value-preserving steps, in call context `cc`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate parameterValueFlowsThrough(ParameterNode p, ReturnKind kind, CallContextCall cc) {
|
||||||
|
parameterValueFlow(p, getAReturnNodeOfKind(kind), cc)
|
||||||
|
}
|
||||||
|
|
||||||
|
pragma[nomagic]
|
||||||
|
private predicate argumentValueFlowsThrough0(
|
||||||
|
DataFlowCall call, ArgumentNode arg, ReturnKind kind, CallContext cc
|
||||||
|
) {
|
||||||
|
exists(ParameterNode param, CallContext innercc |
|
||||||
|
viableParamArg(call, param, arg, cc, innercc) and
|
||||||
|
parameterValueFlowsThrough(param, kind, innercc)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
|
||||||
|
* in call context cc.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate argumentValueFlowsThrough(ArgumentNode arg, OutNode out, CallContext cc) {
|
||||||
|
exists(DataFlowCall call, ReturnKind kind |
|
||||||
|
argumentValueFlowsThrough0(call, arg, kind, cc)
|
||||||
|
|
|
||||||
|
out = getAnOutNode(call, kind) and
|
||||||
|
not isUnreachableInCall(out, cc.(CallContextSpecificCall).getCall()) and
|
||||||
|
compatibleTypes(arg.getType(), out.getType())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `call` passes an implicit or explicit instance argument, i.e., an
|
* Holds if `call` passes an implicit or explicit instance argument, i.e., an
|
||||||
* expression that reaches a `this` parameter.
|
* expression that reaches a `this` parameter.
|
||||||
@@ -344,11 +587,22 @@ private module ImplCommon {
|
|||||||
exists(ArgumentNode arg | arg.argumentOf(call, -1))
|
exists(ArgumentNode arg | arg.argumentOf(call, -1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if the call context `call` either improves virtual dispatch in
|
||||||
|
* `callable` or if it allows us to prune unreachable nodes in `callable`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
|
||||||
|
reducedViableImplInCallContext(_, callable, call)
|
||||||
|
or
|
||||||
|
exists(Node n | n.getEnclosingCallable() = callable | isUnreachableInCall(n, call))
|
||||||
|
}
|
||||||
|
|
||||||
cached
|
cached
|
||||||
newtype TCallContext =
|
newtype TCallContext =
|
||||||
TAnyCallContext() or
|
TAnyCallContext() or
|
||||||
TSpecificCall(DataFlowCall call, int i, boolean emptyAp) {
|
TSpecificCall(DataFlowCall call, int i, boolean emptyAp) {
|
||||||
reducedViableImplInCallContext(_, _, call) and
|
recordDataFlowCallSite(call, _) and
|
||||||
(emptyAp = true or emptyAp = false) and
|
(emptyAp = true or emptyAp = false) and
|
||||||
(
|
(
|
||||||
exists(call.getArgument(i))
|
exists(call.getArgument(i))
|
||||||
@@ -362,25 +616,29 @@ private module ImplCommon {
|
|||||||
cached
|
cached
|
||||||
newtype TReturnPosition =
|
newtype TReturnPosition =
|
||||||
TReturnPosition0(DataFlowCallable c, ReturnKind kind) { returnPosition(_, c, kind) }
|
TReturnPosition0(DataFlowCallable c, ReturnKind kind) { returnPosition(_, c, kind) }
|
||||||
}
|
|
||||||
|
|
||||||
import ImplCommon
|
cached
|
||||||
|
newtype TLocalFlowCallContext =
|
||||||
|
TAnyLocalCall() or
|
||||||
|
TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) }
|
||||||
|
}
|
||||||
|
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
private predicate returnPosition(ReturnNode ret, DataFlowCallable c, ReturnKind kind) {
|
private predicate returnPosition(ReturnNode ret, DataFlowCallable c, ReturnKind kind) {
|
||||||
c = returnNodeGetEnclosingCallable(ret) and
|
c = returnNodeGetEnclosingCallable(ret) and
|
||||||
kind = ret.getKind()
|
kind = ret.getKind()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A call context to restrict the targets of virtual dispatch and match the
|
* A call context to restrict the targets of virtual dispatch, prune local flow,
|
||||||
* call sites of flow into a method with flow out of a method.
|
* and match the call sites of flow into a method with flow out of a method.
|
||||||
*
|
*
|
||||||
* There are four cases:
|
* There are four cases:
|
||||||
* - `TAnyCallContext()` : No restrictions on method flow.
|
* - `TAnyCallContext()` : No restrictions on method flow.
|
||||||
* - `TSpecificCall(DataFlowCall call, int i)` : Flow entered through the `i`th
|
* - `TSpecificCall(DataFlowCall call, int i)` : Flow entered through the `i`th
|
||||||
* parameter at the given `call`. This call improves the set of viable
|
* parameter at the given `call`. This call improves the set of viable
|
||||||
* dispatch targets for at least one method call in the current callable.
|
* dispatch targets for at least one method call in the current callable
|
||||||
|
* or helps prune unreachable nodes in the current callable.
|
||||||
* - `TSomeCall(ParameterNode p)` : Flow entered through parameter `p`. The
|
* - `TSomeCall(ParameterNode p)` : Flow entered through parameter `p`. The
|
||||||
* originating call does not improve the set of dispatch targets for any
|
* originating call does not improve the set of dispatch targets for any
|
||||||
* method call in the current callable and was therefore not recorded.
|
* method call in the current callable and was therefore not recorded.
|
||||||
@@ -388,36 +646,98 @@ private predicate returnPosition(ReturnNode ret, DataFlowCallable c, ReturnKind
|
|||||||
* this dispatch target of `call` implies a reduced set of dispatch origins
|
* this dispatch target of `call` implies a reduced set of dispatch origins
|
||||||
* to which data may flow if it should reach a `return` statement.
|
* to which data may flow if it should reach a `return` statement.
|
||||||
*/
|
*/
|
||||||
abstract class CallContext extends TCallContext {
|
abstract class CallContext extends TCallContext {
|
||||||
abstract string toString();
|
abstract string toString();
|
||||||
}
|
|
||||||
|
|
||||||
class CallContextAny extends CallContext, TAnyCallContext {
|
/** Holds if this call context is relevant for `callable`. */
|
||||||
|
abstract predicate relevantFor(DataFlowCallable callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
class CallContextAny extends CallContext, TAnyCallContext {
|
||||||
override string toString() { result = "CcAny" }
|
override string toString() { result = "CcAny" }
|
||||||
}
|
|
||||||
|
|
||||||
abstract class CallContextCall extends CallContext { }
|
override predicate relevantFor(DataFlowCallable callable) { any() }
|
||||||
|
}
|
||||||
|
|
||||||
class CallContextSpecificCall extends CallContextCall, TSpecificCall {
|
abstract class CallContextCall extends CallContext { }
|
||||||
|
|
||||||
|
class CallContextSpecificCall extends CallContextCall, TSpecificCall {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
exists(DataFlowCall call, int i | this = TSpecificCall(call, i, _) |
|
exists(DataFlowCall call, int i | this = TSpecificCall(call, i, _) |
|
||||||
result = "CcCall(" + call + ", " + i + ")"
|
result = "CcCall(" + call + ", " + i + ")"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class CallContextSomeCall extends CallContextCall, TSomeCall {
|
override predicate relevantFor(DataFlowCallable callable) {
|
||||||
|
recordDataFlowCallSite(getCall(), callable)
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFlowCall getCall() { this = TSpecificCall(result, _, _) }
|
||||||
|
}
|
||||||
|
|
||||||
|
class CallContextSomeCall extends CallContextCall, TSomeCall {
|
||||||
override string toString() { result = "CcSomeCall" }
|
override string toString() { result = "CcSomeCall" }
|
||||||
}
|
|
||||||
|
|
||||||
class CallContextReturn extends CallContext, TReturn {
|
override predicate relevantFor(DataFlowCallable callable) {
|
||||||
|
exists(ParameterNode p | this = TSomeCall(p, _) and p.getEnclosingCallable() = callable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CallContextReturn extends CallContext, TReturn {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
|
exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/** A callable tagged with a relevant return kind. */
|
override predicate relevantFor(DataFlowCallable callable) {
|
||||||
class ReturnPosition extends TReturnPosition0 {
|
exists(DataFlowCall call | this = TReturn(_, call) and call.getEnclosingCallable() = callable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A call context that is relevant for pruning local flow.
|
||||||
|
*/
|
||||||
|
abstract class LocalCallContext extends TLocalFlowCallContext {
|
||||||
|
abstract string toString();
|
||||||
|
|
||||||
|
/** Holds if this call context is relevant for `callable`. */
|
||||||
|
abstract predicate relevantFor(DataFlowCallable callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
class LocalCallContextAny extends LocalCallContext, TAnyLocalCall {
|
||||||
|
override string toString() { result = "LocalCcAny" }
|
||||||
|
|
||||||
|
override predicate relevantFor(DataFlowCallable callable) { any() }
|
||||||
|
}
|
||||||
|
|
||||||
|
class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall {
|
||||||
|
LocalCallContextSpecificCall() { this = TSpecificLocalCall(call) }
|
||||||
|
|
||||||
|
DataFlowCall call;
|
||||||
|
|
||||||
|
DataFlowCall getCall() { result = call }
|
||||||
|
|
||||||
|
override string toString() { result = "LocalCcCall(" + call + ")" }
|
||||||
|
|
||||||
|
override predicate relevantFor(DataFlowCallable callable) { relevantLocalCCtx(call, callable) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) {
|
||||||
|
exists(Node n | n.getEnclosingCallable() = callable and isUnreachableInCall(n, call))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the local call context given the call context and the callable that
|
||||||
|
* the contexts apply to.
|
||||||
|
*/
|
||||||
|
LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) {
|
||||||
|
ctx.relevantFor(callable) and
|
||||||
|
if relevantLocalCCtx(ctx.(CallContextSpecificCall).getCall(), callable)
|
||||||
|
then result.(LocalCallContextSpecificCall).getCall() = ctx.(CallContextSpecificCall).getCall()
|
||||||
|
else result instanceof LocalCallContextAny
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A callable tagged with a relevant return kind. */
|
||||||
|
class ReturnPosition extends TReturnPosition0 {
|
||||||
private DataFlowCallable c;
|
private DataFlowCallable c;
|
||||||
private ReturnKind kind;
|
private ReturnKind kind;
|
||||||
|
|
||||||
@@ -431,22 +751,22 @@ class ReturnPosition extends TReturnPosition0 {
|
|||||||
|
|
||||||
/** Gets a textual representation of this return position. */
|
/** Gets a textual representation of this return position. */
|
||||||
string toString() { result = "[" + kind + "] " + c }
|
string toString() { result = "[" + kind + "] " + c }
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
DataFlowCallable returnNodeGetEnclosingCallable(ReturnNode ret) {
|
DataFlowCallable returnNodeGetEnclosingCallable(ReturnNode ret) {
|
||||||
result = ret.getEnclosingCallable()
|
result = ret.getEnclosingCallable()
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
ReturnPosition getReturnPosition(ReturnNode ret) {
|
ReturnPosition getReturnPosition(ReturnNode ret) {
|
||||||
exists(DataFlowCallable c, ReturnKind k | returnPosition(ret, c, k) |
|
exists(DataFlowCallable c, ReturnKind k | returnPosition(ret, c, k) |
|
||||||
result = TReturnPosition0(c, k)
|
result = TReturnPosition0(c, k)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
bindingset[cc, callable]
|
bindingset[cc, callable]
|
||||||
predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
|
predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
|
||||||
cc instanceof CallContextAny and callable = viableCallable(call)
|
cc instanceof CallContextAny and callable = viableCallable(call)
|
||||||
or
|
or
|
||||||
exists(DataFlowCallable c0, DataFlowCall call0 |
|
exists(DataFlowCallable c0, DataFlowCall call0 |
|
||||||
@@ -454,10 +774,10 @@ predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall
|
|||||||
cc = TReturn(c0, call0) and
|
cc = TReturn(c0, call0) and
|
||||||
c0 = prunedViableImplInCallContextReverse(call0, call)
|
c0 = prunedViableImplInCallContextReverse(call0, call)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
bindingset[call, cc]
|
bindingset[call, cc]
|
||||||
DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
|
DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
|
||||||
exists(DataFlowCall ctx | cc = TSpecificCall(ctx, _, _) |
|
exists(DataFlowCall ctx | cc = TSpecificCall(ctx, _, _) |
|
||||||
if reducedViableImplInCallContext(call, _, ctx)
|
if reducedViableImplInCallContext(call, _, ctx)
|
||||||
then result = prunedViableImplInCallContext(call, ctx)
|
then result = prunedViableImplInCallContext(call, ctx)
|
||||||
@@ -469,4 +789,5 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
|
|||||||
result = viableCallable(call) and cc instanceof CallContextAny
|
result = viableCallable(call) and cc instanceof CallContextAny
|
||||||
or
|
or
|
||||||
result = viableCallable(call) and cc instanceof CallContextReturn
|
result = viableCallable(call) and cc instanceof CallContextReturn
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* on each other without introducing mutual recursion among those configurations.
|
* on each other without introducing mutual recursion among those configurations.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private import DataFlowImplCommon
|
private import DataFlowImplCommon::Public
|
||||||
private import DataFlowImplSpecific::Private
|
private import DataFlowImplSpecific::Private
|
||||||
import DataFlowImplSpecific::Public
|
import DataFlowImplSpecific::Public
|
||||||
|
|
||||||
@@ -905,8 +905,10 @@ private predicate localFlowExit(Node node, Configuration config) {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate localFlowStepPlus(
|
private predicate localFlowStepPlus(
|
||||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||||
) {
|
) {
|
||||||
|
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||||
|
(
|
||||||
localFlowEntry(node1, config) and
|
localFlowEntry(node1, config) and
|
||||||
(
|
(
|
||||||
localFlowStep(node1, node2, config) and preservesValue = true
|
localFlowStep(node1, node2, config) and preservesValue = true
|
||||||
@@ -914,33 +916,36 @@ private predicate localFlowStepPlus(
|
|||||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||||
) and
|
) and
|
||||||
node1 != node2 and
|
node1 != node2 and
|
||||||
|
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||||
|
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||||
localFlowStep(mid, node2, config) and
|
localFlowStep(mid, node2, config) and
|
||||||
not mid instanceof CastNode and
|
not mid instanceof CastNode and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowStepPlus(node1, mid, _, config) and
|
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||||
additionalLocalFlowStep(mid, node2, config) and
|
additionalLocalFlowStep(mid, node2, config) and
|
||||||
not mid instanceof CastNode and
|
not mid instanceof CastNode and
|
||||||
preservesValue = false and
|
preservesValue = false and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
)
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node1` can step to `node2` in one or more local steps and this
|
* Holds if `node1` can step to `node2` in one or more local steps and this
|
||||||
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
||||||
*/
|
*/
|
||||||
pragma[noinline]
|
pragma[nomagic]
|
||||||
private predicate localFlowBigStep(
|
private predicate localFlowBigStep(
|
||||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
|
||||||
) {
|
) {
|
||||||
localFlowStepPlus(node1, node2, preservesValue, config) and
|
localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
|
||||||
localFlowExit(node2, config)
|
localFlowExit(node2, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
|||||||
(
|
(
|
||||||
any(Configuration c).isSource(this)
|
any(Configuration c).isSource(this)
|
||||||
or
|
or
|
||||||
localFlowBigStep(_, this, false, _)
|
localFlowBigStep(_, this, false, _, _)
|
||||||
or
|
or
|
||||||
additionalJumpStep(_, this, _)
|
additionalJumpStep(_, this, _)
|
||||||
)
|
)
|
||||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
|||||||
(
|
(
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
flowCandFwd(mid, fromArg, apf, config) and
|
flowCandFwd(mid, fromArg, apf, config) and
|
||||||
localFlowBigStep(mid, node, true, config)
|
localFlowBigStep(mid, node, true, config, _)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathFrontNil nil |
|
exists(Node mid, AccessPathFrontNil nil |
|
||||||
flowCandFwd(mid, fromArg, nil, config) and
|
flowCandFwd(mid, fromArg, nil, config) and
|
||||||
localFlowBigStep(mid, node, false, config) and
|
localFlowBigStep(mid, node, false, config, _) and
|
||||||
apf = node.(AccessPathFrontNilNode).getApf()
|
apf = node.(AccessPathFrontNilNode).getApf()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
|||||||
flowCandFwd(mid, fromArg, _, config) and
|
flowCandFwd(mid, fromArg, _, config) and
|
||||||
store(mid, f, node) and
|
store(mid, f, node) and
|
||||||
nodeCand(node, unbind(config)) and
|
nodeCand(node, unbind(config)) and
|
||||||
|
readStoreCand(f, unbind(config)) and
|
||||||
apf.headUsesContent(f)
|
apf.headUsesContent(f)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
|||||||
apf instanceof AccessPathFrontNil
|
apf instanceof AccessPathFrontNil
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowBigStep(node, mid, true, config) and
|
localFlowBigStep(node, mid, true, config, _) and
|
||||||
flowCand(mid, toReturn, apf, config)
|
flowCand(mid, toReturn, apf, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathFrontNil nil |
|
exists(Node mid, AccessPathFrontNil nil |
|
||||||
flowCandFwd(node, _, apf, config) and
|
flowCandFwd(node, _, apf, config) and
|
||||||
localFlowBigStep(node, mid, false, config) and
|
localFlowBigStep(node, mid, false, config, _) and
|
||||||
flowCand(mid, toReturn, nil, config) and
|
flowCand(mid, toReturn, nil, config) and
|
||||||
apf instanceof AccessPathFrontNil
|
apf instanceof AccessPathFrontNil
|
||||||
)
|
)
|
||||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
|||||||
exists(Content f, AccessPathFront apf0 |
|
exists(Content f, AccessPathFront apf0 |
|
||||||
flowCandStore(node, f, toReturn, apf0, config) and
|
flowCandStore(node, f, toReturn, apf0, config) and
|
||||||
apf0.headUsesContent(f) and
|
apf0.headUsesContent(f) and
|
||||||
consCand(f, apf, unbind(config))
|
consCand(f, apf, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Content f, AccessPathFront apf0 |
|
exists(Content f, AccessPathFront apf0 |
|
||||||
flowCandRead(node, f, toReturn, apf0, config) and
|
flowCandRead(node, f, toReturn, apf0, config) and
|
||||||
consCandFwd(f, apf0, unbind(config)) and
|
consCandFwd(f, apf0, config) and
|
||||||
apf.headUsesContent(f)
|
apf.headUsesContent(f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1221,8 +1227,8 @@ private newtype TAccessPath =
|
|||||||
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||||
* element of the list and its length are tracked. If data flows from a source to
|
* elements of the list and its length are tracked. If data flows from a source to
|
||||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||||
* dereference operations needed to get from the value in the node to the
|
* dereference operations needed to get from the value in the node to the
|
||||||
* tracked object. The final type indicates the type of the tracked object.
|
* tracked object. The final type indicates the type of the tracked object.
|
||||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
|||||||
|
|
||||||
private class AccessPathNil extends AccessPath, TNil {
|
private class AccessPathNil extends AccessPath, TNil {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
|
exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override AccessPathFront getFront() {
|
override AccessPathFront getFront() {
|
||||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||||
// The `concat` becomes "" if `ppReprType` has no result.
|
// The `concat` becomes "" if `ppReprType` has no result.
|
||||||
result = f.toString() + concat(" : " + ppReprType(t))
|
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1293,8 +1299,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||||
if len = 2
|
if len = 2
|
||||||
then result = f1.toString() + ", " + f2.toString()
|
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
|||||||
(
|
(
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
flowFwd(mid, fromArg, apf, ap, config) and
|
flowFwd(mid, fromArg, apf, ap, config) and
|
||||||
localFlowBigStep(mid, node, true, config)
|
localFlowBigStep(mid, node, true, config, _)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathNil nil |
|
exists(Node mid, AccessPathNil nil |
|
||||||
flowFwd(mid, fromArg, _, nil, config) and
|
flowFwd(mid, fromArg, _, nil, config) and
|
||||||
localFlowBigStep(mid, node, false, config) and
|
localFlowBigStep(mid, node, false, config, _) and
|
||||||
ap = node.(AccessPathNilNode).getAp() and
|
ap = node.(AccessPathNilNode).getAp() and
|
||||||
apf = ap.(AccessPathNil).getFront()
|
apf = ap.(AccessPathNil).getFront()
|
||||||
)
|
)
|
||||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
|||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowBigStep(node, mid, true, config) and
|
localFlowBigStep(node, mid, true, config, _) and
|
||||||
flow(mid, toReturn, ap, config)
|
flow(mid, toReturn, ap, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathNil nil |
|
exists(Node mid, AccessPathNil nil |
|
||||||
flowFwd(node, _, _, ap, config) and
|
flowFwd(node, _, _, ap, config) and
|
||||||
localFlowBigStep(node, mid, false, config) and
|
localFlowBigStep(node, mid, false, config, _) and
|
||||||
flow(mid, toReturn, nil, config) and
|
flow(mid, toReturn, nil, config) and
|
||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
)
|
)
|
||||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
|||||||
this instanceof PathNodeSink and result = ""
|
this instanceof PathNodeSink and result = ""
|
||||||
or
|
or
|
||||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||||
if s = "" then result = "" else result = " [" + s + "]"
|
if s = "" then result = "" else result = " " + s
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1728,14 +1734,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
|||||||
* a callable is recorded by `cc`.
|
* a callable is recorded by `cc`.
|
||||||
*/
|
*/
|
||||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||||
|
midnode = mid.getNode() and
|
||||||
|
conf = mid.getConfiguration() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
ap = mid.getAp()
|
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||||
|
ap0 = mid.getAp()
|
||||||
|
|
|
||||||
|
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||||
|
ap = ap0
|
||||||
or
|
or
|
||||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||||
cc = mid.getCallContext() and
|
ap0 instanceof AccessPathNil and
|
||||||
mid.getAp() instanceof AccessPathNil and
|
|
||||||
ap = node.(AccessPathNilNode).getAp()
|
ap = node.(AccessPathNilNode).getAp()
|
||||||
|
)
|
||||||
or
|
or
|
||||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||||
cc instanceof CallContextAny and
|
cc instanceof CallContextAny and
|
||||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
|||||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||||
p.isParameterOf(callable, i)
|
p.isParameterOf(callable, i)
|
||||||
|
|
|
|
||||||
if reducedViableImplInCallContext(_, callable, call)
|
if recordDataFlowCallSite(call, callable)
|
||||||
then innercc = TSpecificCall(call, i, emptyAp)
|
then innercc = TSpecificCall(call, i, emptyAp)
|
||||||
else innercc = TSomeCall(p, emptyAp)
|
else innercc = TSomeCall(p, emptyAp)
|
||||||
)
|
)
|
||||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
|||||||
|
|
||||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
|
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override AccessPathFront getFront() {
|
override AccessPathFront getFront() {
|
||||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||||
if len = 1
|
if len = 1
|
||||||
then result = f.toString()
|
then result = "[" + f.toString() + "]"
|
||||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
|||||||
|
|
||||||
private string ppAp() {
|
private string ppAp() {
|
||||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||||
if s = "" then result = "" else result = " [" + s + "]"
|
if s = "" then result = "" else result = " " + s
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2204,6 +2216,8 @@ private module FlowExploration {
|
|||||||
private predicate partialPathStep(
|
private predicate partialPathStep(
|
||||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
|
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||||
|
(
|
||||||
localFlowStep(mid.getNode(), node, config) and
|
localFlowStep(mid.getNode(), node, config) and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
@@ -2214,6 +2228,7 @@ private module FlowExploration {
|
|||||||
mid.getAp() instanceof PartialAccessPathNil and
|
mid.getAp() instanceof PartialAccessPathNil and
|
||||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||||
config = mid.getConfiguration()
|
config = mid.getConfiguration()
|
||||||
|
)
|
||||||
or
|
or
|
||||||
jumpStep(mid.getNode(), node, config) and
|
jumpStep(mid.getNode(), node, config) and
|
||||||
cc instanceof CallContextAny and
|
cc instanceof CallContextAny and
|
||||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
|||||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||||
p.isParameterOf(callable, i)
|
p.isParameterOf(callable, i)
|
||||||
|
|
|
|
||||||
if reducedViableImplInCallContext(_, callable, call)
|
if recordDataFlowCallSite(call, callable)
|
||||||
then innercc = TSpecificCall(call, i, emptyAp)
|
then innercc = TSpecificCall(call, i, emptyAp)
|
||||||
else innercc = TSomeCall(p, emptyAp)
|
else innercc = TSomeCall(p, emptyAp)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -51,7 +51,9 @@ class ArgumentNode extends Node {
|
|||||||
DataFlowCall getCall() { this.argumentOf(result, _) }
|
DataFlowCall getCall() { this.argumentOf(result, _) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private newtype TReturnKind = TNormalReturnKind()
|
private newtype TReturnKind =
|
||||||
|
TNormalReturnKind() or
|
||||||
|
TRefReturnKind(int i) { exists(Parameter parameter | i = parameter.getIndex()) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A return kind. A return kind describes how a value can be returned
|
* A return kind. A return kind describes how a value can be returned
|
||||||
@@ -59,23 +61,54 @@ private newtype TReturnKind = TNormalReturnKind()
|
|||||||
*/
|
*/
|
||||||
class ReturnKind extends TReturnKind {
|
class ReturnKind extends TReturnKind {
|
||||||
/** Gets a textual representation of this return kind. */
|
/** Gets a textual representation of this return kind. */
|
||||||
string toString() { result = "return" }
|
string toString() {
|
||||||
|
this instanceof TNormalReturnKind and
|
||||||
|
result = "return"
|
||||||
|
or
|
||||||
|
this instanceof TRefReturnKind and
|
||||||
|
result = "ref"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A data flow node that occurs as the result of a `ReturnStmt`. */
|
/** A data flow node that represents a returned value in the called function. */
|
||||||
class ReturnNode extends ExprNode {
|
abstract class ReturnNode extends Node {
|
||||||
ReturnNode() { exists(ReturnStmt ret | this.getExpr() = ret.getExpr()) }
|
/** Gets the kind of this returned value. */
|
||||||
|
abstract ReturnKind getKind();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A `ReturnNode` that occurs as the result of a `ReturnStmt`. */
|
||||||
|
private class NormalReturnNode extends ReturnNode, ExprNode {
|
||||||
|
NormalReturnNode() { exists(ReturnStmt ret | this.getExpr() = ret.getExpr()) }
|
||||||
|
|
||||||
/** Gets the kind of this returned value. */
|
/** Gets the kind of this returned value. */
|
||||||
ReturnKind getKind() { result = TNormalReturnKind() }
|
override ReturnKind getKind() { result = TNormalReturnKind() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A data flow node that represents the output of a call. */
|
/**
|
||||||
class OutNode extends ExprNode {
|
* A `ReturnNode` that occurs as a result of a definition of a reference
|
||||||
OutNode() { this.getExpr() instanceof Call }
|
* parameter reaching the end of a function body.
|
||||||
|
*/
|
||||||
|
private class RefReturnNode extends ReturnNode, RefParameterFinalValueNode {
|
||||||
|
/** Gets the kind of this returned value. */
|
||||||
|
override ReturnKind getKind() { result = TRefReturnKind(this.getParameter().getIndex()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A data flow node that represents the output of a call at the call site. */
|
||||||
|
abstract class OutNode extends Node {
|
||||||
|
/** Gets the underlying call. */
|
||||||
|
abstract DataFlowCall getCall();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ExprOutNode extends OutNode, ExprNode {
|
||||||
|
ExprOutNode() { this.getExpr() instanceof Call }
|
||||||
|
|
||||||
/** Gets the underlying call. */
|
/** Gets the underlying call. */
|
||||||
DataFlowCall getCall() { result = this.getExpr() }
|
override DataFlowCall getCall() { result = this.getExpr() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RefOutNode extends OutNode, DefinitionByReferenceNode {
|
||||||
|
/** Gets the underlying call. */
|
||||||
|
override DataFlowCall getCall() { result = this.getArgument().getParent() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -85,6 +118,11 @@ class OutNode extends ExprNode {
|
|||||||
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
|
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
|
||||||
result = call.getNode() and
|
result = call.getNode() and
|
||||||
kind = TNormalReturnKind()
|
kind = TNormalReturnKind()
|
||||||
|
or
|
||||||
|
exists(int i |
|
||||||
|
result.asDefiningArgument() = call.getArgument(i) and
|
||||||
|
kind = TRefReturnKind(i)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -264,3 +302,5 @@ class DataFlowCall extends Expr {
|
|||||||
/** Gets the enclosing callable of this call. */
|
/** Gets the enclosing callable of this call. */
|
||||||
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
|
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ private newtype TNode =
|
|||||||
not c.getTarget().getParameter(i).getUnderlyingType().(PointerType).getBaseType().isConst()
|
not c.getTarget().getParameter(i).getUnderlyingType().(PointerType).getBaseType().isConst()
|
||||||
)
|
)
|
||||||
} or
|
} or
|
||||||
TUninitializedNode(LocalVariable v) { not v.hasInitializer() }
|
TUninitializedNode(LocalVariable v) { not v.hasInitializer() } or
|
||||||
|
TRefParameterFinalValueNode(Parameter p) { exists(FlowVar var | var.reachesRefParameter(p)) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A node in a data flow graph.
|
* A node in a data flow graph.
|
||||||
@@ -248,6 +249,23 @@ class UninitializedNode extends Node, TUninitializedNode {
|
|||||||
LocalVariable getLocalVariable() { result = v }
|
LocalVariable getLocalVariable() { result = v }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** INTERNAL: do not use. The final value of a non-const ref parameter. */
|
||||||
|
class RefParameterFinalValueNode extends Node, TRefParameterFinalValueNode {
|
||||||
|
Parameter p;
|
||||||
|
|
||||||
|
RefParameterFinalValueNode() { this = TRefParameterFinalValueNode(p) }
|
||||||
|
|
||||||
|
override Function getFunction() { result = p.getFunction() }
|
||||||
|
|
||||||
|
override Type getType() { result = p.getType() }
|
||||||
|
|
||||||
|
override string toString() { result = p.toString() }
|
||||||
|
|
||||||
|
override Location getLocation() { result = p.getLocation() }
|
||||||
|
|
||||||
|
Parameter getParameter() { result = p }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A node associated with an object after an operation that might have
|
* A node associated with an object after an operation that might have
|
||||||
* changed its state.
|
* changed its state.
|
||||||
@@ -490,7 +508,7 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
|||||||
or
|
or
|
||||||
var.definedPartiallyAt(nodeFrom.asPartialDefinition())
|
var.definedPartiallyAt(nodeFrom.asPartialDefinition())
|
||||||
) and
|
) and
|
||||||
varToExprStep(var, nodeTo.asExpr())
|
varToNodeStep(var, nodeTo)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// Expr -> DefinitionByReferenceNode
|
// Expr -> DefinitionByReferenceNode
|
||||||
@@ -533,9 +551,13 @@ private predicate exprToVarStep(Expr assignedExpr, FlowVar var) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if the expression `e` is an access of the variable `var`.
|
* Holds if the node `n` is an access of the variable `var`.
|
||||||
*/
|
*/
|
||||||
private predicate varToExprStep(FlowVar var, Expr e) { e = var.getAnAccess() }
|
private predicate varToNodeStep(FlowVar var, Node n) {
|
||||||
|
n.asExpr() = var.getAnAccess()
|
||||||
|
or
|
||||||
|
var.reachesRefParameter(n.(RefParameterFinalValueNode).getParameter())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if data flows from `fromExpr` to `toExpr` directly, in the case
|
* Holds if data flows from `fromExpr` to `toExpr` directly, in the case
|
||||||
@@ -578,8 +600,8 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) {
|
|||||||
exists(DataFlowFunction f, FunctionInput inModel, FunctionOutput outModel, int iIn |
|
exists(DataFlowFunction f, FunctionInput inModel, FunctionOutput outModel, int iIn |
|
||||||
call.getTarget() = f and
|
call.getTarget() = f and
|
||||||
f.hasDataFlow(inModel, outModel) and
|
f.hasDataFlow(inModel, outModel) and
|
||||||
outModel.isOutReturnValue() and
|
outModel.isReturnValue() and
|
||||||
inModel.isInParameter(iIn) and
|
inModel.isParameter(iIn) and
|
||||||
fromExpr = call.getArgument(iIn)
|
fromExpr = call.getArgument(iIn)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -589,12 +611,12 @@ private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
|
|||||||
exists(DataFlowFunction f, Call call, FunctionOutput outModel, int argOutIndex |
|
exists(DataFlowFunction f, Call call, FunctionOutput outModel, int argOutIndex |
|
||||||
call.getTarget() = f and
|
call.getTarget() = f and
|
||||||
argOut = call.getArgument(argOutIndex) and
|
argOut = call.getArgument(argOutIndex) and
|
||||||
outModel.isOutParameterPointer(argOutIndex) and
|
outModel.isParameterDeref(argOutIndex) and
|
||||||
exists(int argInIndex, FunctionInput inModel | f.hasDataFlow(inModel, outModel) |
|
exists(int argInIndex, FunctionInput inModel | f.hasDataFlow(inModel, outModel) |
|
||||||
inModel.isInParameterPointer(argInIndex) and
|
inModel.isParameterDeref(argInIndex) and
|
||||||
call.passesByReference(argInIndex, exprIn)
|
call.passesByReference(argInIndex, exprIn)
|
||||||
or
|
or
|
||||||
inModel.isInParameter(argInIndex) and
|
inModel.isParameter(argInIndex) and
|
||||||
exprIn = call.getArgument(argInIndex)
|
exprIn = call.getArgument(argInIndex)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -62,9 +62,20 @@ class FlowVar extends TFlowVar {
|
|||||||
cached
|
cached
|
||||||
abstract predicate definedByReference(Expr arg);
|
abstract predicate definedByReference(Expr arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this `FlowVar` is a `PartialDefinition` whose defined expression
|
||||||
|
* is `e`.
|
||||||
|
*/
|
||||||
cached
|
cached
|
||||||
abstract predicate definedPartiallyAt(Expr e);
|
abstract predicate definedPartiallyAt(Expr e);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this `FlowVar` is a definition of a reference parameter `p` that
|
||||||
|
* persists until the function returns.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
abstract predicate reachesRefParameter(Parameter p);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this `FlowVar` corresponds to the initial value of `v`. The following
|
* Holds if this `FlowVar` corresponds to the initial value of `v`. The following
|
||||||
* is an exhaustive list of cases where this may happen.
|
* is an exhaustive list of cases where this may happen.
|
||||||
@@ -338,6 +349,9 @@ module FlowVar_internal {
|
|||||||
param = v
|
param = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `fullySupportedSsaVariable` excludes reference types
|
||||||
|
override predicate reachesRefParameter(Parameter p) { none() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this `SsaVar` corresponds to a non-phi definition. Users of this
|
* Holds if this `SsaVar` corresponds to a non-phi definition. Users of this
|
||||||
* library will never directly use an `SsaVar` that comes from a phi node,
|
* library will never directly use an `SsaVar` that comes from a phi node,
|
||||||
@@ -387,6 +401,13 @@ module FlowVar_internal {
|
|||||||
sbb = v.(Parameter).getFunction().getEntryPoint()
|
sbb = v.(Parameter).getFunction().getEntryPoint()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override predicate reachesRefParameter(Parameter p) {
|
||||||
|
parameterIsNonConstReference(p) and
|
||||||
|
p = v and
|
||||||
|
// This definition reaches the exit node of the function CFG
|
||||||
|
getAReachedBlockVarSBB(this).getANode() = p.getFunction()
|
||||||
|
}
|
||||||
|
|
||||||
override predicate definedByInitialValue(LocalScopeVariable lsv) {
|
override predicate definedByInitialValue(LocalScopeVariable lsv) {
|
||||||
blockVarDefinedByVariable(sbb, lsv) and
|
blockVarDefinedByVariable(sbb, lsv) and
|
||||||
lsv = v
|
lsv = v
|
||||||
@@ -490,7 +511,7 @@ module FlowVar_internal {
|
|||||||
exists(VariableAccess va |
|
exists(VariableAccess va |
|
||||||
va.getTarget() = result and
|
va.getTarget() = result and
|
||||||
readAccess(va) and
|
readAccess(va) and
|
||||||
bbNotInLoop(va.getBasicBlock())
|
exists(BasicBlock bb | bb = va.getBasicBlock() | not this.bbInLoop(bb))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -513,10 +534,8 @@ module FlowVar_internal {
|
|||||||
bbInLoopCondition(bb)
|
bbInLoopCondition(bb)
|
||||||
}
|
}
|
||||||
|
|
||||||
predicate bbNotInLoop(BasicBlock bb) {
|
/** Holds if `sbb` is inside this loop. */
|
||||||
not this.bbInLoop(bb) and
|
predicate sbbInLoop(SubBasicBlock sbb) { this.bbInLoop(sbb.getBasicBlock()) }
|
||||||
bb.getEnclosingFunction() = this.getEnclosingFunction()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `bb` is a basic block inside this loop where `v` has not been
|
* Holds if `bb` is a basic block inside this loop where `v` has not been
|
||||||
@@ -537,22 +556,19 @@ module FlowVar_internal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if some loop always assigns to `v` before leaving through an edge
|
* Holds if `loop` always assigns to `v` before leaving through an edge
|
||||||
* from `bbInside` in its condition to `bbOutside` outside the loop, where
|
* from `bbInside` in its condition to `bbOutside` outside the loop. Also,
|
||||||
* (`sbbDef`, `v`) is a `BlockVar` defined outside the loop. Also, `v` must
|
* `v` must be used outside the loop.
|
||||||
* be used outside the loop.
|
|
||||||
*/
|
*/
|
||||||
predicate skipLoop(
|
predicate skipLoop(
|
||||||
SubBasicBlock sbbInside, SubBasicBlock sbbOutside, SubBasicBlock sbbDef, Variable v
|
SubBasicBlock sbbInside, SubBasicBlock sbbOutside, Variable v, AlwaysTrueUponEntryLoop loop
|
||||||
) {
|
) {
|
||||||
exists(AlwaysTrueUponEntryLoop loop, BasicBlock bbInside, BasicBlock bbOutside |
|
exists(BasicBlock bbInside, BasicBlock bbOutside |
|
||||||
loop.alwaysAssignsBeforeLeavingCondition(bbInside, bbOutside, v) and
|
loop.alwaysAssignsBeforeLeavingCondition(bbInside, bbOutside, v) and
|
||||||
bbInside = sbbInside.getBasicBlock() and
|
bbInside = sbbInside.getBasicBlock() and
|
||||||
bbOutside = sbbOutside.getBasicBlock() and
|
bbOutside = sbbOutside.getBasicBlock() and
|
||||||
sbbInside.lastInBB() and
|
sbbInside.lastInBB() and
|
||||||
sbbOutside.firstInBB() and
|
sbbOutside.firstInBB()
|
||||||
loop.bbNotInLoop(sbbDef.getBasicBlock()) and
|
|
||||||
exists(TBlockVar(sbbDef, v))
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -571,7 +587,7 @@ module FlowVar_internal {
|
|||||||
start = TBlockVar(sbbDef, v) and
|
start = TBlockVar(sbbDef, v) and
|
||||||
result = mid.getASuccessor() and
|
result = mid.getASuccessor() and
|
||||||
variableLiveInSBB(result, v) and
|
variableLiveInSBB(result, v) and
|
||||||
not skipLoop(mid, result, sbbDef, v) and
|
forall(AlwaysTrueUponEntryLoop loop | skipLoop(mid, result, v, loop) | loop.sbbInLoop(sbbDef)) and
|
||||||
not assignmentLikeOperation(result, v, _, _)
|
not assignmentLikeOperation(result, v, _, _)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -593,12 +609,23 @@ module FlowVar_internal {
|
|||||||
private predicate variableLiveInSBB(SubBasicBlock sbb, Variable v) {
|
private predicate variableLiveInSBB(SubBasicBlock sbb, Variable v) {
|
||||||
variableAccessInSBB(v, sbb, _)
|
variableAccessInSBB(v, sbb, _)
|
||||||
or
|
or
|
||||||
|
// Non-const reference parameters are live at the end of the function
|
||||||
|
parameterIsNonConstReference(v) and
|
||||||
|
sbb.contains(v.(Parameter).getFunction())
|
||||||
|
or
|
||||||
exists(SubBasicBlock succ | succ = sbb.getASuccessor() |
|
exists(SubBasicBlock succ | succ = sbb.getASuccessor() |
|
||||||
variableLiveInSBB(succ, v) and
|
variableLiveInSBB(succ, v) and
|
||||||
not variableNotLiveBefore(succ, v)
|
not variableNotLiveBefore(succ, v)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
predicate parameterIsNonConstReference(Parameter p) {
|
||||||
|
exists(ReferenceType refType |
|
||||||
|
refType = p.getUnderlyingType() and
|
||||||
|
not refType.getBaseType().isConst()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if liveness of `v` should stop propagating backwards from `sbb`.
|
* Holds if liveness of `v` should stop propagating backwards from `sbb`.
|
||||||
*/
|
*/
|
||||||
@@ -679,10 +706,11 @@ module FlowVar_internal {
|
|||||||
predicate dominatedByOverwrite(UninitializedLocalVariable v, VariableAccess va) {
|
predicate dominatedByOverwrite(UninitializedLocalVariable v, VariableAccess va) {
|
||||||
exists(BasicBlock bb, int vaIndex |
|
exists(BasicBlock bb, int vaIndex |
|
||||||
va = bb.getNode(vaIndex) and
|
va = bb.getNode(vaIndex) and
|
||||||
va.getTarget() = v
|
va.getTarget() = v and
|
||||||
|
|
|
||||||
vaIndex > indexOfFirstOverwriteInBB(v, bb)
|
vaIndex > indexOfFirstOverwriteInBB(v, bb)
|
||||||
or
|
or
|
||||||
|
va = bb.getNode(vaIndex) and
|
||||||
|
va.getTarget() = v and
|
||||||
bbStrictlyDominates(getAnOverwritingBB(v), bb)
|
bbStrictlyDominates(getAnOverwritingBB(v), bb)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,11 +122,11 @@ private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
|
|||||||
exists(DataFlowFunction f, Call call, FunctionOutput outModel, int argOutIndex |
|
exists(DataFlowFunction f, Call call, FunctionOutput outModel, int argOutIndex |
|
||||||
call.getTarget() = f and
|
call.getTarget() = f and
|
||||||
argOut = call.getArgument(argOutIndex) and
|
argOut = call.getArgument(argOutIndex) and
|
||||||
outModel.isOutParameterPointer(argOutIndex) and
|
outModel.isParameterDeref(argOutIndex) and
|
||||||
exists(int argInIndex, FunctionInput inModel | f.hasDataFlow(inModel, outModel) |
|
exists(int argInIndex, FunctionInput inModel | f.hasDataFlow(inModel, outModel) |
|
||||||
// Taint flows from a pointer to a dereference, which DataFlow does not handle
|
// Taint flows from a pointer to a dereference, which DataFlow does not handle
|
||||||
// memcpy(&dest_var, tainted_ptr, len)
|
// memcpy(&dest_var, tainted_ptr, len)
|
||||||
inModel.isInParameterPointer(argInIndex) and
|
inModel.isParameterDeref(argInIndex) and
|
||||||
exprIn = call.getArgument(argInIndex)
|
exprIn = call.getArgument(argInIndex)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -134,15 +134,15 @@ private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
|
|||||||
exists(TaintFunction f, Call call, FunctionOutput outModel, int argOutIndex |
|
exists(TaintFunction f, Call call, FunctionOutput outModel, int argOutIndex |
|
||||||
call.getTarget() = f and
|
call.getTarget() = f and
|
||||||
argOut = call.getArgument(argOutIndex) and
|
argOut = call.getArgument(argOutIndex) and
|
||||||
outModel.isOutParameterPointer(argOutIndex) and
|
outModel.isParameterDeref(argOutIndex) and
|
||||||
exists(int argInIndex, FunctionInput inModel | f.hasTaintFlow(inModel, outModel) |
|
exists(int argInIndex, FunctionInput inModel | f.hasTaintFlow(inModel, outModel) |
|
||||||
inModel.isInParameterPointer(argInIndex) and
|
inModel.isParameterDeref(argInIndex) and
|
||||||
exprIn = call.getArgument(argInIndex)
|
exprIn = call.getArgument(argInIndex)
|
||||||
or
|
or
|
||||||
inModel.isInParameterPointer(argInIndex) and
|
inModel.isParameterDeref(argInIndex) and
|
||||||
call.passesByReference(argInIndex, exprIn)
|
call.passesByReference(argInIndex, exprIn)
|
||||||
or
|
or
|
||||||
inModel.isInParameter(argInIndex) and
|
inModel.isParameter(argInIndex) and
|
||||||
exprIn = call.getArgument(argInIndex)
|
exprIn = call.getArgument(argInIndex)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -18,7 +18,18 @@ abstract class Access extends Expr, NameQualifiableElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A C/C++ enum constant access expression.
|
* A C/C++ `enum` constant access expression. For example the access to
|
||||||
|
* `MYENUMCONST1` in `myFunction` in the following code:
|
||||||
|
* ```
|
||||||
|
* enum MyEnum {
|
||||||
|
* MYENUMCONST1,
|
||||||
|
* MYENUMCONST2
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* void myFunction() {
|
||||||
|
* MyEnum v = MYENUMCONST1;
|
||||||
|
* };
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
class EnumConstantAccess extends Access, @varaccess {
|
class EnumConstantAccess extends Access, @varaccess {
|
||||||
override string getCanonicalQLClass() { result = "EnumConstantAccess" }
|
override string getCanonicalQLClass() { result = "EnumConstantAccess" }
|
||||||
@@ -27,15 +38,23 @@ class EnumConstantAccess extends Access, @varaccess {
|
|||||||
exists(EnumConstant c | varbind(underlyingElement(this), unresolveElement(c)))
|
exists(EnumConstant c | varbind(underlyingElement(this), unresolveElement(c)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the accessed enum constant. */
|
/** Gets the accessed `enum` constant. */
|
||||||
override EnumConstant getTarget() { varbind(underlyingElement(this), unresolveElement(result)) }
|
override EnumConstant getTarget() { varbind(underlyingElement(this), unresolveElement(result)) }
|
||||||
|
|
||||||
/** Gets a textual representation of this enum constant access. */
|
/** Gets a textual representation of this `enum` constant access. */
|
||||||
override string toString() { result = this.getTarget().getName() }
|
override string toString() { result = this.getTarget().getName() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A C/C++ variable access expression.
|
* A C/C++ variable access expression. For example the accesses to
|
||||||
|
* `x` and `y` in `myFunction` in the following code:
|
||||||
|
* ```
|
||||||
|
* int x;
|
||||||
|
*
|
||||||
|
* void myFunction(int y) {
|
||||||
|
* x = y;
|
||||||
|
* };
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
class VariableAccess extends Access, @varaccess {
|
class VariableAccess extends Access, @varaccess {
|
||||||
override string getCanonicalQLClass() { result = "VariableAccess" }
|
override string getCanonicalQLClass() { result = "VariableAccess" }
|
||||||
@@ -129,7 +148,18 @@ class VariableAccess extends Access, @varaccess {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A C/C++ field access expression.
|
* A C/C++ field access expression. For example the accesses to
|
||||||
|
* `x` and `y` in `myMethod` in the following code:
|
||||||
|
* ```
|
||||||
|
* class MyClass {
|
||||||
|
* public:
|
||||||
|
* void myMethod(MyClass &other) {
|
||||||
|
* x = other.y;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* int x, y;
|
||||||
|
* };
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
class FieldAccess extends VariableAccess {
|
class FieldAccess extends VariableAccess {
|
||||||
override string getCanonicalQLClass() { result = "FieldAccess" }
|
override string getCanonicalQLClass() { result = "FieldAccess" }
|
||||||
@@ -141,8 +171,23 @@ class FieldAccess extends VariableAccess {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A field access of the form `obj->field`. The type of `obj` is a pointer,
|
* A field access whose qualifier is a pointer to a class, struct or union.
|
||||||
* so this is equivalent to `(*obj).field`.
|
* These typically take the form `obj->field`. Another case is a field access
|
||||||
|
* with an implicit `this->` qualifier, which is often a `PointerFieldAccess`
|
||||||
|
* (but see also `ImplicitThisFieldAccess`).
|
||||||
|
*
|
||||||
|
* For example the accesses to `x` and `y` in `myMethod` in the following code
|
||||||
|
* are each a `PointerFieldAccess`:
|
||||||
|
* ```
|
||||||
|
* class MyClass {
|
||||||
|
* public:
|
||||||
|
* void myMethod(MyClass *other) {
|
||||||
|
* other->x = y;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* int x, y;
|
||||||
|
* };
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
class PointerFieldAccess extends FieldAccess {
|
class PointerFieldAccess extends FieldAccess {
|
||||||
override string getCanonicalQLClass() { result = "PointerFieldAccess" }
|
override string getCanonicalQLClass() { result = "PointerFieldAccess" }
|
||||||
@@ -169,7 +214,18 @@ class DotFieldAccess extends FieldAccess {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A field access of the form `obj.field`, where the type of `obj` is a
|
* A field access of the form `obj.field`, where the type of `obj` is a
|
||||||
* reference to a class/struct/union.
|
* reference to a class/struct/union. For example the accesses to `y` in
|
||||||
|
* `myMethod` in the following code:
|
||||||
|
* ```
|
||||||
|
* class MyClass {
|
||||||
|
* public:
|
||||||
|
* void myMethod(MyClass a, MyClass &b) {
|
||||||
|
* a.x = b.y;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* int x, y;
|
||||||
|
* };
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
class ReferenceFieldAccess extends DotFieldAccess {
|
class ReferenceFieldAccess extends DotFieldAccess {
|
||||||
override string getCanonicalQLClass() { result = "ReferenceFieldAccess" }
|
override string getCanonicalQLClass() { result = "ReferenceFieldAccess" }
|
||||||
@@ -179,7 +235,18 @@ class ReferenceFieldAccess extends DotFieldAccess {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A field access of the form `obj.field`, where the type of `obj` is a
|
* A field access of the form `obj.field`, where the type of `obj` is a
|
||||||
* class/struct/union (and not a reference).
|
* class/struct/union (and not a reference). For example the accesses to `x`
|
||||||
|
* in `myMethod` in the following code:
|
||||||
|
* ```
|
||||||
|
* class MyClass {
|
||||||
|
* public:
|
||||||
|
* void myMethod(MyClass a, MyClass &b) {
|
||||||
|
* a.x = b.y;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* int x, y;
|
||||||
|
* };
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
class ValueFieldAccess extends DotFieldAccess {
|
class ValueFieldAccess extends DotFieldAccess {
|
||||||
override string getCanonicalQLClass() { result = "ValueFieldAccess" }
|
override string getCanonicalQLClass() { result = "ValueFieldAccess" }
|
||||||
@@ -198,25 +265,40 @@ private predicate referenceConversion(Conversion c) {
|
|||||||
/**
|
/**
|
||||||
* Holds if `e` is a reference expression (that is, it has a type of the
|
* Holds if `e` is a reference expression (that is, it has a type of the
|
||||||
* form `T&`), which is converted to a value. For example:
|
* form `T&`), which is converted to a value. For example:
|
||||||
*
|
|
||||||
* ```
|
* ```
|
||||||
* int myfcn(MyStruct &x) {
|
* int myfcn(MyStruct &x) {
|
||||||
* return x.field;
|
* return x.field;
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*
|
|
||||||
* In this example, the type of `x` is `MyStruct&`, but it gets implicitly
|
* In this example, the type of `x` is `MyStruct&`, but it gets implicitly
|
||||||
* converted to `MyStruct` in the expression `x.field`.
|
* converted to `MyStruct` in the expression `x.field`.
|
||||||
*/
|
*/
|
||||||
private predicate exprHasReferenceConversion(Expr e) { referenceConversion(e.getConversion+()) }
|
private predicate exprHasReferenceConversion(Expr e) { referenceConversion(e.getConversion+()) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A field access of a field of `this`. The access has no qualifier because
|
* A field access of a field of `this` which has no qualifier because
|
||||||
* the use of `this` is implicit. For example, `field` is equivalent to
|
* the use of `this` is implicit. For example, in the following code the
|
||||||
* `this->field` if `field` is a member of `this`.
|
* implicit call to the destructor of `A` has no qualifier because the
|
||||||
|
* use of `this` is implicit:
|
||||||
|
* ```
|
||||||
|
* class A {
|
||||||
|
* public:
|
||||||
|
* ~A() {
|
||||||
|
* // ...
|
||||||
|
* }
|
||||||
|
* };
|
||||||
*
|
*
|
||||||
|
* class B {
|
||||||
|
* public:
|
||||||
|
* A a;
|
||||||
|
*
|
||||||
|
* ~B() {
|
||||||
|
* // Implicit call to the destructor of `A`.
|
||||||
|
* }
|
||||||
|
* };
|
||||||
|
* ```
|
||||||
* Note: the C++ front-end often automatically desugars `field` to
|
* Note: the C++ front-end often automatically desugars `field` to
|
||||||
* `this->field`, so most implicit accesses of `this->field` are instances
|
* `this->field`, so most accesses of `this->field` are instances
|
||||||
* of `PointerFieldAccess` (with `ThisExpr` as the qualifier), not
|
* of `PointerFieldAccess` (with `ThisExpr` as the qualifier), not
|
||||||
* `ImplicitThisFieldAccess`.
|
* `ImplicitThisFieldAccess`.
|
||||||
*/
|
*/
|
||||||
@@ -250,7 +332,15 @@ class PointerToFieldLiteral extends ImplicitThisFieldAccess {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A C/C++ function access expression.
|
* A C/C++ function access expression. For example the access to
|
||||||
|
* `myFunctionTarget` in `myFunction` in the following code:
|
||||||
|
* ```
|
||||||
|
* int myFunctionTarget(int);
|
||||||
|
*
|
||||||
|
* void myFunction() {
|
||||||
|
* int (*myFunctionPointer)(int) = &myTarget;
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
class FunctionAccess extends Access, @routineexpr {
|
class FunctionAccess extends Access, @routineexpr {
|
||||||
FunctionAccess() { not iscall(underlyingElement(this), _) }
|
FunctionAccess() { not iscall(underlyingElement(this), _) }
|
||||||
@@ -269,7 +359,7 @@ class FunctionAccess extends Access, @routineexpr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An access to a parameter of a function signature for the purposes of a decltype.
|
* An access to a parameter of a function signature for the purposes of a `decltype`.
|
||||||
*
|
*
|
||||||
* For example, given the following code:
|
* For example, given the following code:
|
||||||
* ```
|
* ```
|
||||||
@@ -279,7 +369,7 @@ class FunctionAccess extends Access, @routineexpr {
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
* The return type of the function is a decltype, the expression of which contains
|
* The return type of the function is a decltype, the expression of which contains
|
||||||
* an add expression, which in turn has two ParamAccessForType children.
|
* an add expression, which in turn has two `ParamAccessForType` children.
|
||||||
*/
|
*/
|
||||||
class ParamAccessForType extends Expr, @param_ref {
|
class ParamAccessForType extends Expr, @param_ref {
|
||||||
override string toString() { result = "param access" }
|
override string toString() { result = "param access" }
|
||||||
@@ -287,7 +377,22 @@ class ParamAccessForType extends Expr, @param_ref {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* An access to a type. This occurs in certain contexts where a built-in
|
* An access to a type. This occurs in certain contexts where a built-in
|
||||||
* works on types directly rather than variables, expressions etc.
|
* works on types directly rather than variables, expressions etc. For
|
||||||
|
* example the reference to `MyClass` in `__is_pod` in the following code:
|
||||||
|
* ```
|
||||||
|
* class MyClass {
|
||||||
|
* ...
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* void myFunction() {
|
||||||
|
* if (__is_pod(MyClass))
|
||||||
|
* {
|
||||||
|
* ...
|
||||||
|
* } else {
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
class TypeName extends Expr, @type_operand {
|
class TypeName extends Expr, @type_operand {
|
||||||
override string getCanonicalQLClass() { result = "TypeName" }
|
override string getCanonicalQLClass() { result = "TypeName" }
|
||||||
@@ -296,9 +401,17 @@ class TypeName extends Expr, @type_operand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A C/C++ array access expression.
|
* A C/C++ array access expression. For example, the access to `as` in
|
||||||
|
* `myFunction` in the following code:
|
||||||
|
* ```
|
||||||
|
* int as[10];
|
||||||
*
|
*
|
||||||
* For calls to operator[], which look syntactically identical, see OverloadedArrayExpr.
|
* void myFunction() {
|
||||||
|
* as[0]++;
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
* For calls to `operator[]`, which look syntactically identical, see
|
||||||
|
* `OverloadedArrayExpr`.
|
||||||
*/
|
*/
|
||||||
class ArrayExpr extends Expr, @subscriptexpr {
|
class ArrayExpr extends Expr, @subscriptexpr {
|
||||||
override string getCanonicalQLClass() { result = "ArrayExpr" }
|
override string getCanonicalQLClass() { result = "ArrayExpr" }
|
||||||
@@ -306,14 +419,14 @@ class ArrayExpr extends Expr, @subscriptexpr {
|
|||||||
/**
|
/**
|
||||||
* Gets the array or pointer expression being subscripted.
|
* Gets the array or pointer expression being subscripted.
|
||||||
*
|
*
|
||||||
* This is arr in both arr[0] and 0[arr].
|
* This is `arr` in both `arr[0]` and `0[arr]`.
|
||||||
*/
|
*/
|
||||||
Expr getArrayBase() { result = this.getChild(0) }
|
Expr getArrayBase() { result = this.getChild(0) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the expression giving the index into the array.
|
* Gets the expression giving the index into the array.
|
||||||
*
|
*
|
||||||
* This is 0 in both arr[0] and 0[arr].
|
* This is `0` in both `arr[0]` and `0[arr]`.
|
||||||
*/
|
*/
|
||||||
Expr getArrayOffset() { result = this.getChild(1) }
|
Expr getArrayOffset() { result = this.getChild(1) }
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ class UnaryPlusExpr extends UnaryArithmeticOperation, @unaryplusexpr {
|
|||||||
*/
|
*/
|
||||||
class ConjugationExpr extends UnaryArithmeticOperation, @conjugation {
|
class ConjugationExpr extends UnaryArithmeticOperation, @conjugation {
|
||||||
override string getOperator() { result = "~" }
|
override string getOperator() { result = "~" }
|
||||||
|
|
||||||
|
override string getCanonicalQLClass() { result = "ConjugationExpr" }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -142,6 +144,8 @@ class PostfixDecrExpr extends DecrementOperation, PostfixCrementOperation, @post
|
|||||||
*/
|
*/
|
||||||
class RealPartExpr extends UnaryArithmeticOperation, @realpartexpr {
|
class RealPartExpr extends UnaryArithmeticOperation, @realpartexpr {
|
||||||
override string getOperator() { result = "__real" }
|
override string getOperator() { result = "__real" }
|
||||||
|
|
||||||
|
override string getCanonicalQLClass() { result = "RealPartExpr" }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -149,6 +153,8 @@ class RealPartExpr extends UnaryArithmeticOperation, @realpartexpr {
|
|||||||
*/
|
*/
|
||||||
class ImaginaryPartExpr extends UnaryArithmeticOperation, @imagpartexpr {
|
class ImaginaryPartExpr extends UnaryArithmeticOperation, @imagpartexpr {
|
||||||
override string getOperator() { result = "__imag" }
|
override string getOperator() { result = "__imag" }
|
||||||
|
|
||||||
|
override string getCanonicalQLClass() { result = "ImaginaryPartExpr" }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -217,6 +223,8 @@ class RemExpr extends BinaryArithmeticOperation, @remexpr {
|
|||||||
class ImaginaryMulExpr extends BinaryArithmeticOperation, @jmulexpr {
|
class ImaginaryMulExpr extends BinaryArithmeticOperation, @jmulexpr {
|
||||||
override string getOperator() { result = "*" }
|
override string getOperator() { result = "*" }
|
||||||
|
|
||||||
|
override string getCanonicalQLClass() { result = "ImaginaryMulExpr" }
|
||||||
|
|
||||||
override int getPrecedence() { result = 13 }
|
override int getPrecedence() { result = 13 }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,6 +234,8 @@ class ImaginaryMulExpr extends BinaryArithmeticOperation, @jmulexpr {
|
|||||||
class ImaginaryDivExpr extends BinaryArithmeticOperation, @jdivexpr {
|
class ImaginaryDivExpr extends BinaryArithmeticOperation, @jdivexpr {
|
||||||
override string getOperator() { result = "/" }
|
override string getOperator() { result = "/" }
|
||||||
|
|
||||||
|
override string getCanonicalQLClass() { result = "ImaginaryDivExpr" }
|
||||||
|
|
||||||
override int getPrecedence() { result = 13 }
|
override int getPrecedence() { result = 13 }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,6 +245,8 @@ class ImaginaryDivExpr extends BinaryArithmeticOperation, @jdivexpr {
|
|||||||
class RealImaginaryAddExpr extends BinaryArithmeticOperation, @fjaddexpr {
|
class RealImaginaryAddExpr extends BinaryArithmeticOperation, @fjaddexpr {
|
||||||
override string getOperator() { result = "+" }
|
override string getOperator() { result = "+" }
|
||||||
|
|
||||||
|
override string getCanonicalQLClass() { result = "RealImaginaryAddExpr" }
|
||||||
|
|
||||||
override int getPrecedence() { result = 12 }
|
override int getPrecedence() { result = 12 }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,6 +256,8 @@ class RealImaginaryAddExpr extends BinaryArithmeticOperation, @fjaddexpr {
|
|||||||
class ImaginaryRealAddExpr extends BinaryArithmeticOperation, @jfaddexpr {
|
class ImaginaryRealAddExpr extends BinaryArithmeticOperation, @jfaddexpr {
|
||||||
override string getOperator() { result = "+" }
|
override string getOperator() { result = "+" }
|
||||||
|
|
||||||
|
override string getCanonicalQLClass() { result = "ImaginaryRealAddExpr" }
|
||||||
|
|
||||||
override int getPrecedence() { result = 12 }
|
override int getPrecedence() { result = 12 }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,6 +267,8 @@ class ImaginaryRealAddExpr extends BinaryArithmeticOperation, @jfaddexpr {
|
|||||||
class RealImaginarySubExpr extends BinaryArithmeticOperation, @fjsubexpr {
|
class RealImaginarySubExpr extends BinaryArithmeticOperation, @fjsubexpr {
|
||||||
override string getOperator() { result = "-" }
|
override string getOperator() { result = "-" }
|
||||||
|
|
||||||
|
override string getCanonicalQLClass() { result = "RealImaginarySubExpr" }
|
||||||
|
|
||||||
override int getPrecedence() { result = 12 }
|
override int getPrecedence() { result = 12 }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,6 +278,8 @@ class RealImaginarySubExpr extends BinaryArithmeticOperation, @fjsubexpr {
|
|||||||
class ImaginaryRealSubExpr extends BinaryArithmeticOperation, @jfsubexpr {
|
class ImaginaryRealSubExpr extends BinaryArithmeticOperation, @jfsubexpr {
|
||||||
override string getOperator() { result = "-" }
|
override string getOperator() { result = "-" }
|
||||||
|
|
||||||
|
override string getCanonicalQLClass() { result = "ImaginaryRealSubExpr" }
|
||||||
|
|
||||||
override int getPrecedence() { result = 12 }
|
override int getPrecedence() { result = 12 }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,6 +288,8 @@ class ImaginaryRealSubExpr extends BinaryArithmeticOperation, @jfsubexpr {
|
|||||||
*/
|
*/
|
||||||
class MinExpr extends BinaryArithmeticOperation, @minexpr {
|
class MinExpr extends BinaryArithmeticOperation, @minexpr {
|
||||||
override string getOperator() { result = "<?" }
|
override string getOperator() { result = "<?" }
|
||||||
|
|
||||||
|
override string getCanonicalQLClass() { result = "MinExpr" }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -277,6 +297,8 @@ class MinExpr extends BinaryArithmeticOperation, @minexpr {
|
|||||||
*/
|
*/
|
||||||
class MaxExpr extends BinaryArithmeticOperation, @maxexpr {
|
class MaxExpr extends BinaryArithmeticOperation, @maxexpr {
|
||||||
override string getOperator() { result = ">?" }
|
override string getOperator() { result = ">?" }
|
||||||
|
|
||||||
|
override string getCanonicalQLClass() { result = "MaxExpr" }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -519,3 +519,18 @@ class BuiltInChooseExpr extends BuiltInOperation, @builtinchooseexpr {
|
|||||||
class VectorFillOperation extends UnaryOperation, @vec_fill {
|
class VectorFillOperation extends UnaryOperation, @vec_fill {
|
||||||
override string getOperator() { result = "(vector fill)" }
|
override string getOperator() { result = "(vector fill)" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The GNU `__builtin_complex` operation.
|
||||||
|
*/
|
||||||
|
class BuiltInComplexOperation extends BuiltInOperation, @builtincomplex {
|
||||||
|
override string toString() { result = "__builtin_complex" }
|
||||||
|
|
||||||
|
override string getCanonicalQLClass() { result = "BuiltInComplexOperation" }
|
||||||
|
|
||||||
|
/** Gets the operand corresponding to the real part of the complex number. */
|
||||||
|
Expr getRealOperand() { this.hasChild(result, 0) }
|
||||||
|
|
||||||
|
/** Gets the operand corresponding to the imaginary part of the complex number. */
|
||||||
|
Expr getImaginaryOperand() { this.hasChild(result, 1) }
|
||||||
|
}
|
||||||
|
|||||||
@@ -131,6 +131,8 @@ class Expr extends StmtParent, @expr {
|
|||||||
valuebind(_, underlyingElement(this))
|
valuebind(_, underlyingElement(this))
|
||||||
or
|
or
|
||||||
addressConstantExpression(this)
|
addressConstantExpression(this)
|
||||||
|
or
|
||||||
|
constantTemplateLiteral(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1119,3 +1121,17 @@ private predicate isStandardPlacementNewAllocator(Function operatorNew) {
|
|||||||
|
|
||||||
// Pulled out for performance. See QL-796.
|
// Pulled out for performance. See QL-796.
|
||||||
private predicate hasNoConversions(Expr e) { not e.hasConversion() }
|
private predicate hasNoConversions(Expr e) { not e.hasConversion() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `e` is a literal of unknown value in a template, or a cast thereof.
|
||||||
|
* We assume that such literals are constant.
|
||||||
|
*/
|
||||||
|
private predicate constantTemplateLiteral(Expr e) {
|
||||||
|
// Unknown literals in uninstantiated templates could be enum constant
|
||||||
|
// accesses or pointer-to-member literals.
|
||||||
|
e instanceof Literal and
|
||||||
|
e.isFromUninstantiatedTemplate(_) and
|
||||||
|
not exists(e.getValue())
|
||||||
|
or
|
||||||
|
constantTemplateLiteral(e.(Cast).getExpr())
|
||||||
|
}
|
||||||
|
|||||||
@@ -132,7 +132,6 @@ class HexLiteral extends Literal {
|
|||||||
* A C/C++ aggregate literal.
|
* A C/C++ aggregate literal.
|
||||||
*/
|
*/
|
||||||
class AggregateLiteral extends Expr, @aggregateliteral {
|
class AggregateLiteral extends Expr, @aggregateliteral {
|
||||||
// if this is turned into a Literal we need to change mayBeImpure
|
|
||||||
override string getCanonicalQLClass() { result = "AggregateLiteral" }
|
override string getCanonicalQLClass() { result = "AggregateLiteral" }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -145,6 +144,10 @@ class AggregateLiteral extends Expr, @aggregateliteral {
|
|||||||
result = this.(ClassAggregateLiteral).getFieldExpr(f)
|
result = this.(ClassAggregateLiteral).getFieldExpr(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override predicate mayBeImpure() { this.getAChild().mayBeImpure() }
|
||||||
|
|
||||||
|
override predicate mayBeGloballyImpure() { this.getAChild().mayBeGloballyImpure() }
|
||||||
|
|
||||||
/** Gets a textual representation of this aggregate literal. */
|
/** Gets a textual representation of this aggregate literal. */
|
||||||
override string toString() { result = "{...}" }
|
override string toString() { result = "{...}" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ private predicate constantAddressLValue(Expr lvalue) {
|
|||||||
// tells us how it's going to be used.
|
// tells us how it's going to be used.
|
||||||
lvalue.(FunctionAccess).getType() instanceof RoutineType
|
lvalue.(FunctionAccess).getType() instanceof RoutineType
|
||||||
or
|
or
|
||||||
|
// Pointer-to-member literals in uninstantiated templates
|
||||||
|
lvalue instanceof Literal and
|
||||||
|
not exists(lvalue.getValue()) and
|
||||||
|
lvalue.isFromUninstantiatedTemplate(_)
|
||||||
|
or
|
||||||
// String literals have array types and undergo array-to-pointer conversion.
|
// String literals have array types and undergo array-to-pointer conversion.
|
||||||
lvalue instanceof StringLiteral
|
lvalue instanceof StringLiteral
|
||||||
or
|
or
|
||||||
@@ -61,6 +66,10 @@ private predicate constantAddressPointer(Expr pointer) {
|
|||||||
// tells us how it's going to be used.
|
// tells us how it's going to be used.
|
||||||
pointer.(FunctionAccess).getType() instanceof FunctionPointerType
|
pointer.(FunctionAccess).getType() instanceof FunctionPointerType
|
||||||
or
|
or
|
||||||
|
// Pointer to member function. These accesses are always pointers even though
|
||||||
|
// their type is `RoutineType`.
|
||||||
|
pointer.(FunctionAccess).getTarget() instanceof MemberFunction
|
||||||
|
or
|
||||||
addressConstantVariable(pointer.(VariableAccess).getTarget()) and
|
addressConstantVariable(pointer.(VariableAccess).getTarget()) and
|
||||||
pointer.getType().getUnderlyingType() instanceof PointerType
|
pointer.getType().getUnderlyingType() instanceof PointerType
|
||||||
or
|
or
|
||||||
|
|||||||
@@ -4,25 +4,17 @@ private import semmle.code.cpp.ir.dataflow.DataFlow
|
|||||||
private import semmle.code.cpp.ir.IR
|
private import semmle.code.cpp.ir.IR
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A predictable expression is one where an external user can predict
|
* A predictable instruction is one where an external user can predict
|
||||||
* the value. For example, a literal in the source code is considered
|
* the value. For example, a literal in the source code is considered
|
||||||
* predictable.
|
* predictable.
|
||||||
*/
|
*/
|
||||||
// TODO: Change to use Instruction instead of Expr. Naive attempt breaks
|
|
||||||
// TaintedAllocationSize qltest.
|
|
||||||
private predicate predictable(Expr expr) {
|
|
||||||
expr instanceof Literal
|
|
||||||
or
|
|
||||||
exists(BinaryOperation binop | binop = expr |
|
|
||||||
predictable(binop.getLeftOperand()) and predictable(binop.getRightOperand())
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(UnaryOperation unop | unop = expr | predictable(unop.getOperand()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove when `predictable` has an `Instruction` parameter instead of `Expr`.
|
|
||||||
private predicate predictableInstruction(Instruction instr) {
|
private predicate predictableInstruction(Instruction instr) {
|
||||||
predictable(DataFlow::instructionNode(instr).asExpr())
|
instr instanceof ConstantInstruction
|
||||||
|
or
|
||||||
|
instr instanceof StringConstantInstruction
|
||||||
|
or
|
||||||
|
// This could be a conversion on a string literal
|
||||||
|
predictableInstruction(instr.(UnaryInstruction).getUnary())
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
|
private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* on each other without introducing mutual recursion among those configurations.
|
* on each other without introducing mutual recursion among those configurations.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private import DataFlowImplCommon
|
private import DataFlowImplCommon::Public
|
||||||
private import DataFlowImplSpecific::Private
|
private import DataFlowImplSpecific::Private
|
||||||
import DataFlowImplSpecific::Public
|
import DataFlowImplSpecific::Public
|
||||||
|
|
||||||
@@ -905,8 +905,10 @@ private predicate localFlowExit(Node node, Configuration config) {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate localFlowStepPlus(
|
private predicate localFlowStepPlus(
|
||||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||||
) {
|
) {
|
||||||
|
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||||
|
(
|
||||||
localFlowEntry(node1, config) and
|
localFlowEntry(node1, config) and
|
||||||
(
|
(
|
||||||
localFlowStep(node1, node2, config) and preservesValue = true
|
localFlowStep(node1, node2, config) and preservesValue = true
|
||||||
@@ -914,33 +916,36 @@ private predicate localFlowStepPlus(
|
|||||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||||
) and
|
) and
|
||||||
node1 != node2 and
|
node1 != node2 and
|
||||||
|
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||||
|
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||||
localFlowStep(mid, node2, config) and
|
localFlowStep(mid, node2, config) and
|
||||||
not mid instanceof CastNode and
|
not mid instanceof CastNode and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowStepPlus(node1, mid, _, config) and
|
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||||
additionalLocalFlowStep(mid, node2, config) and
|
additionalLocalFlowStep(mid, node2, config) and
|
||||||
not mid instanceof CastNode and
|
not mid instanceof CastNode and
|
||||||
preservesValue = false and
|
preservesValue = false and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
)
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node1` can step to `node2` in one or more local steps and this
|
* Holds if `node1` can step to `node2` in one or more local steps and this
|
||||||
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
||||||
*/
|
*/
|
||||||
pragma[noinline]
|
pragma[nomagic]
|
||||||
private predicate localFlowBigStep(
|
private predicate localFlowBigStep(
|
||||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
|
||||||
) {
|
) {
|
||||||
localFlowStepPlus(node1, node2, preservesValue, config) and
|
localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
|
||||||
localFlowExit(node2, config)
|
localFlowExit(node2, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
|||||||
(
|
(
|
||||||
any(Configuration c).isSource(this)
|
any(Configuration c).isSource(this)
|
||||||
or
|
or
|
||||||
localFlowBigStep(_, this, false, _)
|
localFlowBigStep(_, this, false, _, _)
|
||||||
or
|
or
|
||||||
additionalJumpStep(_, this, _)
|
additionalJumpStep(_, this, _)
|
||||||
)
|
)
|
||||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
|||||||
(
|
(
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
flowCandFwd(mid, fromArg, apf, config) and
|
flowCandFwd(mid, fromArg, apf, config) and
|
||||||
localFlowBigStep(mid, node, true, config)
|
localFlowBigStep(mid, node, true, config, _)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathFrontNil nil |
|
exists(Node mid, AccessPathFrontNil nil |
|
||||||
flowCandFwd(mid, fromArg, nil, config) and
|
flowCandFwd(mid, fromArg, nil, config) and
|
||||||
localFlowBigStep(mid, node, false, config) and
|
localFlowBigStep(mid, node, false, config, _) and
|
||||||
apf = node.(AccessPathFrontNilNode).getApf()
|
apf = node.(AccessPathFrontNilNode).getApf()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
|||||||
flowCandFwd(mid, fromArg, _, config) and
|
flowCandFwd(mid, fromArg, _, config) and
|
||||||
store(mid, f, node) and
|
store(mid, f, node) and
|
||||||
nodeCand(node, unbind(config)) and
|
nodeCand(node, unbind(config)) and
|
||||||
|
readStoreCand(f, unbind(config)) and
|
||||||
apf.headUsesContent(f)
|
apf.headUsesContent(f)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
|||||||
apf instanceof AccessPathFrontNil
|
apf instanceof AccessPathFrontNil
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowBigStep(node, mid, true, config) and
|
localFlowBigStep(node, mid, true, config, _) and
|
||||||
flowCand(mid, toReturn, apf, config)
|
flowCand(mid, toReturn, apf, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathFrontNil nil |
|
exists(Node mid, AccessPathFrontNil nil |
|
||||||
flowCandFwd(node, _, apf, config) and
|
flowCandFwd(node, _, apf, config) and
|
||||||
localFlowBigStep(node, mid, false, config) and
|
localFlowBigStep(node, mid, false, config, _) and
|
||||||
flowCand(mid, toReturn, nil, config) and
|
flowCand(mid, toReturn, nil, config) and
|
||||||
apf instanceof AccessPathFrontNil
|
apf instanceof AccessPathFrontNil
|
||||||
)
|
)
|
||||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
|||||||
exists(Content f, AccessPathFront apf0 |
|
exists(Content f, AccessPathFront apf0 |
|
||||||
flowCandStore(node, f, toReturn, apf0, config) and
|
flowCandStore(node, f, toReturn, apf0, config) and
|
||||||
apf0.headUsesContent(f) and
|
apf0.headUsesContent(f) and
|
||||||
consCand(f, apf, unbind(config))
|
consCand(f, apf, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Content f, AccessPathFront apf0 |
|
exists(Content f, AccessPathFront apf0 |
|
||||||
flowCandRead(node, f, toReturn, apf0, config) and
|
flowCandRead(node, f, toReturn, apf0, config) and
|
||||||
consCandFwd(f, apf0, unbind(config)) and
|
consCandFwd(f, apf0, config) and
|
||||||
apf.headUsesContent(f)
|
apf.headUsesContent(f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1221,8 +1227,8 @@ private newtype TAccessPath =
|
|||||||
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||||
* element of the list and its length are tracked. If data flows from a source to
|
* elements of the list and its length are tracked. If data flows from a source to
|
||||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||||
* dereference operations needed to get from the value in the node to the
|
* dereference operations needed to get from the value in the node to the
|
||||||
* tracked object. The final type indicates the type of the tracked object.
|
* tracked object. The final type indicates the type of the tracked object.
|
||||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
|||||||
|
|
||||||
private class AccessPathNil extends AccessPath, TNil {
|
private class AccessPathNil extends AccessPath, TNil {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
|
exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override AccessPathFront getFront() {
|
override AccessPathFront getFront() {
|
||||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||||
// The `concat` becomes "" if `ppReprType` has no result.
|
// The `concat` becomes "" if `ppReprType` has no result.
|
||||||
result = f.toString() + concat(" : " + ppReprType(t))
|
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1293,8 +1299,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||||
if len = 2
|
if len = 2
|
||||||
then result = f1.toString() + ", " + f2.toString()
|
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
|||||||
(
|
(
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
flowFwd(mid, fromArg, apf, ap, config) and
|
flowFwd(mid, fromArg, apf, ap, config) and
|
||||||
localFlowBigStep(mid, node, true, config)
|
localFlowBigStep(mid, node, true, config, _)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathNil nil |
|
exists(Node mid, AccessPathNil nil |
|
||||||
flowFwd(mid, fromArg, _, nil, config) and
|
flowFwd(mid, fromArg, _, nil, config) and
|
||||||
localFlowBigStep(mid, node, false, config) and
|
localFlowBigStep(mid, node, false, config, _) and
|
||||||
ap = node.(AccessPathNilNode).getAp() and
|
ap = node.(AccessPathNilNode).getAp() and
|
||||||
apf = ap.(AccessPathNil).getFront()
|
apf = ap.(AccessPathNil).getFront()
|
||||||
)
|
)
|
||||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
|||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowBigStep(node, mid, true, config) and
|
localFlowBigStep(node, mid, true, config, _) and
|
||||||
flow(mid, toReturn, ap, config)
|
flow(mid, toReturn, ap, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathNil nil |
|
exists(Node mid, AccessPathNil nil |
|
||||||
flowFwd(node, _, _, ap, config) and
|
flowFwd(node, _, _, ap, config) and
|
||||||
localFlowBigStep(node, mid, false, config) and
|
localFlowBigStep(node, mid, false, config, _) and
|
||||||
flow(mid, toReturn, nil, config) and
|
flow(mid, toReturn, nil, config) and
|
||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
)
|
)
|
||||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
|||||||
this instanceof PathNodeSink and result = ""
|
this instanceof PathNodeSink and result = ""
|
||||||
or
|
or
|
||||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||||
if s = "" then result = "" else result = " [" + s + "]"
|
if s = "" then result = "" else result = " " + s
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1728,14 +1734,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
|||||||
* a callable is recorded by `cc`.
|
* a callable is recorded by `cc`.
|
||||||
*/
|
*/
|
||||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||||
|
midnode = mid.getNode() and
|
||||||
|
conf = mid.getConfiguration() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
ap = mid.getAp()
|
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||||
|
ap0 = mid.getAp()
|
||||||
|
|
|
||||||
|
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||||
|
ap = ap0
|
||||||
or
|
or
|
||||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||||
cc = mid.getCallContext() and
|
ap0 instanceof AccessPathNil and
|
||||||
mid.getAp() instanceof AccessPathNil and
|
|
||||||
ap = node.(AccessPathNilNode).getAp()
|
ap = node.(AccessPathNilNode).getAp()
|
||||||
|
)
|
||||||
or
|
or
|
||||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||||
cc instanceof CallContextAny and
|
cc instanceof CallContextAny and
|
||||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
|||||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||||
p.isParameterOf(callable, i)
|
p.isParameterOf(callable, i)
|
||||||
|
|
|
|
||||||
if reducedViableImplInCallContext(_, callable, call)
|
if recordDataFlowCallSite(call, callable)
|
||||||
then innercc = TSpecificCall(call, i, emptyAp)
|
then innercc = TSpecificCall(call, i, emptyAp)
|
||||||
else innercc = TSomeCall(p, emptyAp)
|
else innercc = TSomeCall(p, emptyAp)
|
||||||
)
|
)
|
||||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
|||||||
|
|
||||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
|
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override AccessPathFront getFront() {
|
override AccessPathFront getFront() {
|
||||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||||
if len = 1
|
if len = 1
|
||||||
then result = f.toString()
|
then result = "[" + f.toString() + "]"
|
||||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
|||||||
|
|
||||||
private string ppAp() {
|
private string ppAp() {
|
||||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||||
if s = "" then result = "" else result = " [" + s + "]"
|
if s = "" then result = "" else result = " " + s
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2204,6 +2216,8 @@ private module FlowExploration {
|
|||||||
private predicate partialPathStep(
|
private predicate partialPathStep(
|
||||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
|
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||||
|
(
|
||||||
localFlowStep(mid.getNode(), node, config) and
|
localFlowStep(mid.getNode(), node, config) and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
@@ -2214,6 +2228,7 @@ private module FlowExploration {
|
|||||||
mid.getAp() instanceof PartialAccessPathNil and
|
mid.getAp() instanceof PartialAccessPathNil and
|
||||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||||
config = mid.getConfiguration()
|
config = mid.getConfiguration()
|
||||||
|
)
|
||||||
or
|
or
|
||||||
jumpStep(mid.getNode(), node, config) and
|
jumpStep(mid.getNode(), node, config) and
|
||||||
cc instanceof CallContextAny and
|
cc instanceof CallContextAny and
|
||||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
|||||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||||
p.isParameterOf(callable, i)
|
p.isParameterOf(callable, i)
|
||||||
|
|
|
|
||||||
if reducedViableImplInCallContext(_, callable, call)
|
if recordDataFlowCallSite(call, callable)
|
||||||
then innercc = TSpecificCall(call, i, emptyAp)
|
then innercc = TSpecificCall(call, i, emptyAp)
|
||||||
else innercc = TSomeCall(p, emptyAp)
|
else innercc = TSomeCall(p, emptyAp)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* on each other without introducing mutual recursion among those configurations.
|
* on each other without introducing mutual recursion among those configurations.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private import DataFlowImplCommon
|
private import DataFlowImplCommon::Public
|
||||||
private import DataFlowImplSpecific::Private
|
private import DataFlowImplSpecific::Private
|
||||||
import DataFlowImplSpecific::Public
|
import DataFlowImplSpecific::Public
|
||||||
|
|
||||||
@@ -905,8 +905,10 @@ private predicate localFlowExit(Node node, Configuration config) {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate localFlowStepPlus(
|
private predicate localFlowStepPlus(
|
||||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||||
) {
|
) {
|
||||||
|
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||||
|
(
|
||||||
localFlowEntry(node1, config) and
|
localFlowEntry(node1, config) and
|
||||||
(
|
(
|
||||||
localFlowStep(node1, node2, config) and preservesValue = true
|
localFlowStep(node1, node2, config) and preservesValue = true
|
||||||
@@ -914,33 +916,36 @@ private predicate localFlowStepPlus(
|
|||||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||||
) and
|
) and
|
||||||
node1 != node2 and
|
node1 != node2 and
|
||||||
|
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||||
|
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||||
localFlowStep(mid, node2, config) and
|
localFlowStep(mid, node2, config) and
|
||||||
not mid instanceof CastNode and
|
not mid instanceof CastNode and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowStepPlus(node1, mid, _, config) and
|
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||||
additionalLocalFlowStep(mid, node2, config) and
|
additionalLocalFlowStep(mid, node2, config) and
|
||||||
not mid instanceof CastNode and
|
not mid instanceof CastNode and
|
||||||
preservesValue = false and
|
preservesValue = false and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
)
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node1` can step to `node2` in one or more local steps and this
|
* Holds if `node1` can step to `node2` in one or more local steps and this
|
||||||
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
||||||
*/
|
*/
|
||||||
pragma[noinline]
|
pragma[nomagic]
|
||||||
private predicate localFlowBigStep(
|
private predicate localFlowBigStep(
|
||||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
|
||||||
) {
|
) {
|
||||||
localFlowStepPlus(node1, node2, preservesValue, config) and
|
localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
|
||||||
localFlowExit(node2, config)
|
localFlowExit(node2, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
|||||||
(
|
(
|
||||||
any(Configuration c).isSource(this)
|
any(Configuration c).isSource(this)
|
||||||
or
|
or
|
||||||
localFlowBigStep(_, this, false, _)
|
localFlowBigStep(_, this, false, _, _)
|
||||||
or
|
or
|
||||||
additionalJumpStep(_, this, _)
|
additionalJumpStep(_, this, _)
|
||||||
)
|
)
|
||||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
|||||||
(
|
(
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
flowCandFwd(mid, fromArg, apf, config) and
|
flowCandFwd(mid, fromArg, apf, config) and
|
||||||
localFlowBigStep(mid, node, true, config)
|
localFlowBigStep(mid, node, true, config, _)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathFrontNil nil |
|
exists(Node mid, AccessPathFrontNil nil |
|
||||||
flowCandFwd(mid, fromArg, nil, config) and
|
flowCandFwd(mid, fromArg, nil, config) and
|
||||||
localFlowBigStep(mid, node, false, config) and
|
localFlowBigStep(mid, node, false, config, _) and
|
||||||
apf = node.(AccessPathFrontNilNode).getApf()
|
apf = node.(AccessPathFrontNilNode).getApf()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
|||||||
flowCandFwd(mid, fromArg, _, config) and
|
flowCandFwd(mid, fromArg, _, config) and
|
||||||
store(mid, f, node) and
|
store(mid, f, node) and
|
||||||
nodeCand(node, unbind(config)) and
|
nodeCand(node, unbind(config)) and
|
||||||
|
readStoreCand(f, unbind(config)) and
|
||||||
apf.headUsesContent(f)
|
apf.headUsesContent(f)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
|||||||
apf instanceof AccessPathFrontNil
|
apf instanceof AccessPathFrontNil
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowBigStep(node, mid, true, config) and
|
localFlowBigStep(node, mid, true, config, _) and
|
||||||
flowCand(mid, toReturn, apf, config)
|
flowCand(mid, toReturn, apf, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathFrontNil nil |
|
exists(Node mid, AccessPathFrontNil nil |
|
||||||
flowCandFwd(node, _, apf, config) and
|
flowCandFwd(node, _, apf, config) and
|
||||||
localFlowBigStep(node, mid, false, config) and
|
localFlowBigStep(node, mid, false, config, _) and
|
||||||
flowCand(mid, toReturn, nil, config) and
|
flowCand(mid, toReturn, nil, config) and
|
||||||
apf instanceof AccessPathFrontNil
|
apf instanceof AccessPathFrontNil
|
||||||
)
|
)
|
||||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
|||||||
exists(Content f, AccessPathFront apf0 |
|
exists(Content f, AccessPathFront apf0 |
|
||||||
flowCandStore(node, f, toReturn, apf0, config) and
|
flowCandStore(node, f, toReturn, apf0, config) and
|
||||||
apf0.headUsesContent(f) and
|
apf0.headUsesContent(f) and
|
||||||
consCand(f, apf, unbind(config))
|
consCand(f, apf, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Content f, AccessPathFront apf0 |
|
exists(Content f, AccessPathFront apf0 |
|
||||||
flowCandRead(node, f, toReturn, apf0, config) and
|
flowCandRead(node, f, toReturn, apf0, config) and
|
||||||
consCandFwd(f, apf0, unbind(config)) and
|
consCandFwd(f, apf0, config) and
|
||||||
apf.headUsesContent(f)
|
apf.headUsesContent(f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1221,8 +1227,8 @@ private newtype TAccessPath =
|
|||||||
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||||
* element of the list and its length are tracked. If data flows from a source to
|
* elements of the list and its length are tracked. If data flows from a source to
|
||||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||||
* dereference operations needed to get from the value in the node to the
|
* dereference operations needed to get from the value in the node to the
|
||||||
* tracked object. The final type indicates the type of the tracked object.
|
* tracked object. The final type indicates the type of the tracked object.
|
||||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
|||||||
|
|
||||||
private class AccessPathNil extends AccessPath, TNil {
|
private class AccessPathNil extends AccessPath, TNil {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
|
exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override AccessPathFront getFront() {
|
override AccessPathFront getFront() {
|
||||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||||
// The `concat` becomes "" if `ppReprType` has no result.
|
// The `concat` becomes "" if `ppReprType` has no result.
|
||||||
result = f.toString() + concat(" : " + ppReprType(t))
|
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1293,8 +1299,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||||
if len = 2
|
if len = 2
|
||||||
then result = f1.toString() + ", " + f2.toString()
|
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
|||||||
(
|
(
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
flowFwd(mid, fromArg, apf, ap, config) and
|
flowFwd(mid, fromArg, apf, ap, config) and
|
||||||
localFlowBigStep(mid, node, true, config)
|
localFlowBigStep(mid, node, true, config, _)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathNil nil |
|
exists(Node mid, AccessPathNil nil |
|
||||||
flowFwd(mid, fromArg, _, nil, config) and
|
flowFwd(mid, fromArg, _, nil, config) and
|
||||||
localFlowBigStep(mid, node, false, config) and
|
localFlowBigStep(mid, node, false, config, _) and
|
||||||
ap = node.(AccessPathNilNode).getAp() and
|
ap = node.(AccessPathNilNode).getAp() and
|
||||||
apf = ap.(AccessPathNil).getFront()
|
apf = ap.(AccessPathNil).getFront()
|
||||||
)
|
)
|
||||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
|||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowBigStep(node, mid, true, config) and
|
localFlowBigStep(node, mid, true, config, _) and
|
||||||
flow(mid, toReturn, ap, config)
|
flow(mid, toReturn, ap, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathNil nil |
|
exists(Node mid, AccessPathNil nil |
|
||||||
flowFwd(node, _, _, ap, config) and
|
flowFwd(node, _, _, ap, config) and
|
||||||
localFlowBigStep(node, mid, false, config) and
|
localFlowBigStep(node, mid, false, config, _) and
|
||||||
flow(mid, toReturn, nil, config) and
|
flow(mid, toReturn, nil, config) and
|
||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
)
|
)
|
||||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
|||||||
this instanceof PathNodeSink and result = ""
|
this instanceof PathNodeSink and result = ""
|
||||||
or
|
or
|
||||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||||
if s = "" then result = "" else result = " [" + s + "]"
|
if s = "" then result = "" else result = " " + s
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1728,14 +1734,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
|||||||
* a callable is recorded by `cc`.
|
* a callable is recorded by `cc`.
|
||||||
*/
|
*/
|
||||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||||
|
midnode = mid.getNode() and
|
||||||
|
conf = mid.getConfiguration() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
ap = mid.getAp()
|
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||||
|
ap0 = mid.getAp()
|
||||||
|
|
|
||||||
|
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||||
|
ap = ap0
|
||||||
or
|
or
|
||||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||||
cc = mid.getCallContext() and
|
ap0 instanceof AccessPathNil and
|
||||||
mid.getAp() instanceof AccessPathNil and
|
|
||||||
ap = node.(AccessPathNilNode).getAp()
|
ap = node.(AccessPathNilNode).getAp()
|
||||||
|
)
|
||||||
or
|
or
|
||||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||||
cc instanceof CallContextAny and
|
cc instanceof CallContextAny and
|
||||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
|||||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||||
p.isParameterOf(callable, i)
|
p.isParameterOf(callable, i)
|
||||||
|
|
|
|
||||||
if reducedViableImplInCallContext(_, callable, call)
|
if recordDataFlowCallSite(call, callable)
|
||||||
then innercc = TSpecificCall(call, i, emptyAp)
|
then innercc = TSpecificCall(call, i, emptyAp)
|
||||||
else innercc = TSomeCall(p, emptyAp)
|
else innercc = TSomeCall(p, emptyAp)
|
||||||
)
|
)
|
||||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
|||||||
|
|
||||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
|
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override AccessPathFront getFront() {
|
override AccessPathFront getFront() {
|
||||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||||
if len = 1
|
if len = 1
|
||||||
then result = f.toString()
|
then result = "[" + f.toString() + "]"
|
||||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
|||||||
|
|
||||||
private string ppAp() {
|
private string ppAp() {
|
||||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||||
if s = "" then result = "" else result = " [" + s + "]"
|
if s = "" then result = "" else result = " " + s
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2204,6 +2216,8 @@ private module FlowExploration {
|
|||||||
private predicate partialPathStep(
|
private predicate partialPathStep(
|
||||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
|
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||||
|
(
|
||||||
localFlowStep(mid.getNode(), node, config) and
|
localFlowStep(mid.getNode(), node, config) and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
@@ -2214,6 +2228,7 @@ private module FlowExploration {
|
|||||||
mid.getAp() instanceof PartialAccessPathNil and
|
mid.getAp() instanceof PartialAccessPathNil and
|
||||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||||
config = mid.getConfiguration()
|
config = mid.getConfiguration()
|
||||||
|
)
|
||||||
or
|
or
|
||||||
jumpStep(mid.getNode(), node, config) and
|
jumpStep(mid.getNode(), node, config) and
|
||||||
cc instanceof CallContextAny and
|
cc instanceof CallContextAny and
|
||||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
|||||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||||
p.isParameterOf(callable, i)
|
p.isParameterOf(callable, i)
|
||||||
|
|
|
|
||||||
if reducedViableImplInCallContext(_, callable, call)
|
if recordDataFlowCallSite(call, callable)
|
||||||
then innercc = TSpecificCall(call, i, emptyAp)
|
then innercc = TSpecificCall(call, i, emptyAp)
|
||||||
else innercc = TSomeCall(p, emptyAp)
|
else innercc = TSomeCall(p, emptyAp)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* on each other without introducing mutual recursion among those configurations.
|
* on each other without introducing mutual recursion among those configurations.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private import DataFlowImplCommon
|
private import DataFlowImplCommon::Public
|
||||||
private import DataFlowImplSpecific::Private
|
private import DataFlowImplSpecific::Private
|
||||||
import DataFlowImplSpecific::Public
|
import DataFlowImplSpecific::Public
|
||||||
|
|
||||||
@@ -905,8 +905,10 @@ private predicate localFlowExit(Node node, Configuration config) {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate localFlowStepPlus(
|
private predicate localFlowStepPlus(
|
||||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||||
) {
|
) {
|
||||||
|
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||||
|
(
|
||||||
localFlowEntry(node1, config) and
|
localFlowEntry(node1, config) and
|
||||||
(
|
(
|
||||||
localFlowStep(node1, node2, config) and preservesValue = true
|
localFlowStep(node1, node2, config) and preservesValue = true
|
||||||
@@ -914,33 +916,36 @@ private predicate localFlowStepPlus(
|
|||||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||||
) and
|
) and
|
||||||
node1 != node2 and
|
node1 != node2 and
|
||||||
|
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||||
|
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||||
localFlowStep(mid, node2, config) and
|
localFlowStep(mid, node2, config) and
|
||||||
not mid instanceof CastNode and
|
not mid instanceof CastNode and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowStepPlus(node1, mid, _, config) and
|
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||||
additionalLocalFlowStep(mid, node2, config) and
|
additionalLocalFlowStep(mid, node2, config) and
|
||||||
not mid instanceof CastNode and
|
not mid instanceof CastNode and
|
||||||
preservesValue = false and
|
preservesValue = false and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
)
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node1` can step to `node2` in one or more local steps and this
|
* Holds if `node1` can step to `node2` in one or more local steps and this
|
||||||
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
||||||
*/
|
*/
|
||||||
pragma[noinline]
|
pragma[nomagic]
|
||||||
private predicate localFlowBigStep(
|
private predicate localFlowBigStep(
|
||||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
|
||||||
) {
|
) {
|
||||||
localFlowStepPlus(node1, node2, preservesValue, config) and
|
localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
|
||||||
localFlowExit(node2, config)
|
localFlowExit(node2, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
|||||||
(
|
(
|
||||||
any(Configuration c).isSource(this)
|
any(Configuration c).isSource(this)
|
||||||
or
|
or
|
||||||
localFlowBigStep(_, this, false, _)
|
localFlowBigStep(_, this, false, _, _)
|
||||||
or
|
or
|
||||||
additionalJumpStep(_, this, _)
|
additionalJumpStep(_, this, _)
|
||||||
)
|
)
|
||||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
|||||||
(
|
(
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
flowCandFwd(mid, fromArg, apf, config) and
|
flowCandFwd(mid, fromArg, apf, config) and
|
||||||
localFlowBigStep(mid, node, true, config)
|
localFlowBigStep(mid, node, true, config, _)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathFrontNil nil |
|
exists(Node mid, AccessPathFrontNil nil |
|
||||||
flowCandFwd(mid, fromArg, nil, config) and
|
flowCandFwd(mid, fromArg, nil, config) and
|
||||||
localFlowBigStep(mid, node, false, config) and
|
localFlowBigStep(mid, node, false, config, _) and
|
||||||
apf = node.(AccessPathFrontNilNode).getApf()
|
apf = node.(AccessPathFrontNilNode).getApf()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
|||||||
flowCandFwd(mid, fromArg, _, config) and
|
flowCandFwd(mid, fromArg, _, config) and
|
||||||
store(mid, f, node) and
|
store(mid, f, node) and
|
||||||
nodeCand(node, unbind(config)) and
|
nodeCand(node, unbind(config)) and
|
||||||
|
readStoreCand(f, unbind(config)) and
|
||||||
apf.headUsesContent(f)
|
apf.headUsesContent(f)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
|||||||
apf instanceof AccessPathFrontNil
|
apf instanceof AccessPathFrontNil
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowBigStep(node, mid, true, config) and
|
localFlowBigStep(node, mid, true, config, _) and
|
||||||
flowCand(mid, toReturn, apf, config)
|
flowCand(mid, toReturn, apf, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathFrontNil nil |
|
exists(Node mid, AccessPathFrontNil nil |
|
||||||
flowCandFwd(node, _, apf, config) and
|
flowCandFwd(node, _, apf, config) and
|
||||||
localFlowBigStep(node, mid, false, config) and
|
localFlowBigStep(node, mid, false, config, _) and
|
||||||
flowCand(mid, toReturn, nil, config) and
|
flowCand(mid, toReturn, nil, config) and
|
||||||
apf instanceof AccessPathFrontNil
|
apf instanceof AccessPathFrontNil
|
||||||
)
|
)
|
||||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
|||||||
exists(Content f, AccessPathFront apf0 |
|
exists(Content f, AccessPathFront apf0 |
|
||||||
flowCandStore(node, f, toReturn, apf0, config) and
|
flowCandStore(node, f, toReturn, apf0, config) and
|
||||||
apf0.headUsesContent(f) and
|
apf0.headUsesContent(f) and
|
||||||
consCand(f, apf, unbind(config))
|
consCand(f, apf, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Content f, AccessPathFront apf0 |
|
exists(Content f, AccessPathFront apf0 |
|
||||||
flowCandRead(node, f, toReturn, apf0, config) and
|
flowCandRead(node, f, toReturn, apf0, config) and
|
||||||
consCandFwd(f, apf0, unbind(config)) and
|
consCandFwd(f, apf0, config) and
|
||||||
apf.headUsesContent(f)
|
apf.headUsesContent(f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1221,8 +1227,8 @@ private newtype TAccessPath =
|
|||||||
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||||
* element of the list and its length are tracked. If data flows from a source to
|
* elements of the list and its length are tracked. If data flows from a source to
|
||||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||||
* dereference operations needed to get from the value in the node to the
|
* dereference operations needed to get from the value in the node to the
|
||||||
* tracked object. The final type indicates the type of the tracked object.
|
* tracked object. The final type indicates the type of the tracked object.
|
||||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
|||||||
|
|
||||||
private class AccessPathNil extends AccessPath, TNil {
|
private class AccessPathNil extends AccessPath, TNil {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
|
exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override AccessPathFront getFront() {
|
override AccessPathFront getFront() {
|
||||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||||
// The `concat` becomes "" if `ppReprType` has no result.
|
// The `concat` becomes "" if `ppReprType` has no result.
|
||||||
result = f.toString() + concat(" : " + ppReprType(t))
|
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1293,8 +1299,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||||
if len = 2
|
if len = 2
|
||||||
then result = f1.toString() + ", " + f2.toString()
|
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
|||||||
(
|
(
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
flowFwd(mid, fromArg, apf, ap, config) and
|
flowFwd(mid, fromArg, apf, ap, config) and
|
||||||
localFlowBigStep(mid, node, true, config)
|
localFlowBigStep(mid, node, true, config, _)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathNil nil |
|
exists(Node mid, AccessPathNil nil |
|
||||||
flowFwd(mid, fromArg, _, nil, config) and
|
flowFwd(mid, fromArg, _, nil, config) and
|
||||||
localFlowBigStep(mid, node, false, config) and
|
localFlowBigStep(mid, node, false, config, _) and
|
||||||
ap = node.(AccessPathNilNode).getAp() and
|
ap = node.(AccessPathNilNode).getAp() and
|
||||||
apf = ap.(AccessPathNil).getFront()
|
apf = ap.(AccessPathNil).getFront()
|
||||||
)
|
)
|
||||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
|||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowBigStep(node, mid, true, config) and
|
localFlowBigStep(node, mid, true, config, _) and
|
||||||
flow(mid, toReturn, ap, config)
|
flow(mid, toReturn, ap, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathNil nil |
|
exists(Node mid, AccessPathNil nil |
|
||||||
flowFwd(node, _, _, ap, config) and
|
flowFwd(node, _, _, ap, config) and
|
||||||
localFlowBigStep(node, mid, false, config) and
|
localFlowBigStep(node, mid, false, config, _) and
|
||||||
flow(mid, toReturn, nil, config) and
|
flow(mid, toReturn, nil, config) and
|
||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
)
|
)
|
||||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
|||||||
this instanceof PathNodeSink and result = ""
|
this instanceof PathNodeSink and result = ""
|
||||||
or
|
or
|
||||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||||
if s = "" then result = "" else result = " [" + s + "]"
|
if s = "" then result = "" else result = " " + s
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1728,14 +1734,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
|||||||
* a callable is recorded by `cc`.
|
* a callable is recorded by `cc`.
|
||||||
*/
|
*/
|
||||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||||
|
midnode = mid.getNode() and
|
||||||
|
conf = mid.getConfiguration() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
ap = mid.getAp()
|
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||||
|
ap0 = mid.getAp()
|
||||||
|
|
|
||||||
|
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||||
|
ap = ap0
|
||||||
or
|
or
|
||||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||||
cc = mid.getCallContext() and
|
ap0 instanceof AccessPathNil and
|
||||||
mid.getAp() instanceof AccessPathNil and
|
|
||||||
ap = node.(AccessPathNilNode).getAp()
|
ap = node.(AccessPathNilNode).getAp()
|
||||||
|
)
|
||||||
or
|
or
|
||||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||||
cc instanceof CallContextAny and
|
cc instanceof CallContextAny and
|
||||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
|||||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||||
p.isParameterOf(callable, i)
|
p.isParameterOf(callable, i)
|
||||||
|
|
|
|
||||||
if reducedViableImplInCallContext(_, callable, call)
|
if recordDataFlowCallSite(call, callable)
|
||||||
then innercc = TSpecificCall(call, i, emptyAp)
|
then innercc = TSpecificCall(call, i, emptyAp)
|
||||||
else innercc = TSomeCall(p, emptyAp)
|
else innercc = TSomeCall(p, emptyAp)
|
||||||
)
|
)
|
||||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
|||||||
|
|
||||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
|
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override AccessPathFront getFront() {
|
override AccessPathFront getFront() {
|
||||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||||
if len = 1
|
if len = 1
|
||||||
then result = f.toString()
|
then result = "[" + f.toString() + "]"
|
||||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
|||||||
|
|
||||||
private string ppAp() {
|
private string ppAp() {
|
||||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||||
if s = "" then result = "" else result = " [" + s + "]"
|
if s = "" then result = "" else result = " " + s
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2204,6 +2216,8 @@ private module FlowExploration {
|
|||||||
private predicate partialPathStep(
|
private predicate partialPathStep(
|
||||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
|
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||||
|
(
|
||||||
localFlowStep(mid.getNode(), node, config) and
|
localFlowStep(mid.getNode(), node, config) and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
@@ -2214,6 +2228,7 @@ private module FlowExploration {
|
|||||||
mid.getAp() instanceof PartialAccessPathNil and
|
mid.getAp() instanceof PartialAccessPathNil and
|
||||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||||
config = mid.getConfiguration()
|
config = mid.getConfiguration()
|
||||||
|
)
|
||||||
or
|
or
|
||||||
jumpStep(mid.getNode(), node, config) and
|
jumpStep(mid.getNode(), node, config) and
|
||||||
cc instanceof CallContextAny and
|
cc instanceof CallContextAny and
|
||||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
|||||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||||
p.isParameterOf(callable, i)
|
p.isParameterOf(callable, i)
|
||||||
|
|
|
|
||||||
if reducedViableImplInCallContext(_, callable, call)
|
if recordDataFlowCallSite(call, callable)
|
||||||
then innercc = TSpecificCall(call, i, emptyAp)
|
then innercc = TSpecificCall(call, i, emptyAp)
|
||||||
else innercc = TSomeCall(p, emptyAp)
|
else innercc = TSomeCall(p, emptyAp)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* on each other without introducing mutual recursion among those configurations.
|
* on each other without introducing mutual recursion among those configurations.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private import DataFlowImplCommon
|
private import DataFlowImplCommon::Public
|
||||||
private import DataFlowImplSpecific::Private
|
private import DataFlowImplSpecific::Private
|
||||||
import DataFlowImplSpecific::Public
|
import DataFlowImplSpecific::Public
|
||||||
|
|
||||||
@@ -905,8 +905,10 @@ private predicate localFlowExit(Node node, Configuration config) {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate localFlowStepPlus(
|
private predicate localFlowStepPlus(
|
||||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||||
) {
|
) {
|
||||||
|
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||||
|
(
|
||||||
localFlowEntry(node1, config) and
|
localFlowEntry(node1, config) and
|
||||||
(
|
(
|
||||||
localFlowStep(node1, node2, config) and preservesValue = true
|
localFlowStep(node1, node2, config) and preservesValue = true
|
||||||
@@ -914,33 +916,36 @@ private predicate localFlowStepPlus(
|
|||||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||||
) and
|
) and
|
||||||
node1 != node2 and
|
node1 != node2 and
|
||||||
|
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||||
|
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||||
localFlowStep(mid, node2, config) and
|
localFlowStep(mid, node2, config) and
|
||||||
not mid instanceof CastNode and
|
not mid instanceof CastNode and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowStepPlus(node1, mid, _, config) and
|
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||||
additionalLocalFlowStep(mid, node2, config) and
|
additionalLocalFlowStep(mid, node2, config) and
|
||||||
not mid instanceof CastNode and
|
not mid instanceof CastNode and
|
||||||
preservesValue = false and
|
preservesValue = false and
|
||||||
nodeCand(node2, unbind(config))
|
nodeCand(node2, unbind(config))
|
||||||
)
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node1` can step to `node2` in one or more local steps and this
|
* Holds if `node1` can step to `node2` in one or more local steps and this
|
||||||
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
* path can occur as a maximal subsequence of local steps in a dataflow path.
|
||||||
*/
|
*/
|
||||||
pragma[noinline]
|
pragma[nomagic]
|
||||||
private predicate localFlowBigStep(
|
private predicate localFlowBigStep(
|
||||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
|
||||||
) {
|
) {
|
||||||
localFlowStepPlus(node1, node2, preservesValue, config) and
|
localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
|
||||||
localFlowExit(node2, config)
|
localFlowExit(node2, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
|||||||
(
|
(
|
||||||
any(Configuration c).isSource(this)
|
any(Configuration c).isSource(this)
|
||||||
or
|
or
|
||||||
localFlowBigStep(_, this, false, _)
|
localFlowBigStep(_, this, false, _, _)
|
||||||
or
|
or
|
||||||
additionalJumpStep(_, this, _)
|
additionalJumpStep(_, this, _)
|
||||||
)
|
)
|
||||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
|||||||
(
|
(
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
flowCandFwd(mid, fromArg, apf, config) and
|
flowCandFwd(mid, fromArg, apf, config) and
|
||||||
localFlowBigStep(mid, node, true, config)
|
localFlowBigStep(mid, node, true, config, _)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathFrontNil nil |
|
exists(Node mid, AccessPathFrontNil nil |
|
||||||
flowCandFwd(mid, fromArg, nil, config) and
|
flowCandFwd(mid, fromArg, nil, config) and
|
||||||
localFlowBigStep(mid, node, false, config) and
|
localFlowBigStep(mid, node, false, config, _) and
|
||||||
apf = node.(AccessPathFrontNilNode).getApf()
|
apf = node.(AccessPathFrontNilNode).getApf()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
|||||||
flowCandFwd(mid, fromArg, _, config) and
|
flowCandFwd(mid, fromArg, _, config) and
|
||||||
store(mid, f, node) and
|
store(mid, f, node) and
|
||||||
nodeCand(node, unbind(config)) and
|
nodeCand(node, unbind(config)) and
|
||||||
|
readStoreCand(f, unbind(config)) and
|
||||||
apf.headUsesContent(f)
|
apf.headUsesContent(f)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
|||||||
apf instanceof AccessPathFrontNil
|
apf instanceof AccessPathFrontNil
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowBigStep(node, mid, true, config) and
|
localFlowBigStep(node, mid, true, config, _) and
|
||||||
flowCand(mid, toReturn, apf, config)
|
flowCand(mid, toReturn, apf, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathFrontNil nil |
|
exists(Node mid, AccessPathFrontNil nil |
|
||||||
flowCandFwd(node, _, apf, config) and
|
flowCandFwd(node, _, apf, config) and
|
||||||
localFlowBigStep(node, mid, false, config) and
|
localFlowBigStep(node, mid, false, config, _) and
|
||||||
flowCand(mid, toReturn, nil, config) and
|
flowCand(mid, toReturn, nil, config) and
|
||||||
apf instanceof AccessPathFrontNil
|
apf instanceof AccessPathFrontNil
|
||||||
)
|
)
|
||||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
|||||||
exists(Content f, AccessPathFront apf0 |
|
exists(Content f, AccessPathFront apf0 |
|
||||||
flowCandStore(node, f, toReturn, apf0, config) and
|
flowCandStore(node, f, toReturn, apf0, config) and
|
||||||
apf0.headUsesContent(f) and
|
apf0.headUsesContent(f) and
|
||||||
consCand(f, apf, unbind(config))
|
consCand(f, apf, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Content f, AccessPathFront apf0 |
|
exists(Content f, AccessPathFront apf0 |
|
||||||
flowCandRead(node, f, toReturn, apf0, config) and
|
flowCandRead(node, f, toReturn, apf0, config) and
|
||||||
consCandFwd(f, apf0, unbind(config)) and
|
consCandFwd(f, apf0, config) and
|
||||||
apf.headUsesContent(f)
|
apf.headUsesContent(f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1221,8 +1227,8 @@ private newtype TAccessPath =
|
|||||||
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||||
* element of the list and its length are tracked. If data flows from a source to
|
* elements of the list and its length are tracked. If data flows from a source to
|
||||||
* a given node with a given `AccessPath`, this indicates the sequence of
|
* a given node with a given `AccessPath`, this indicates the sequence of
|
||||||
* dereference operations needed to get from the value in the node to the
|
* dereference operations needed to get from the value in the node to the
|
||||||
* tracked object. The final type indicates the type of the tracked object.
|
* tracked object. The final type indicates the type of the tracked object.
|
||||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
|||||||
|
|
||||||
private class AccessPathNil extends AccessPath, TNil {
|
private class AccessPathNil extends AccessPath, TNil {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
|
exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override AccessPathFront getFront() {
|
override AccessPathFront getFront() {
|
||||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||||
// The `concat` becomes "" if `ppReprType` has no result.
|
// The `concat` becomes "" if `ppReprType` has no result.
|
||||||
result = f.toString() + concat(" : " + ppReprType(t))
|
result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1293,8 +1299,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||||
if len = 2
|
if len = 2
|
||||||
then result = f1.toString() + ", " + f2.toString()
|
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
|||||||
(
|
(
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
flowFwd(mid, fromArg, apf, ap, config) and
|
flowFwd(mid, fromArg, apf, ap, config) and
|
||||||
localFlowBigStep(mid, node, true, config)
|
localFlowBigStep(mid, node, true, config, _)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathNil nil |
|
exists(Node mid, AccessPathNil nil |
|
||||||
flowFwd(mid, fromArg, _, nil, config) and
|
flowFwd(mid, fromArg, _, nil, config) and
|
||||||
localFlowBigStep(mid, node, false, config) and
|
localFlowBigStep(mid, node, false, config, _) and
|
||||||
ap = node.(AccessPathNilNode).getAp() and
|
ap = node.(AccessPathNilNode).getAp() and
|
||||||
apf = ap.(AccessPathNil).getFront()
|
apf = ap.(AccessPathNil).getFront()
|
||||||
)
|
)
|
||||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
|||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
localFlowBigStep(node, mid, true, config) and
|
localFlowBigStep(node, mid, true, config, _) and
|
||||||
flow(mid, toReturn, ap, config)
|
flow(mid, toReturn, ap, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node mid, AccessPathNil nil |
|
exists(Node mid, AccessPathNil nil |
|
||||||
flowFwd(node, _, _, ap, config) and
|
flowFwd(node, _, _, ap, config) and
|
||||||
localFlowBigStep(node, mid, false, config) and
|
localFlowBigStep(node, mid, false, config, _) and
|
||||||
flow(mid, toReturn, nil, config) and
|
flow(mid, toReturn, nil, config) and
|
||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
)
|
)
|
||||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
|||||||
this instanceof PathNodeSink and result = ""
|
this instanceof PathNodeSink and result = ""
|
||||||
or
|
or
|
||||||
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
exists(string s | s = this.(PathNodeMid).getAp().toString() |
|
||||||
if s = "" then result = "" else result = " [" + s + "]"
|
if s = "" then result = "" else result = " " + s
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1728,14 +1734,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
|
|||||||
* a callable is recorded by `cc`.
|
* a callable is recorded by `cc`.
|
||||||
*/
|
*/
|
||||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||||
|
midnode = mid.getNode() and
|
||||||
|
conf = mid.getConfiguration() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
ap = mid.getAp()
|
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||||
|
ap0 = mid.getAp()
|
||||||
|
|
|
||||||
|
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||||
|
ap = ap0
|
||||||
or
|
or
|
||||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||||
cc = mid.getCallContext() and
|
ap0 instanceof AccessPathNil and
|
||||||
mid.getAp() instanceof AccessPathNil and
|
|
||||||
ap = node.(AccessPathNilNode).getAp()
|
ap = node.(AccessPathNilNode).getAp()
|
||||||
|
)
|
||||||
or
|
or
|
||||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||||
cc instanceof CallContextAny and
|
cc instanceof CallContextAny and
|
||||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
|||||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||||
p.isParameterOf(callable, i)
|
p.isParameterOf(callable, i)
|
||||||
|
|
|
|
||||||
if reducedViableImplInCallContext(_, callable, call)
|
if recordDataFlowCallSite(call, callable)
|
||||||
then innercc = TSpecificCall(call, i, emptyAp)
|
then innercc = TSpecificCall(call, i, emptyAp)
|
||||||
else innercc = TSomeCall(p, emptyAp)
|
else innercc = TSomeCall(p, emptyAp)
|
||||||
)
|
)
|
||||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
|||||||
|
|
||||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
|
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override AccessPathFront getFront() {
|
override AccessPathFront getFront() {
|
||||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
|||||||
override string toString() {
|
override string toString() {
|
||||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||||
if len = 1
|
if len = 1
|
||||||
then result = f.toString()
|
then result = "[" + f.toString() + "]"
|
||||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
|||||||
|
|
||||||
private string ppAp() {
|
private string ppAp() {
|
||||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||||
if s = "" then result = "" else result = " [" + s + "]"
|
if s = "" then result = "" else result = " " + s
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2204,6 +2216,8 @@ private module FlowExploration {
|
|||||||
private predicate partialPathStep(
|
private predicate partialPathStep(
|
||||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
|
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||||
|
(
|
||||||
localFlowStep(mid.getNode(), node, config) and
|
localFlowStep(mid.getNode(), node, config) and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
@@ -2214,6 +2228,7 @@ private module FlowExploration {
|
|||||||
mid.getAp() instanceof PartialAccessPathNil and
|
mid.getAp() instanceof PartialAccessPathNil and
|
||||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||||
config = mid.getConfiguration()
|
config = mid.getConfiguration()
|
||||||
|
)
|
||||||
or
|
or
|
||||||
jumpStep(mid.getNode(), node, config) and
|
jumpStep(mid.getNode(), node, config) and
|
||||||
cc instanceof CallContextAny and
|
cc instanceof CallContextAny and
|
||||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
|||||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||||
p.isParameterOf(callable, i)
|
p.isParameterOf(callable, i)
|
||||||
|
|
|
|
||||||
if reducedViableImplInCallContext(_, callable, call)
|
if recordDataFlowCallSite(call, callable)
|
||||||
then innercc = TSpecificCall(call, i, emptyAp)
|
then innercc = TSpecificCall(call, i, emptyAp)
|
||||||
else innercc = TSomeCall(p, emptyAp)
|
else innercc = TSomeCall(p, emptyAp)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,8 +3,16 @@ import DataFlowImplSpecific::Public
|
|||||||
|
|
||||||
private ReturnNode getAReturnNodeOfKind(ReturnKind kind) { result.getKind() = kind }
|
private ReturnNode getAReturnNodeOfKind(ReturnKind kind) { result.getKind() = kind }
|
||||||
|
|
||||||
cached
|
module Public {
|
||||||
|
import ImplCommon
|
||||||
|
import FlowThrough_v2
|
||||||
|
}
|
||||||
|
|
||||||
private module ImplCommon {
|
private module ImplCommon {
|
||||||
|
import Cached
|
||||||
|
|
||||||
|
cached
|
||||||
|
private module Cached {
|
||||||
/**
|
/**
|
||||||
* Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
|
* Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
|
||||||
* The instance parameter is considered to have index `-1`.
|
* The instance parameter is considered to have index `-1`.
|
||||||
@@ -26,23 +34,36 @@ private module ImplCommon {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The `FlowThrough_*` modules take a `step` relation as input and provide
|
||||||
|
* an `argumentValueFlowsThrough` relation as output.
|
||||||
|
*
|
||||||
|
* `FlowThrough_v1` includes just `simpleLocalFlowStep`, which is then used
|
||||||
|
* to detect getters and setters.
|
||||||
|
* `FlowThrough_v2` then includes a little bit of local field flow on top
|
||||||
|
* of `simpleLocalFlowStep`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
private module FlowThrough_v1 {
|
||||||
|
private predicate step = simpleLocalFlowStep/2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `p` can flow to `node` in the same callable using only
|
* Holds if `p` can flow to `node` in the same callable using only
|
||||||
* value-preserving steps, not taking call contexts into account.
|
* value-preserving steps, not taking call contexts into account.
|
||||||
*/
|
*/
|
||||||
private predicate parameterValueFlowNoCtx(ParameterNode p, Node node) {
|
private predicate parameterValueFlowCand(ParameterNode p, Node node) {
|
||||||
p = node
|
p = node
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
parameterValueFlowNoCtx(p, mid) and
|
parameterValueFlowCand(p, mid) and
|
||||||
simpleLocalFlowStep(mid, node) and
|
step(mid, node) and
|
||||||
compatibleTypes(p.getType(), node.getType())
|
compatibleTypes(p.getType(), node.getType())
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// flow through a callable
|
// flow through a callable
|
||||||
exists(Node arg |
|
exists(Node arg |
|
||||||
parameterValueFlowNoCtx(p, arg) and
|
parameterValueFlowCand(p, arg) and
|
||||||
argumentValueFlowsThroughNoCtx(arg, node) and
|
argumentValueFlowsThroughCand(arg, node) and
|
||||||
compatibleTypes(p.getType(), node.getType())
|
compatibleTypes(p.getType(), node.getType())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -52,16 +73,16 @@ private module ImplCommon {
|
|||||||
* callable using only value-preserving steps, not taking call contexts
|
* callable using only value-preserving steps, not taking call contexts
|
||||||
* into account.
|
* into account.
|
||||||
*/
|
*/
|
||||||
private predicate parameterValueFlowsThroughNoCtx(ParameterNode p, ReturnKind kind) {
|
private predicate parameterValueFlowsThroughCand(ParameterNode p, ReturnKind kind) {
|
||||||
parameterValueFlowNoCtx(p, getAReturnNodeOfKind(kind))
|
parameterValueFlowCand(p, getAReturnNodeOfKind(kind))
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate argumentValueFlowsThroughNoCtx0(
|
private predicate argumentValueFlowsThroughCand0(
|
||||||
DataFlowCall call, ArgumentNode arg, ReturnKind kind
|
DataFlowCall call, ArgumentNode arg, ReturnKind kind
|
||||||
) {
|
) {
|
||||||
exists(ParameterNode param | viableParamArg(call, param, arg) |
|
exists(ParameterNode param | viableParamArg(call, param, arg) |
|
||||||
parameterValueFlowsThroughNoCtx(param, kind)
|
parameterValueFlowsThroughCand(param, kind)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,8 +90,10 @@ private module ImplCommon {
|
|||||||
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
|
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
|
||||||
* not taking call contexts into account.
|
* not taking call contexts into account.
|
||||||
*/
|
*/
|
||||||
private predicate argumentValueFlowsThroughNoCtx(ArgumentNode arg, OutNode out) {
|
private predicate argumentValueFlowsThroughCand(ArgumentNode arg, OutNode out) {
|
||||||
exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughNoCtx0(call, arg, kind) |
|
exists(DataFlowCall call, ReturnKind kind |
|
||||||
|
argumentValueFlowsThroughCand0(call, arg, kind)
|
||||||
|
|
|
||||||
out = getAnOutNode(call, kind) and
|
out = getAnOutNode(call, kind) and
|
||||||
compatibleTypes(arg.getType(), out.getType())
|
compatibleTypes(arg.getType(), out.getType())
|
||||||
)
|
)
|
||||||
@@ -85,7 +108,7 @@ private module ImplCommon {
|
|||||||
DataFlowCall call, int i, ArgumentNode arg, DataFlowCallable enclosing
|
DataFlowCall call, int i, ArgumentNode arg, DataFlowCallable enclosing
|
||||||
) {
|
) {
|
||||||
arg.argumentOf(call, i) and
|
arg.argumentOf(call, i) and
|
||||||
argumentValueFlowsThroughNoCtx(arg, _) and
|
argumentValueFlowsThroughCand(arg, _) and
|
||||||
enclosing = arg.getEnclosingCallable()
|
enclosing = arg.getEnclosingCallable()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,15 +116,20 @@ private module ImplCommon {
|
|||||||
private ParameterNode getAParameter(DataFlowCallable c) { result.getEnclosingCallable() = c }
|
private ParameterNode getAParameter(DataFlowCallable c) { result.getEnclosingCallable() = c }
|
||||||
|
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
private predicate viableParamArg0(int i, ArgumentNode arg, CallContext outercc, DataFlowCall call) {
|
private predicate viableParamArg0(
|
||||||
|
int i, ArgumentNode arg, CallContext outercc, DataFlowCall call
|
||||||
|
) {
|
||||||
exists(DataFlowCallable c | argumentOf(call, i, arg, c) |
|
exists(DataFlowCallable c | argumentOf(call, i, arg, c) |
|
||||||
|
(
|
||||||
outercc = TAnyCallContext()
|
outercc = TAnyCallContext()
|
||||||
or
|
or
|
||||||
outercc = TSomeCall(getAParameter(c), _)
|
outercc = TSomeCall(getAParameter(c), _)
|
||||||
or
|
or
|
||||||
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
|
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
|
||||||
reducedViableImplInCallContext(_, c, other)
|
recordDataFlowCallSite(other, c)
|
||||||
)
|
)
|
||||||
|
) and
|
||||||
|
not isUnreachableInCall(arg, outercc.(CallContextSpecificCall).getCall())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,8 +152,10 @@ private module ImplCommon {
|
|||||||
DataFlowCall call, ParameterNode p, ArgumentNode arg, CallContext outercc,
|
DataFlowCall call, ParameterNode p, ArgumentNode arg, CallContext outercc,
|
||||||
CallContextCall innercc
|
CallContextCall innercc
|
||||||
) {
|
) {
|
||||||
exists(int i, DataFlowCallable callable | viableParamArg1(p, callable, i, arg, outercc, call) |
|
exists(int i, DataFlowCallable callable |
|
||||||
if reducedViableImplInCallContext(_, callable, call)
|
viableParamArg1(p, callable, i, arg, outercc, call)
|
||||||
|
|
|
||||||
|
if recordDataFlowCallSite(call, callable)
|
||||||
then innercc = TSpecificCall(call, i, true)
|
then innercc = TSpecificCall(call, i, true)
|
||||||
else innercc = TSomeCall(p, true)
|
else innercc = TSomeCall(p, true)
|
||||||
)
|
)
|
||||||
@@ -137,7 +167,7 @@ private module ImplCommon {
|
|||||||
exists(DataFlowCall call, int i, DataFlowCallable callable |
|
exists(DataFlowCall call, int i, DataFlowCallable callable |
|
||||||
result = TSpecificCall(call, i, _) and
|
result = TSpecificCall(call, i, _) and
|
||||||
p.isParameterOf(callable, i) and
|
p.isParameterOf(callable, i) and
|
||||||
reducedViableImplInCallContext(_, callable, call)
|
recordDataFlowCallSite(call, callable)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,20 +177,22 @@ private module ImplCommon {
|
|||||||
*/
|
*/
|
||||||
private predicate parameterValueFlow(ParameterNode p, Node node, CallContextCall cc) {
|
private predicate parameterValueFlow(ParameterNode p, Node node, CallContextCall cc) {
|
||||||
p = node and
|
p = node and
|
||||||
parameterValueFlowsThroughNoCtx(p, _) and
|
parameterValueFlowsThroughCand(p, _) and
|
||||||
cc = getAValidCallContextForParameter(p)
|
cc = getAValidCallContextForParameter(p)
|
||||||
or
|
or
|
||||||
exists(Node mid |
|
exists(Node mid |
|
||||||
parameterValueFlow(p, mid, cc) and
|
parameterValueFlow(p, mid, cc) and
|
||||||
simpleLocalFlowStep(mid, node) and
|
step(mid, node) and
|
||||||
compatibleTypes(p.getType(), node.getType())
|
compatibleTypes(p.getType(), node.getType()) and
|
||||||
|
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// flow through a callable
|
// flow through a callable
|
||||||
exists(Node arg |
|
exists(Node arg |
|
||||||
parameterValueFlow(p, arg, cc) and
|
parameterValueFlow(p, arg, cc) and
|
||||||
argumentValueFlowsThrough(arg, node, cc) and
|
argumentValueFlowsThrough(arg, node, cc) and
|
||||||
compatibleTypes(p.getType(), node.getType())
|
compatibleTypes(p.getType(), node.getType()) and
|
||||||
|
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,8 +200,9 @@ private module ImplCommon {
|
|||||||
* Holds if `p` can flow to a return node of kind `kind` in the same
|
* Holds if `p` can flow to a return node of kind `kind` in the same
|
||||||
* callable using only value-preserving steps, in call context `cc`.
|
* callable using only value-preserving steps, in call context `cc`.
|
||||||
*/
|
*/
|
||||||
cached
|
private predicate parameterValueFlowsThrough(
|
||||||
predicate parameterValueFlowsThrough(ParameterNode p, ReturnKind kind, CallContextCall cc) {
|
ParameterNode p, ReturnKind kind, CallContextCall cc
|
||||||
|
) {
|
||||||
parameterValueFlow(p, getAReturnNodeOfKind(kind), cc)
|
parameterValueFlow(p, getAReturnNodeOfKind(kind), cc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,13 +220,16 @@ private module ImplCommon {
|
|||||||
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
|
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
|
||||||
* in call context cc.
|
* in call context cc.
|
||||||
*/
|
*/
|
||||||
cached
|
|
||||||
predicate argumentValueFlowsThrough(ArgumentNode arg, OutNode out, CallContext cc) {
|
predicate argumentValueFlowsThrough(ArgumentNode arg, OutNode out, CallContext cc) {
|
||||||
exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, cc) |
|
exists(DataFlowCall call, ReturnKind kind |
|
||||||
|
argumentValueFlowsThrough0(call, arg, kind, cc)
|
||||||
|
|
|
||||||
out = getAnOutNode(call, kind) and
|
out = getAnOutNode(call, kind) and
|
||||||
|
not isUnreachableInCall(out, cc.(CallContextSpecificCall).getCall()) and
|
||||||
compatibleTypes(arg.getType(), out.getType())
|
compatibleTypes(arg.getType(), out.getType())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `p` can flow to the pre-update node of `n` in the same callable
|
* Holds if `p` can flow to the pre-update node of `n` in the same callable
|
||||||
@@ -210,7 +246,22 @@ private module ImplCommon {
|
|||||||
*/
|
*/
|
||||||
private predicate localValueStep(Node node1, Node node2) {
|
private predicate localValueStep(Node node1, Node node2) {
|
||||||
simpleLocalFlowStep(node1, node2) or
|
simpleLocalFlowStep(node1, node2) or
|
||||||
argumentValueFlowsThrough(node1, node2, _)
|
FlowThrough_v1::argumentValueFlowsThrough(node1, node2, _)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `p` can flow to `node` in the same callable allowing local flow
|
||||||
|
* steps and value flow through methods. Call contexts are only accounted
|
||||||
|
* for in the nested calls.
|
||||||
|
*/
|
||||||
|
private predicate parameterValueFlowNoCtx(ParameterNode p, Node node) {
|
||||||
|
p = node
|
||||||
|
or
|
||||||
|
exists(Node mid |
|
||||||
|
parameterValueFlowNoCtx(p, mid) and
|
||||||
|
localValueStep(mid, node) and
|
||||||
|
compatibleTypes(p.getType(), node.getType())
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -332,10 +383,202 @@ private module ImplCommon {
|
|||||||
exists(Node mid1, Node mid2, Content f |
|
exists(Node mid1, Node mid2, Content f |
|
||||||
store(node1, f, mid1) and
|
store(node1, f, mid1) and
|
||||||
localValueStep*(mid1, mid2) and
|
localValueStep*(mid1, mid2) and
|
||||||
read(mid2, f, node2)
|
read(mid2, f, node2) and
|
||||||
|
compatibleTypes(node1.getTypeBound(), node2.getTypeBound())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cached
|
||||||
|
module FlowThrough_v2 {
|
||||||
|
private predicate step(Node node1, Node node2) {
|
||||||
|
simpleLocalFlowStep(node1, node2) or
|
||||||
|
localStoreReadStep(node1, node2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `p` can flow to `node` in the same callable using only
|
||||||
|
* value-preserving steps, not taking call contexts into account.
|
||||||
|
*/
|
||||||
|
private predicate parameterValueFlowCand(ParameterNode p, Node node) {
|
||||||
|
p = node
|
||||||
|
or
|
||||||
|
exists(Node mid |
|
||||||
|
parameterValueFlowCand(p, mid) and
|
||||||
|
step(mid, node) and
|
||||||
|
compatibleTypes(p.getType(), node.getType())
|
||||||
|
)
|
||||||
|
or
|
||||||
|
// flow through a callable
|
||||||
|
exists(Node arg |
|
||||||
|
parameterValueFlowCand(p, arg) and
|
||||||
|
argumentValueFlowsThroughCand(arg, node) and
|
||||||
|
compatibleTypes(p.getType(), node.getType())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `p` can flow to a return node of kind `kind` in the same
|
||||||
|
* callable using only value-preserving steps, not taking call contexts
|
||||||
|
* into account.
|
||||||
|
*/
|
||||||
|
private predicate parameterValueFlowsThroughCand(ParameterNode p, ReturnKind kind) {
|
||||||
|
parameterValueFlowCand(p, getAReturnNodeOfKind(kind))
|
||||||
|
}
|
||||||
|
|
||||||
|
pragma[nomagic]
|
||||||
|
private predicate argumentValueFlowsThroughCand0(
|
||||||
|
DataFlowCall call, ArgumentNode arg, ReturnKind kind
|
||||||
|
) {
|
||||||
|
exists(ParameterNode param | viableParamArg(call, param, arg) |
|
||||||
|
parameterValueFlowsThroughCand(param, kind)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
|
||||||
|
* not taking call contexts into account.
|
||||||
|
*/
|
||||||
|
private predicate argumentValueFlowsThroughCand(ArgumentNode arg, OutNode out) {
|
||||||
|
exists(DataFlowCall call, ReturnKind kind |
|
||||||
|
argumentValueFlowsThroughCand0(call, arg, kind)
|
||||||
|
|
|
||||||
|
out = getAnOutNode(call, kind) and
|
||||||
|
compatibleTypes(arg.getType(), out.getType())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `arg` is the `i`th argument of `call` inside the callable
|
||||||
|
* `enclosing`, and `arg` may flow through `call`.
|
||||||
|
*/
|
||||||
|
pragma[noinline]
|
||||||
|
private predicate argumentOf(
|
||||||
|
DataFlowCall call, int i, ArgumentNode arg, DataFlowCallable enclosing
|
||||||
|
) {
|
||||||
|
arg.argumentOf(call, i) and
|
||||||
|
argumentValueFlowsThroughCand(arg, _) and
|
||||||
|
enclosing = arg.getEnclosingCallable()
|
||||||
|
}
|
||||||
|
|
||||||
|
pragma[noinline]
|
||||||
|
private ParameterNode getAParameter(DataFlowCallable c) { result.getEnclosingCallable() = c }
|
||||||
|
|
||||||
|
pragma[noinline]
|
||||||
|
private predicate viableParamArg0(
|
||||||
|
int i, ArgumentNode arg, CallContext outercc, DataFlowCall call
|
||||||
|
) {
|
||||||
|
exists(DataFlowCallable c | argumentOf(call, i, arg, c) |
|
||||||
|
(
|
||||||
|
outercc = TAnyCallContext()
|
||||||
|
or
|
||||||
|
outercc = TSomeCall(getAParameter(c), _)
|
||||||
|
or
|
||||||
|
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
|
||||||
|
recordDataFlowCallSite(other, c)
|
||||||
|
)
|
||||||
|
) and
|
||||||
|
not isUnreachableInCall(arg, outercc.(CallContextSpecificCall).getCall())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pragma[noinline]
|
||||||
|
private predicate viableParamArg1(
|
||||||
|
ParameterNode p, DataFlowCallable callable, int i, ArgumentNode arg, CallContext outercc,
|
||||||
|
DataFlowCall call
|
||||||
|
) {
|
||||||
|
viableParamArg0(i, arg, outercc, call) and
|
||||||
|
callable = resolveCall(call, outercc) and
|
||||||
|
p.isParameterOf(callable, any(int j | j <= i and j >= i))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `arg` is a possible argument to `p`, in the call `call`, and
|
||||||
|
* `arg` may flow through `call`. The possible contexts before and after
|
||||||
|
* entering the callable are `outercc` and `innercc`, respectively.
|
||||||
|
*/
|
||||||
|
private predicate viableParamArg(
|
||||||
|
DataFlowCall call, ParameterNode p, ArgumentNode arg, CallContext outercc,
|
||||||
|
CallContextCall innercc
|
||||||
|
) {
|
||||||
|
exists(int i, DataFlowCallable callable |
|
||||||
|
viableParamArg1(p, callable, i, arg, outercc, call)
|
||||||
|
|
|
||||||
|
if recordDataFlowCallSite(call, callable)
|
||||||
|
then innercc = TSpecificCall(call, i, true)
|
||||||
|
else innercc = TSomeCall(p, true)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private CallContextCall getAValidCallContextForParameter(ParameterNode p) {
|
||||||
|
result = TSomeCall(p, _)
|
||||||
|
or
|
||||||
|
exists(DataFlowCall call, int i, DataFlowCallable callable |
|
||||||
|
result = TSpecificCall(call, i, _) and
|
||||||
|
p.isParameterOf(callable, i) and
|
||||||
|
recordDataFlowCallSite(call, callable)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `p` can flow to `node` in the same callable using only
|
||||||
|
* value-preserving steps, in call context `cc`.
|
||||||
|
*/
|
||||||
|
private predicate parameterValueFlow(ParameterNode p, Node node, CallContextCall cc) {
|
||||||
|
p = node and
|
||||||
|
parameterValueFlowsThroughCand(p, _) and
|
||||||
|
cc = getAValidCallContextForParameter(p)
|
||||||
|
or
|
||||||
|
exists(Node mid |
|
||||||
|
parameterValueFlow(p, mid, cc) and
|
||||||
|
step(mid, node) and
|
||||||
|
compatibleTypes(p.getType(), node.getType()) and
|
||||||
|
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
|
||||||
|
)
|
||||||
|
or
|
||||||
|
// flow through a callable
|
||||||
|
exists(Node arg |
|
||||||
|
parameterValueFlow(p, arg, cc) and
|
||||||
|
argumentValueFlowsThrough(arg, node, cc) and
|
||||||
|
compatibleTypes(p.getType(), node.getType()) and
|
||||||
|
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `p` can flow to a return node of kind `kind` in the same
|
||||||
|
* callable using only value-preserving steps, in call context `cc`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate parameterValueFlowsThrough(ParameterNode p, ReturnKind kind, CallContextCall cc) {
|
||||||
|
parameterValueFlow(p, getAReturnNodeOfKind(kind), cc)
|
||||||
|
}
|
||||||
|
|
||||||
|
pragma[nomagic]
|
||||||
|
private predicate argumentValueFlowsThrough0(
|
||||||
|
DataFlowCall call, ArgumentNode arg, ReturnKind kind, CallContext cc
|
||||||
|
) {
|
||||||
|
exists(ParameterNode param, CallContext innercc |
|
||||||
|
viableParamArg(call, param, arg, cc, innercc) and
|
||||||
|
parameterValueFlowsThrough(param, kind, innercc)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `arg` flows to `out` through a call using only value-preserving steps,
|
||||||
|
* in call context cc.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate argumentValueFlowsThrough(ArgumentNode arg, OutNode out, CallContext cc) {
|
||||||
|
exists(DataFlowCall call, ReturnKind kind |
|
||||||
|
argumentValueFlowsThrough0(call, arg, kind, cc)
|
||||||
|
|
|
||||||
|
out = getAnOutNode(call, kind) and
|
||||||
|
not isUnreachableInCall(out, cc.(CallContextSpecificCall).getCall()) and
|
||||||
|
compatibleTypes(arg.getType(), out.getType())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `call` passes an implicit or explicit instance argument, i.e., an
|
* Holds if `call` passes an implicit or explicit instance argument, i.e., an
|
||||||
* expression that reaches a `this` parameter.
|
* expression that reaches a `this` parameter.
|
||||||
@@ -344,11 +587,22 @@ private module ImplCommon {
|
|||||||
exists(ArgumentNode arg | arg.argumentOf(call, -1))
|
exists(ArgumentNode arg | arg.argumentOf(call, -1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if the call context `call` either improves virtual dispatch in
|
||||||
|
* `callable` or if it allows us to prune unreachable nodes in `callable`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
|
||||||
|
reducedViableImplInCallContext(_, callable, call)
|
||||||
|
or
|
||||||
|
exists(Node n | n.getEnclosingCallable() = callable | isUnreachableInCall(n, call))
|
||||||
|
}
|
||||||
|
|
||||||
cached
|
cached
|
||||||
newtype TCallContext =
|
newtype TCallContext =
|
||||||
TAnyCallContext() or
|
TAnyCallContext() or
|
||||||
TSpecificCall(DataFlowCall call, int i, boolean emptyAp) {
|
TSpecificCall(DataFlowCall call, int i, boolean emptyAp) {
|
||||||
reducedViableImplInCallContext(_, _, call) and
|
recordDataFlowCallSite(call, _) and
|
||||||
(emptyAp = true or emptyAp = false) and
|
(emptyAp = true or emptyAp = false) and
|
||||||
(
|
(
|
||||||
exists(call.getArgument(i))
|
exists(call.getArgument(i))
|
||||||
@@ -362,25 +616,29 @@ private module ImplCommon {
|
|||||||
cached
|
cached
|
||||||
newtype TReturnPosition =
|
newtype TReturnPosition =
|
||||||
TReturnPosition0(DataFlowCallable c, ReturnKind kind) { returnPosition(_, c, kind) }
|
TReturnPosition0(DataFlowCallable c, ReturnKind kind) { returnPosition(_, c, kind) }
|
||||||
}
|
|
||||||
|
|
||||||
import ImplCommon
|
cached
|
||||||
|
newtype TLocalFlowCallContext =
|
||||||
|
TAnyLocalCall() or
|
||||||
|
TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) }
|
||||||
|
}
|
||||||
|
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
private predicate returnPosition(ReturnNode ret, DataFlowCallable c, ReturnKind kind) {
|
private predicate returnPosition(ReturnNode ret, DataFlowCallable c, ReturnKind kind) {
|
||||||
c = returnNodeGetEnclosingCallable(ret) and
|
c = returnNodeGetEnclosingCallable(ret) and
|
||||||
kind = ret.getKind()
|
kind = ret.getKind()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A call context to restrict the targets of virtual dispatch and match the
|
* A call context to restrict the targets of virtual dispatch, prune local flow,
|
||||||
* call sites of flow into a method with flow out of a method.
|
* and match the call sites of flow into a method with flow out of a method.
|
||||||
*
|
*
|
||||||
* There are four cases:
|
* There are four cases:
|
||||||
* - `TAnyCallContext()` : No restrictions on method flow.
|
* - `TAnyCallContext()` : No restrictions on method flow.
|
||||||
* - `TSpecificCall(DataFlowCall call, int i)` : Flow entered through the `i`th
|
* - `TSpecificCall(DataFlowCall call, int i)` : Flow entered through the `i`th
|
||||||
* parameter at the given `call`. This call improves the set of viable
|
* parameter at the given `call`. This call improves the set of viable
|
||||||
* dispatch targets for at least one method call in the current callable.
|
* dispatch targets for at least one method call in the current callable
|
||||||
|
* or helps prune unreachable nodes in the current callable.
|
||||||
* - `TSomeCall(ParameterNode p)` : Flow entered through parameter `p`. The
|
* - `TSomeCall(ParameterNode p)` : Flow entered through parameter `p`. The
|
||||||
* originating call does not improve the set of dispatch targets for any
|
* originating call does not improve the set of dispatch targets for any
|
||||||
* method call in the current callable and was therefore not recorded.
|
* method call in the current callable and was therefore not recorded.
|
||||||
@@ -388,36 +646,98 @@ private predicate returnPosition(ReturnNode ret, DataFlowCallable c, ReturnKind
|
|||||||
* this dispatch target of `call` implies a reduced set of dispatch origins
|
* this dispatch target of `call` implies a reduced set of dispatch origins
|
||||||
* to which data may flow if it should reach a `return` statement.
|
* to which data may flow if it should reach a `return` statement.
|
||||||
*/
|
*/
|
||||||
abstract class CallContext extends TCallContext {
|
abstract class CallContext extends TCallContext {
|
||||||
abstract string toString();
|
abstract string toString();
|
||||||
}
|
|
||||||
|
|
||||||
class CallContextAny extends CallContext, TAnyCallContext {
|
/** Holds if this call context is relevant for `callable`. */
|
||||||
|
abstract predicate relevantFor(DataFlowCallable callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
class CallContextAny extends CallContext, TAnyCallContext {
|
||||||
override string toString() { result = "CcAny" }
|
override string toString() { result = "CcAny" }
|
||||||
}
|
|
||||||
|
|
||||||
abstract class CallContextCall extends CallContext { }
|
override predicate relevantFor(DataFlowCallable callable) { any() }
|
||||||
|
}
|
||||||
|
|
||||||
class CallContextSpecificCall extends CallContextCall, TSpecificCall {
|
abstract class CallContextCall extends CallContext { }
|
||||||
|
|
||||||
|
class CallContextSpecificCall extends CallContextCall, TSpecificCall {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
exists(DataFlowCall call, int i | this = TSpecificCall(call, i, _) |
|
exists(DataFlowCall call, int i | this = TSpecificCall(call, i, _) |
|
||||||
result = "CcCall(" + call + ", " + i + ")"
|
result = "CcCall(" + call + ", " + i + ")"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class CallContextSomeCall extends CallContextCall, TSomeCall {
|
override predicate relevantFor(DataFlowCallable callable) {
|
||||||
|
recordDataFlowCallSite(getCall(), callable)
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFlowCall getCall() { this = TSpecificCall(result, _, _) }
|
||||||
|
}
|
||||||
|
|
||||||
|
class CallContextSomeCall extends CallContextCall, TSomeCall {
|
||||||
override string toString() { result = "CcSomeCall" }
|
override string toString() { result = "CcSomeCall" }
|
||||||
}
|
|
||||||
|
|
||||||
class CallContextReturn extends CallContext, TReturn {
|
override predicate relevantFor(DataFlowCallable callable) {
|
||||||
|
exists(ParameterNode p | this = TSomeCall(p, _) and p.getEnclosingCallable() = callable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CallContextReturn extends CallContext, TReturn {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
|
exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/** A callable tagged with a relevant return kind. */
|
override predicate relevantFor(DataFlowCallable callable) {
|
||||||
class ReturnPosition extends TReturnPosition0 {
|
exists(DataFlowCall call | this = TReturn(_, call) and call.getEnclosingCallable() = callable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A call context that is relevant for pruning local flow.
|
||||||
|
*/
|
||||||
|
abstract class LocalCallContext extends TLocalFlowCallContext {
|
||||||
|
abstract string toString();
|
||||||
|
|
||||||
|
/** Holds if this call context is relevant for `callable`. */
|
||||||
|
abstract predicate relevantFor(DataFlowCallable callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
class LocalCallContextAny extends LocalCallContext, TAnyLocalCall {
|
||||||
|
override string toString() { result = "LocalCcAny" }
|
||||||
|
|
||||||
|
override predicate relevantFor(DataFlowCallable callable) { any() }
|
||||||
|
}
|
||||||
|
|
||||||
|
class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall {
|
||||||
|
LocalCallContextSpecificCall() { this = TSpecificLocalCall(call) }
|
||||||
|
|
||||||
|
DataFlowCall call;
|
||||||
|
|
||||||
|
DataFlowCall getCall() { result = call }
|
||||||
|
|
||||||
|
override string toString() { result = "LocalCcCall(" + call + ")" }
|
||||||
|
|
||||||
|
override predicate relevantFor(DataFlowCallable callable) { relevantLocalCCtx(call, callable) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) {
|
||||||
|
exists(Node n | n.getEnclosingCallable() = callable and isUnreachableInCall(n, call))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the local call context given the call context and the callable that
|
||||||
|
* the contexts apply to.
|
||||||
|
*/
|
||||||
|
LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) {
|
||||||
|
ctx.relevantFor(callable) and
|
||||||
|
if relevantLocalCCtx(ctx.(CallContextSpecificCall).getCall(), callable)
|
||||||
|
then result.(LocalCallContextSpecificCall).getCall() = ctx.(CallContextSpecificCall).getCall()
|
||||||
|
else result instanceof LocalCallContextAny
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A callable tagged with a relevant return kind. */
|
||||||
|
class ReturnPosition extends TReturnPosition0 {
|
||||||
private DataFlowCallable c;
|
private DataFlowCallable c;
|
||||||
private ReturnKind kind;
|
private ReturnKind kind;
|
||||||
|
|
||||||
@@ -431,22 +751,22 @@ class ReturnPosition extends TReturnPosition0 {
|
|||||||
|
|
||||||
/** Gets a textual representation of this return position. */
|
/** Gets a textual representation of this return position. */
|
||||||
string toString() { result = "[" + kind + "] " + c }
|
string toString() { result = "[" + kind + "] " + c }
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
DataFlowCallable returnNodeGetEnclosingCallable(ReturnNode ret) {
|
DataFlowCallable returnNodeGetEnclosingCallable(ReturnNode ret) {
|
||||||
result = ret.getEnclosingCallable()
|
result = ret.getEnclosingCallable()
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
ReturnPosition getReturnPosition(ReturnNode ret) {
|
ReturnPosition getReturnPosition(ReturnNode ret) {
|
||||||
exists(DataFlowCallable c, ReturnKind k | returnPosition(ret, c, k) |
|
exists(DataFlowCallable c, ReturnKind k | returnPosition(ret, c, k) |
|
||||||
result = TReturnPosition0(c, k)
|
result = TReturnPosition0(c, k)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
bindingset[cc, callable]
|
bindingset[cc, callable]
|
||||||
predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
|
predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
|
||||||
cc instanceof CallContextAny and callable = viableCallable(call)
|
cc instanceof CallContextAny and callable = viableCallable(call)
|
||||||
or
|
or
|
||||||
exists(DataFlowCallable c0, DataFlowCall call0 |
|
exists(DataFlowCallable c0, DataFlowCall call0 |
|
||||||
@@ -454,10 +774,10 @@ predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall
|
|||||||
cc = TReturn(c0, call0) and
|
cc = TReturn(c0, call0) and
|
||||||
c0 = prunedViableImplInCallContextReverse(call0, call)
|
c0 = prunedViableImplInCallContextReverse(call0, call)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
bindingset[call, cc]
|
bindingset[call, cc]
|
||||||
DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
|
DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
|
||||||
exists(DataFlowCall ctx | cc = TSpecificCall(ctx, _, _) |
|
exists(DataFlowCall ctx | cc = TSpecificCall(ctx, _, _) |
|
||||||
if reducedViableImplInCallContext(call, _, ctx)
|
if reducedViableImplInCallContext(call, _, ctx)
|
||||||
then result = prunedViableImplInCallContext(call, ctx)
|
then result = prunedViableImplInCallContext(call, ctx)
|
||||||
@@ -469,4 +789,5 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
|
|||||||
result = viableCallable(call) and cc instanceof CallContextAny
|
result = viableCallable(call) and cc instanceof CallContextAny
|
||||||
or
|
or
|
||||||
result = viableCallable(call) and cc instanceof CallContextReturn
|
result = viableCallable(call) and cc instanceof CallContextReturn
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -204,3 +204,5 @@ class DataFlowCall extends CallInstruction {
|
|||||||
|
|
||||||
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
|
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
|
||||||
|
|||||||
@@ -66,11 +66,14 @@ private newtype TOpcode =
|
|||||||
TCallSideEffect() or
|
TCallSideEffect() or
|
||||||
TCallReadSideEffect() or
|
TCallReadSideEffect() or
|
||||||
TIndirectReadSideEffect() or
|
TIndirectReadSideEffect() or
|
||||||
TIndirectWriteSideEffect() or
|
TIndirectMustWriteSideEffect() or
|
||||||
TIndirectMayWriteSideEffect() or
|
TIndirectMayWriteSideEffect() or
|
||||||
TBufferReadSideEffect() or
|
TBufferReadSideEffect() or
|
||||||
TBufferWriteSideEffect() or
|
TBufferMustWriteSideEffect() or
|
||||||
TBufferMayWriteSideEffect() or
|
TBufferMayWriteSideEffect() or
|
||||||
|
TSizedBufferReadSideEffect() or
|
||||||
|
TSizedBufferMustWriteSideEffect() or
|
||||||
|
TSizedBufferMayWriteSideEffect() or
|
||||||
TChi() or
|
TChi() or
|
||||||
TInlineAsm() or
|
TInlineAsm() or
|
||||||
TUnreached() or
|
TUnreached() or
|
||||||
@@ -135,17 +138,28 @@ abstract class ReadSideEffectOpcode extends SideEffectOpcode { }
|
|||||||
*/
|
*/
|
||||||
abstract class WriteSideEffectOpcode extends SideEffectOpcode { }
|
abstract class WriteSideEffectOpcode extends SideEffectOpcode { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An opcode that definitely writes to a set of memory locations as a side effect.
|
||||||
|
*/
|
||||||
|
abstract class MustWriteSideEffectOpcode extends WriteSideEffectOpcode { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An opcode that may overwrite some, all, or none of an existing set of memory locations. Modeled
|
* An opcode that may overwrite some, all, or none of an existing set of memory locations. Modeled
|
||||||
* as a read of the original contents, plus a "may" write of the new contents.
|
* as a read of the original contents, plus a "may" write of the new contents.
|
||||||
*/
|
*/
|
||||||
abstract class MayWriteSideEffectOpcode extends SideEffectOpcode { }
|
abstract class MayWriteSideEffectOpcode extends WriteSideEffectOpcode { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An opcode that accesses a buffer via an `AddressOperand` and a `BufferSizeOperand`.
|
* An opcode that accesses a buffer via an `AddressOperand`.
|
||||||
*/
|
*/
|
||||||
abstract class BufferAccessOpcode extends MemoryAccessOpcode { }
|
abstract class BufferAccessOpcode extends MemoryAccessOpcode { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An opcode that accesses a buffer via an `AddressOperand` with a `BufferSizeOperand` specifying
|
||||||
|
* the number of elements accessed.
|
||||||
|
*/
|
||||||
|
abstract class SizedBufferAccessOpcode extends BufferAccessOpcode { }
|
||||||
|
|
||||||
module Opcode {
|
module Opcode {
|
||||||
class NoOp extends Opcode, TNoOp {
|
class NoOp extends Opcode, TNoOp {
|
||||||
final override string toString() { result = "NoOp" }
|
final override string toString() { result = "NoOp" }
|
||||||
@@ -416,9 +430,9 @@ module Opcode {
|
|||||||
final override string toString() { result = "IndirectReadSideEffect" }
|
final override string toString() { result = "IndirectReadSideEffect" }
|
||||||
}
|
}
|
||||||
|
|
||||||
class IndirectWriteSideEffect extends WriteSideEffectOpcode, MemoryAccessOpcode,
|
class IndirectMustWriteSideEffect extends MustWriteSideEffectOpcode, MemoryAccessOpcode,
|
||||||
TIndirectWriteSideEffect {
|
TIndirectMustWriteSideEffect {
|
||||||
final override string toString() { result = "IndirectWriteSideEffect" }
|
final override string toString() { result = "IndirectMustWriteSideEffect" }
|
||||||
}
|
}
|
||||||
|
|
||||||
class IndirectMayWriteSideEffect extends MayWriteSideEffectOpcode, MemoryAccessOpcode,
|
class IndirectMayWriteSideEffect extends MayWriteSideEffectOpcode, MemoryAccessOpcode,
|
||||||
@@ -430,9 +444,9 @@ module Opcode {
|
|||||||
final override string toString() { result = "BufferReadSideEffect" }
|
final override string toString() { result = "BufferReadSideEffect" }
|
||||||
}
|
}
|
||||||
|
|
||||||
class BufferWriteSideEffect extends WriteSideEffectOpcode, BufferAccessOpcode,
|
class BufferMustWriteSideEffect extends MustWriteSideEffectOpcode, BufferAccessOpcode,
|
||||||
TBufferWriteSideEffect {
|
TBufferMustWriteSideEffect {
|
||||||
final override string toString() { result = "BufferWriteSideEffect" }
|
final override string toString() { result = "BufferMustWriteSideEffect" }
|
||||||
}
|
}
|
||||||
|
|
||||||
class BufferMayWriteSideEffect extends MayWriteSideEffectOpcode, BufferAccessOpcode,
|
class BufferMayWriteSideEffect extends MayWriteSideEffectOpcode, BufferAccessOpcode,
|
||||||
@@ -440,6 +454,21 @@ module Opcode {
|
|||||||
final override string toString() { result = "BufferMayWriteSideEffect" }
|
final override string toString() { result = "BufferMayWriteSideEffect" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SizedBufferReadSideEffect extends ReadSideEffectOpcode, SizedBufferAccessOpcode,
|
||||||
|
TSizedBufferReadSideEffect {
|
||||||
|
final override string toString() { result = "SizedBufferReadSideEffect" }
|
||||||
|
}
|
||||||
|
|
||||||
|
class SizedBufferMustWriteSideEffect extends MustWriteSideEffectOpcode, SizedBufferAccessOpcode,
|
||||||
|
TSizedBufferMustWriteSideEffect {
|
||||||
|
final override string toString() { result = "SizedBufferMustWriteSideEffect" }
|
||||||
|
}
|
||||||
|
|
||||||
|
class SizedBufferMayWriteSideEffect extends MayWriteSideEffectOpcode, SizedBufferAccessOpcode,
|
||||||
|
TSizedBufferMayWriteSideEffect {
|
||||||
|
final override string toString() { result = "SizedBufferMayWriteSideEffect" }
|
||||||
|
}
|
||||||
|
|
||||||
class Chi extends Opcode, TChi {
|
class Chi extends Opcode, TChi {
|
||||||
final override string toString() { result = "Chi" }
|
final override string toString() { result = "Chi" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ module InstructionSanity {
|
|||||||
or
|
or
|
||||||
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag
|
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag
|
||||||
or
|
or
|
||||||
opcode instanceof BufferAccessOpcode and tag instanceof BufferSizeOperand
|
opcode instanceof SizedBufferAccessOpcode and tag instanceof BufferSizeOperandTag
|
||||||
or
|
or
|
||||||
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag
|
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag
|
||||||
or
|
or
|
||||||
@@ -48,8 +48,8 @@ module InstructionSanity {
|
|||||||
or
|
or
|
||||||
(
|
(
|
||||||
opcode instanceof ReadSideEffectOpcode or
|
opcode instanceof ReadSideEffectOpcode or
|
||||||
opcode instanceof MayWriteSideEffectOpcode or
|
opcode instanceof Opcode::InlineAsm or
|
||||||
opcode instanceof Opcode::InlineAsm
|
opcode instanceof Opcode::CallSideEffect
|
||||||
) and
|
) and
|
||||||
tag instanceof SideEffectOperandTag
|
tag instanceof SideEffectOperandTag
|
||||||
)
|
)
|
||||||
@@ -120,6 +120,15 @@ module InstructionSanity {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query predicate sideEffectWithoutPrimary(
|
||||||
|
SideEffectInstruction instr, string message, IRFunction func, string funcText
|
||||||
|
) {
|
||||||
|
not exists(instr.getPrimaryInstruction()) and
|
||||||
|
message = "Side effect instruction missing primary instruction in function $@" and
|
||||||
|
func = instr.getEnclosingIRFunction() and
|
||||||
|
funcText = Language::getIdentityString(func.getFunction())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
||||||
*/
|
*/
|
||||||
@@ -609,9 +618,14 @@ class VariableInstruction extends Instruction {
|
|||||||
|
|
||||||
VariableInstruction() { var = Construction::getInstructionVariable(this) }
|
VariableInstruction() { var = Construction::getInstructionVariable(this) }
|
||||||
|
|
||||||
final override string getImmediateString() { result = var.toString() }
|
override string getImmediateString() { result = var.toString() }
|
||||||
|
|
||||||
final IRVariable getVariable() { result = var }
|
final IRVariable getIRVariable() { result = var }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the AST variable that this instruction's IR variable refers to, if one exists.
|
||||||
|
*/
|
||||||
|
final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() }
|
||||||
}
|
}
|
||||||
|
|
||||||
class FieldInstruction extends Instruction {
|
class FieldInstruction extends Instruction {
|
||||||
@@ -644,6 +658,16 @@ class ConstantValueInstruction extends Instruction {
|
|||||||
final string getValue() { result = value }
|
final string getValue() { result = value }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class IndexedInstruction extends Instruction {
|
||||||
|
int index;
|
||||||
|
|
||||||
|
IndexedInstruction() { index = Construction::getInstructionIndex(this) }
|
||||||
|
|
||||||
|
final override string getImmediateString() { result = index.toString() }
|
||||||
|
|
||||||
|
final int getIndex() { result = index }
|
||||||
|
}
|
||||||
|
|
||||||
class EnterFunctionInstruction extends Instruction {
|
class EnterFunctionInstruction extends Instruction {
|
||||||
EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction }
|
EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction }
|
||||||
}
|
}
|
||||||
@@ -1175,6 +1199,8 @@ class CallReadSideEffectInstruction extends SideEffectInstruction {
|
|||||||
*/
|
*/
|
||||||
class IndirectReadSideEffectInstruction extends SideEffectInstruction {
|
class IndirectReadSideEffectInstruction extends SideEffectInstruction {
|
||||||
IndirectReadSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectReadSideEffect }
|
IndirectReadSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectReadSideEffect }
|
||||||
|
|
||||||
|
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1182,13 +1208,39 @@ class IndirectReadSideEffectInstruction extends SideEffectInstruction {
|
|||||||
*/
|
*/
|
||||||
class BufferReadSideEffectInstruction extends SideEffectInstruction {
|
class BufferReadSideEffectInstruction extends SideEffectInstruction {
|
||||||
BufferReadSideEffectInstruction() { getOpcode() instanceof Opcode::BufferReadSideEffect }
|
BufferReadSideEffectInstruction() { getOpcode() instanceof Opcode::BufferReadSideEffect }
|
||||||
|
|
||||||
|
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instruction representing the read of an indirect buffer parameter within a function call.
|
||||||
|
*/
|
||||||
|
class SizedBufferReadSideEffectInstruction extends SideEffectInstruction {
|
||||||
|
SizedBufferReadSideEffectInstruction() {
|
||||||
|
getOpcode() instanceof Opcode::SizedBufferReadSideEffect
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||||
|
|
||||||
|
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instruction representing a side effect of a function call.
|
||||||
|
*/
|
||||||
|
class WriteSideEffectInstruction extends SideEffectInstruction {
|
||||||
|
WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode }
|
||||||
|
|
||||||
|
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An instruction representing the write of an indirect parameter within a function call.
|
* An instruction representing the write of an indirect parameter within a function call.
|
||||||
*/
|
*/
|
||||||
class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
|
class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||||
IndirectWriteSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectWriteSideEffect }
|
IndirectMustWriteSideEffectInstruction() {
|
||||||
|
getOpcode() instanceof Opcode::IndirectMustWriteSideEffect
|
||||||
|
}
|
||||||
|
|
||||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||||
}
|
}
|
||||||
@@ -1197,18 +1249,34 @@ class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
|
|||||||
* An instruction representing the write of an indirect buffer parameter within a function call. The
|
* An instruction representing the write of an indirect buffer parameter within a function call. The
|
||||||
* entire buffer is overwritten.
|
* entire buffer is overwritten.
|
||||||
*/
|
*/
|
||||||
class BufferWriteSideEffectInstruction extends SideEffectInstruction {
|
class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||||
BufferWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferWriteSideEffect }
|
BufferMustWriteSideEffectInstruction() {
|
||||||
|
getOpcode() instanceof Opcode::BufferMustWriteSideEffect
|
||||||
|
}
|
||||||
|
|
||||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instruction representing the write of an indirect buffer parameter within a function call. The
|
||||||
|
* entire buffer is overwritten.
|
||||||
|
*/
|
||||||
|
class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||||
|
SizedBufferMustWriteSideEffectInstruction() {
|
||||||
|
getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect
|
||||||
|
}
|
||||||
|
|
||||||
|
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||||
|
|
||||||
|
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An instruction representing the potential write of an indirect parameter within a function call.
|
* An instruction representing the potential write of an indirect parameter within a function call.
|
||||||
* Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten.
|
* Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten.
|
||||||
* written.
|
* written.
|
||||||
*/
|
*/
|
||||||
class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
|
class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||||
IndirectMayWriteSideEffectInstruction() {
|
IndirectMayWriteSideEffectInstruction() {
|
||||||
getOpcode() instanceof Opcode::IndirectMayWriteSideEffect
|
getOpcode() instanceof Opcode::IndirectMayWriteSideEffect
|
||||||
}
|
}
|
||||||
@@ -1222,7 +1290,7 @@ class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
|
|||||||
* An instruction representing the write of an indirect buffer parameter within a function call.
|
* An instruction representing the write of an indirect buffer parameter within a function call.
|
||||||
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
|
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
|
||||||
*/
|
*/
|
||||||
class BufferMayWriteSideEffectInstruction extends SideEffectInstruction {
|
class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||||
BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect }
|
BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect }
|
||||||
|
|
||||||
final override MemoryAccessKind getResultMemoryAccess() {
|
final override MemoryAccessKind getResultMemoryAccess() {
|
||||||
@@ -1230,6 +1298,22 @@ class BufferMayWriteSideEffectInstruction extends SideEffectInstruction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instruction representing the write of an indirect buffer parameter within a function call.
|
||||||
|
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
|
||||||
|
*/
|
||||||
|
class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||||
|
SizedBufferMayWriteSideEffectInstruction() {
|
||||||
|
getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect
|
||||||
|
}
|
||||||
|
|
||||||
|
final override MemoryAccessKind getResultMemoryAccess() {
|
||||||
|
result instanceof BufferMayMemoryAccess
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An instruction representing a GNU or MSVC inline assembly statement.
|
* An instruction representing a GNU or MSVC inline assembly statement.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -254,6 +254,16 @@ class AddressOperand extends RegisterOperand {
|
|||||||
override string toString() { result = "Address" }
|
override string toString() { result = "Address" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The buffer size operand of an instruction that represents a read or write of
|
||||||
|
* a buffer.
|
||||||
|
*/
|
||||||
|
class BufferSizeOperand extends RegisterOperand {
|
||||||
|
override BufferSizeOperandTag tag;
|
||||||
|
|
||||||
|
override string toString() { result = "BufferSize" }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The source value operand of an instruction that loads a value from memory (e.g. `Load`,
|
* The source value operand of an instruction that loads a value from memory (e.g. `Load`,
|
||||||
* `ReturnValue`, `ThrowValue`).
|
* `ReturnValue`, `ThrowValue`).
|
||||||
@@ -390,10 +400,10 @@ class SideEffectOperand extends TypedOperand {
|
|||||||
useInstr instanceof BufferReadSideEffectInstruction and
|
useInstr instanceof BufferReadSideEffectInstruction and
|
||||||
result instanceof BufferMemoryAccess
|
result instanceof BufferMemoryAccess
|
||||||
or
|
or
|
||||||
useInstr instanceof IndirectWriteSideEffectInstruction and
|
useInstr instanceof IndirectMustWriteSideEffectInstruction and
|
||||||
result instanceof IndirectMemoryAccess
|
result instanceof IndirectMemoryAccess
|
||||||
or
|
or
|
||||||
useInstr instanceof BufferWriteSideEffectInstruction and
|
useInstr instanceof BufferMustWriteSideEffectInstruction and
|
||||||
result instanceof BufferMemoryAccess
|
result instanceof BufferMemoryAccess
|
||||||
or
|
or
|
||||||
useInstr instanceof IndirectMayWriteSideEffectInstruction and
|
useInstr instanceof IndirectMayWriteSideEffectInstruction and
|
||||||
|
|||||||
@@ -135,14 +135,14 @@ private predicate variableAddressValueNumber(
|
|||||||
VariableAddressInstruction instr, IRFunction irFunc, IRVariable var
|
VariableAddressInstruction instr, IRFunction irFunc, IRVariable var
|
||||||
) {
|
) {
|
||||||
instr.getEnclosingIRFunction() = irFunc and
|
instr.getEnclosingIRFunction() = irFunc and
|
||||||
instr.getVariable() = var
|
instr.getIRVariable() = var
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate initializeParameterValueNumber(
|
private predicate initializeParameterValueNumber(
|
||||||
InitializeParameterInstruction instr, IRFunction irFunc, IRVariable var
|
InitializeParameterInstruction instr, IRFunction irFunc, IRVariable var
|
||||||
) {
|
) {
|
||||||
instr.getEnclosingIRFunction() = irFunc and
|
instr.getEnclosingIRFunction() = irFunc and
|
||||||
instr.getVariable() = var
|
instr.getIRVariable() = var
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
|
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ private predicate automaticVariableAddressEscapes(IRAutomaticVariable var) {
|
|||||||
// The variable's address escapes if the result of any
|
// The variable's address escapes if the result of any
|
||||||
// VariableAddressInstruction that computes the variable's address escapes.
|
// VariableAddressInstruction that computes the variable's address escapes.
|
||||||
exists(VariableAddressInstruction instr |
|
exists(VariableAddressInstruction instr |
|
||||||
instr.getVariable() = var and
|
instr.getIRVariable() = var and
|
||||||
resultEscapesNonReturn(instr)
|
resultEscapesNonReturn(instr)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -305,7 +305,7 @@ predicate variableAddressEscapes(IRVariable var) {
|
|||||||
*/
|
*/
|
||||||
predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset) {
|
predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset) {
|
||||||
// The address of a variable points to that variable, at offset 0.
|
// The address of a variable points to that variable, at offset 0.
|
||||||
instr.(VariableAddressInstruction).getVariable() = var and
|
instr.(VariableAddressInstruction).getIRVariable() = var and
|
||||||
bitOffset = 0
|
bitOffset = 0
|
||||||
or
|
or
|
||||||
exists(Operand operand, IntValue originalBitOffset, IntValue propagatedBitOffset |
|
exists(Operand operand, IntValue originalBitOffset, IntValue propagatedBitOffset |
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ private module Cached {
|
|||||||
IRVariable getInstructionVariable(Instruction instruction) {
|
IRVariable getInstructionVariable(Instruction instruction) {
|
||||||
result = getNewIRVariable(getOldInstruction(instruction)
|
result = getNewIRVariable(getOldInstruction(instruction)
|
||||||
.(OldIR::VariableInstruction)
|
.(OldIR::VariableInstruction)
|
||||||
.getVariable())
|
.getIRVariable())
|
||||||
}
|
}
|
||||||
|
|
||||||
cached
|
cached
|
||||||
@@ -342,6 +342,11 @@ private module Cached {
|
|||||||
result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField()
|
result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cached
|
||||||
|
int getInstructionIndex(Instruction instruction) {
|
||||||
|
result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex()
|
||||||
|
}
|
||||||
|
|
||||||
cached
|
cached
|
||||||
Function getInstructionFunction(Instruction instruction) {
|
Function getInstructionFunction(Instruction instruction) {
|
||||||
result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
|
result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
|
||||||
|
|||||||
@@ -66,12 +66,14 @@ AddressOperandTag addressOperand() { result = TAddressOperand() }
|
|||||||
* The buffer size operand of an instruction that represents a read or write of
|
* The buffer size operand of an instruction that represents a read or write of
|
||||||
* a buffer.
|
* a buffer.
|
||||||
*/
|
*/
|
||||||
class BufferSizeOperand extends RegisterOperandTag, TBufferSizeOperand {
|
class BufferSizeOperandTag extends RegisterOperandTag, TBufferSizeOperand {
|
||||||
final override string toString() { result = "BufferSize" }
|
final override string toString() { result = "BufferSize" }
|
||||||
|
|
||||||
final override int getSortOrder() { result = 1 }
|
final override int getSortOrder() { result = 1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BufferSizeOperandTag bufferSizeOperand() { result = TBufferSizeOperand() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The operand representing the read side effect of a `SideEffectInstruction`.
|
* The operand representing the read side effect of a `SideEffectInstruction`.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ module InstructionSanity {
|
|||||||
or
|
or
|
||||||
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag
|
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag
|
||||||
or
|
or
|
||||||
opcode instanceof BufferAccessOpcode and tag instanceof BufferSizeOperand
|
opcode instanceof SizedBufferAccessOpcode and tag instanceof BufferSizeOperandTag
|
||||||
or
|
or
|
||||||
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag
|
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag
|
||||||
or
|
or
|
||||||
@@ -48,8 +48,8 @@ module InstructionSanity {
|
|||||||
or
|
or
|
||||||
(
|
(
|
||||||
opcode instanceof ReadSideEffectOpcode or
|
opcode instanceof ReadSideEffectOpcode or
|
||||||
opcode instanceof MayWriteSideEffectOpcode or
|
opcode instanceof Opcode::InlineAsm or
|
||||||
opcode instanceof Opcode::InlineAsm
|
opcode instanceof Opcode::CallSideEffect
|
||||||
) and
|
) and
|
||||||
tag instanceof SideEffectOperandTag
|
tag instanceof SideEffectOperandTag
|
||||||
)
|
)
|
||||||
@@ -120,6 +120,15 @@ module InstructionSanity {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query predicate sideEffectWithoutPrimary(
|
||||||
|
SideEffectInstruction instr, string message, IRFunction func, string funcText
|
||||||
|
) {
|
||||||
|
not exists(instr.getPrimaryInstruction()) and
|
||||||
|
message = "Side effect instruction missing primary instruction in function $@" and
|
||||||
|
func = instr.getEnclosingIRFunction() and
|
||||||
|
funcText = Language::getIdentityString(func.getFunction())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
||||||
*/
|
*/
|
||||||
@@ -609,9 +618,14 @@ class VariableInstruction extends Instruction {
|
|||||||
|
|
||||||
VariableInstruction() { var = Construction::getInstructionVariable(this) }
|
VariableInstruction() { var = Construction::getInstructionVariable(this) }
|
||||||
|
|
||||||
final override string getImmediateString() { result = var.toString() }
|
override string getImmediateString() { result = var.toString() }
|
||||||
|
|
||||||
final IRVariable getVariable() { result = var }
|
final IRVariable getIRVariable() { result = var }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the AST variable that this instruction's IR variable refers to, if one exists.
|
||||||
|
*/
|
||||||
|
final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() }
|
||||||
}
|
}
|
||||||
|
|
||||||
class FieldInstruction extends Instruction {
|
class FieldInstruction extends Instruction {
|
||||||
@@ -644,6 +658,16 @@ class ConstantValueInstruction extends Instruction {
|
|||||||
final string getValue() { result = value }
|
final string getValue() { result = value }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class IndexedInstruction extends Instruction {
|
||||||
|
int index;
|
||||||
|
|
||||||
|
IndexedInstruction() { index = Construction::getInstructionIndex(this) }
|
||||||
|
|
||||||
|
final override string getImmediateString() { result = index.toString() }
|
||||||
|
|
||||||
|
final int getIndex() { result = index }
|
||||||
|
}
|
||||||
|
|
||||||
class EnterFunctionInstruction extends Instruction {
|
class EnterFunctionInstruction extends Instruction {
|
||||||
EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction }
|
EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction }
|
||||||
}
|
}
|
||||||
@@ -1175,6 +1199,8 @@ class CallReadSideEffectInstruction extends SideEffectInstruction {
|
|||||||
*/
|
*/
|
||||||
class IndirectReadSideEffectInstruction extends SideEffectInstruction {
|
class IndirectReadSideEffectInstruction extends SideEffectInstruction {
|
||||||
IndirectReadSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectReadSideEffect }
|
IndirectReadSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectReadSideEffect }
|
||||||
|
|
||||||
|
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1182,13 +1208,39 @@ class IndirectReadSideEffectInstruction extends SideEffectInstruction {
|
|||||||
*/
|
*/
|
||||||
class BufferReadSideEffectInstruction extends SideEffectInstruction {
|
class BufferReadSideEffectInstruction extends SideEffectInstruction {
|
||||||
BufferReadSideEffectInstruction() { getOpcode() instanceof Opcode::BufferReadSideEffect }
|
BufferReadSideEffectInstruction() { getOpcode() instanceof Opcode::BufferReadSideEffect }
|
||||||
|
|
||||||
|
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instruction representing the read of an indirect buffer parameter within a function call.
|
||||||
|
*/
|
||||||
|
class SizedBufferReadSideEffectInstruction extends SideEffectInstruction {
|
||||||
|
SizedBufferReadSideEffectInstruction() {
|
||||||
|
getOpcode() instanceof Opcode::SizedBufferReadSideEffect
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||||
|
|
||||||
|
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instruction representing a side effect of a function call.
|
||||||
|
*/
|
||||||
|
class WriteSideEffectInstruction extends SideEffectInstruction {
|
||||||
|
WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode }
|
||||||
|
|
||||||
|
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An instruction representing the write of an indirect parameter within a function call.
|
* An instruction representing the write of an indirect parameter within a function call.
|
||||||
*/
|
*/
|
||||||
class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
|
class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||||
IndirectWriteSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectWriteSideEffect }
|
IndirectMustWriteSideEffectInstruction() {
|
||||||
|
getOpcode() instanceof Opcode::IndirectMustWriteSideEffect
|
||||||
|
}
|
||||||
|
|
||||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||||
}
|
}
|
||||||
@@ -1197,18 +1249,34 @@ class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
|
|||||||
* An instruction representing the write of an indirect buffer parameter within a function call. The
|
* An instruction representing the write of an indirect buffer parameter within a function call. The
|
||||||
* entire buffer is overwritten.
|
* entire buffer is overwritten.
|
||||||
*/
|
*/
|
||||||
class BufferWriteSideEffectInstruction extends SideEffectInstruction {
|
class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||||
BufferWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferWriteSideEffect }
|
BufferMustWriteSideEffectInstruction() {
|
||||||
|
getOpcode() instanceof Opcode::BufferMustWriteSideEffect
|
||||||
|
}
|
||||||
|
|
||||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instruction representing the write of an indirect buffer parameter within a function call. The
|
||||||
|
* entire buffer is overwritten.
|
||||||
|
*/
|
||||||
|
class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||||
|
SizedBufferMustWriteSideEffectInstruction() {
|
||||||
|
getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect
|
||||||
|
}
|
||||||
|
|
||||||
|
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||||
|
|
||||||
|
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An instruction representing the potential write of an indirect parameter within a function call.
|
* An instruction representing the potential write of an indirect parameter within a function call.
|
||||||
* Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten.
|
* Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten.
|
||||||
* written.
|
* written.
|
||||||
*/
|
*/
|
||||||
class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
|
class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||||
IndirectMayWriteSideEffectInstruction() {
|
IndirectMayWriteSideEffectInstruction() {
|
||||||
getOpcode() instanceof Opcode::IndirectMayWriteSideEffect
|
getOpcode() instanceof Opcode::IndirectMayWriteSideEffect
|
||||||
}
|
}
|
||||||
@@ -1222,7 +1290,7 @@ class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
|
|||||||
* An instruction representing the write of an indirect buffer parameter within a function call.
|
* An instruction representing the write of an indirect buffer parameter within a function call.
|
||||||
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
|
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
|
||||||
*/
|
*/
|
||||||
class BufferMayWriteSideEffectInstruction extends SideEffectInstruction {
|
class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||||
BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect }
|
BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect }
|
||||||
|
|
||||||
final override MemoryAccessKind getResultMemoryAccess() {
|
final override MemoryAccessKind getResultMemoryAccess() {
|
||||||
@@ -1230,6 +1298,22 @@ class BufferMayWriteSideEffectInstruction extends SideEffectInstruction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instruction representing the write of an indirect buffer parameter within a function call.
|
||||||
|
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
|
||||||
|
*/
|
||||||
|
class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||||
|
SizedBufferMayWriteSideEffectInstruction() {
|
||||||
|
getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect
|
||||||
|
}
|
||||||
|
|
||||||
|
final override MemoryAccessKind getResultMemoryAccess() {
|
||||||
|
result instanceof BufferMayMemoryAccess
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An instruction representing a GNU or MSVC inline assembly statement.
|
* An instruction representing a GNU or MSVC inline assembly statement.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -254,6 +254,16 @@ class AddressOperand extends RegisterOperand {
|
|||||||
override string toString() { result = "Address" }
|
override string toString() { result = "Address" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The buffer size operand of an instruction that represents a read or write of
|
||||||
|
* a buffer.
|
||||||
|
*/
|
||||||
|
class BufferSizeOperand extends RegisterOperand {
|
||||||
|
override BufferSizeOperandTag tag;
|
||||||
|
|
||||||
|
override string toString() { result = "BufferSize" }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The source value operand of an instruction that loads a value from memory (e.g. `Load`,
|
* The source value operand of an instruction that loads a value from memory (e.g. `Load`,
|
||||||
* `ReturnValue`, `ThrowValue`).
|
* `ReturnValue`, `ThrowValue`).
|
||||||
@@ -390,10 +400,10 @@ class SideEffectOperand extends TypedOperand {
|
|||||||
useInstr instanceof BufferReadSideEffectInstruction and
|
useInstr instanceof BufferReadSideEffectInstruction and
|
||||||
result instanceof BufferMemoryAccess
|
result instanceof BufferMemoryAccess
|
||||||
or
|
or
|
||||||
useInstr instanceof IndirectWriteSideEffectInstruction and
|
useInstr instanceof IndirectMustWriteSideEffectInstruction and
|
||||||
result instanceof IndirectMemoryAccess
|
result instanceof IndirectMemoryAccess
|
||||||
or
|
or
|
||||||
useInstr instanceof BufferWriteSideEffectInstruction and
|
useInstr instanceof BufferMustWriteSideEffectInstruction and
|
||||||
result instanceof BufferMemoryAccess
|
result instanceof BufferMemoryAccess
|
||||||
or
|
or
|
||||||
useInstr instanceof IndirectMayWriteSideEffectInstruction and
|
useInstr instanceof IndirectMayWriteSideEffectInstruction and
|
||||||
|
|||||||
@@ -135,14 +135,14 @@ private predicate variableAddressValueNumber(
|
|||||||
VariableAddressInstruction instr, IRFunction irFunc, IRVariable var
|
VariableAddressInstruction instr, IRFunction irFunc, IRVariable var
|
||||||
) {
|
) {
|
||||||
instr.getEnclosingIRFunction() = irFunc and
|
instr.getEnclosingIRFunction() = irFunc and
|
||||||
instr.getVariable() = var
|
instr.getIRVariable() = var
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate initializeParameterValueNumber(
|
private predicate initializeParameterValueNumber(
|
||||||
InitializeParameterInstruction instr, IRFunction irFunc, IRVariable var
|
InitializeParameterInstruction instr, IRFunction irFunc, IRVariable var
|
||||||
) {
|
) {
|
||||||
instr.getEnclosingIRFunction() = irFunc and
|
instr.getEnclosingIRFunction() = irFunc and
|
||||||
instr.getVariable() = var
|
instr.getIRVariable() = var
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
|
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
|
||||||
|
|||||||
@@ -259,6 +259,14 @@ private module Cached {
|
|||||||
.getInstructionConstantValue(getInstructionTag(instruction))
|
.getInstructionConstantValue(getInstructionTag(instruction))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cached
|
||||||
|
int getInstructionIndex(Instruction instruction) {
|
||||||
|
exists(TranslatedElement element, InstructionTag tag |
|
||||||
|
instructionOrigin(instruction, element, tag) and
|
||||||
|
result = element.getInstructionIndex(tag)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
cached
|
cached
|
||||||
StringLiteral getInstructionStringLiteral(Instruction instruction) {
|
StringLiteral getInstructionStringLiteral(Instruction instruction) {
|
||||||
result = getInstructionTranslatedElement(instruction)
|
result = getInstructionTranslatedElement(instruction)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ private import semmle.code.cpp.models.interfaces.SideEffect
|
|||||||
private import InstructionTag
|
private import InstructionTag
|
||||||
private import TranslatedElement
|
private import TranslatedElement
|
||||||
private import TranslatedExpr
|
private import TranslatedExpr
|
||||||
|
private import TranslatedFunction
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The IR translation of a call to a function. The call may be from an actual
|
* The IR translation of a call to a function. The call may be from an actual
|
||||||
@@ -22,6 +23,8 @@ abstract class TranslatedCall extends TranslatedExpr {
|
|||||||
id = -1 and result = getCallTarget()
|
id = -1 and result = getCallTarget()
|
||||||
or
|
or
|
||||||
result = getArgument(id)
|
result = getArgument(id)
|
||||||
|
or
|
||||||
|
id = getNumberOfArguments() and result = getSideEffects()
|
||||||
}
|
}
|
||||||
|
|
||||||
final override Instruction getFirstInstruction() {
|
final override Instruction getFirstInstruction() {
|
||||||
@@ -66,6 +69,9 @@ abstract class TranslatedCall extends TranslatedExpr {
|
|||||||
then result = getArgument(argIndex + 1).getFirstInstruction()
|
then result = getArgument(argIndex + 1).getFirstInstruction()
|
||||||
else result = getInstruction(CallTag())
|
else result = getInstruction(CallTag())
|
||||||
)
|
)
|
||||||
|
or
|
||||||
|
child = getSideEffects() and
|
||||||
|
result = getParent().getChildSuccessor(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||||
@@ -75,12 +81,19 @@ abstract class TranslatedCall extends TranslatedExpr {
|
|||||||
tag = CallTag() and
|
tag = CallTag() and
|
||||||
if hasSideEffect()
|
if hasSideEffect()
|
||||||
then result = getInstruction(CallSideEffectTag())
|
then result = getInstruction(CallSideEffectTag())
|
||||||
|
else
|
||||||
|
if hasPreciseSideEffect()
|
||||||
|
then result = getSideEffects().getFirstInstruction()
|
||||||
else result = getParent().getChildSuccessor(this)
|
else result = getParent().getChildSuccessor(this)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
|
(
|
||||||
hasSideEffect() and
|
hasSideEffect() and
|
||||||
tag = CallSideEffectTag() and
|
tag = CallSideEffectTag() and
|
||||||
result = getParent().getChildSuccessor(this)
|
if hasPreciseSideEffect()
|
||||||
|
then result = getSideEffects().getFirstInstruction()
|
||||||
|
else result = getParent().getChildSuccessor(this)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,6 +178,8 @@ abstract class TranslatedCall extends TranslatedExpr {
|
|||||||
*/
|
*/
|
||||||
abstract TranslatedExpr getArgument(int index);
|
abstract TranslatedExpr getArgument(int index);
|
||||||
|
|
||||||
|
abstract int getNumberOfArguments();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If there are any arguments, gets the first instruction of the first
|
* If there are any arguments, gets the first instruction of the first
|
||||||
* argument. Otherwise, returns the call instruction.
|
* argument. Otherwise, returns the call instruction.
|
||||||
@@ -191,6 +206,10 @@ abstract class TranslatedCall extends TranslatedExpr {
|
|||||||
tag = CallSideEffectTag() and
|
tag = CallSideEffectTag() and
|
||||||
result = getResult()
|
result = getResult()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
predicate hasPreciseSideEffect() { exists(getSideEffects()) }
|
||||||
|
|
||||||
|
TranslatedSideEffects getSideEffects() { result.getCall() = expr }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -245,6 +264,8 @@ abstract class TranslatedCallExpr extends TranslatedNonConstantExpr, TranslatedC
|
|||||||
final override TranslatedExpr getArgument(int index) {
|
final override TranslatedExpr getArgument(int index) {
|
||||||
result = getTranslatedExpr(expr.getArgument(index).getFullyConverted())
|
result = getTranslatedExpr(expr.getArgument(index).getFullyConverted())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final override int getNumberOfArguments() { result = expr.getNumberOfArguments() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -269,11 +290,11 @@ class TranslatedFunctionCall extends TranslatedCallExpr, TranslatedDirectCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override predicate hasReadSideEffect() {
|
override predicate hasReadSideEffect() {
|
||||||
not expr.getTarget().(SideEffectFunction).neverReadsMemory()
|
not expr.getTarget().(SideEffectFunction).hasOnlySpecificReadSideEffects()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate hasWriteSideEffect() {
|
override predicate hasWriteSideEffect() {
|
||||||
not expr.getTarget().(SideEffectFunction).neverWritesMemory()
|
not expr.getTarget().(SideEffectFunction).hasOnlySpecificWriteSideEffects()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,3 +316,229 @@ class TranslatedStructorCall extends TranslatedFunctionCall {
|
|||||||
|
|
||||||
override predicate hasQualifier() { any() }
|
override predicate hasQualifier() { any() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TranslatedSideEffects extends TranslatedElement, TTranslatedSideEffects {
|
||||||
|
Call expr;
|
||||||
|
|
||||||
|
TranslatedSideEffects() { this = TTranslatedSideEffects(expr) }
|
||||||
|
|
||||||
|
override string toString() { result = "(side effects for " + expr.toString() + ")" }
|
||||||
|
|
||||||
|
override Locatable getAST() { result = expr }
|
||||||
|
|
||||||
|
Call getCall() { result = expr }
|
||||||
|
|
||||||
|
override TranslatedElement getChild(int i) {
|
||||||
|
result = rank[i + 1](TranslatedSideEffect tse, int isWrite, int index |
|
||||||
|
(
|
||||||
|
tse.getCall() = getCall() and
|
||||||
|
tse.getArgumentIndex() = index and
|
||||||
|
if tse.isWrite() then isWrite = 1 else isWrite = 0
|
||||||
|
)
|
||||||
|
|
|
||||||
|
tse order by isWrite, index
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override Instruction getChildSuccessor(TranslatedElement te) {
|
||||||
|
exists(int i |
|
||||||
|
getChild(i) = te and
|
||||||
|
if exists(getChild(i + 1))
|
||||||
|
then result = getChild(i + 1).getFirstInstruction()
|
||||||
|
else result = getParent().getChildSuccessor(this)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate hasInstruction(Opcode opcode, InstructionTag tag, Type t, boolean isGLValue) {
|
||||||
|
none()
|
||||||
|
}
|
||||||
|
|
||||||
|
override Instruction getFirstInstruction() { result = getChild(0).getFirstInstruction() }
|
||||||
|
|
||||||
|
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
|
||||||
|
|
||||||
|
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { none() }
|
||||||
|
|
||||||
|
override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { none() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the `TranslatedFunction` containing this expression.
|
||||||
|
*/
|
||||||
|
final TranslatedFunction getEnclosingFunction() {
|
||||||
|
result = getTranslatedFunction(expr.getEnclosingFunction())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the `Function` containing this expression.
|
||||||
|
*/
|
||||||
|
override Function getFunction() { result = expr.getEnclosingFunction() }
|
||||||
|
}
|
||||||
|
|
||||||
|
class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEffect {
|
||||||
|
Call call;
|
||||||
|
Expr arg;
|
||||||
|
int index;
|
||||||
|
boolean write;
|
||||||
|
|
||||||
|
TranslatedSideEffect() { this = TTranslatedArgumentSideEffect(call, arg, index, write) }
|
||||||
|
|
||||||
|
override Locatable getAST() { result = arg }
|
||||||
|
|
||||||
|
Expr getExpr() { result = arg }
|
||||||
|
|
||||||
|
Call getCall() { result = call }
|
||||||
|
|
||||||
|
int getArgumentIndex() { result = index }
|
||||||
|
|
||||||
|
predicate isWrite() { write = true }
|
||||||
|
|
||||||
|
override string toString() {
|
||||||
|
write = true and
|
||||||
|
result = "(write side effect for " + arg.toString() + ")"
|
||||||
|
or
|
||||||
|
write = false and
|
||||||
|
result = "(read side effect for " + arg.toString() + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
override TranslatedElement getChild(int n) { none() }
|
||||||
|
|
||||||
|
override Instruction getChildSuccessor(TranslatedElement child) { none() }
|
||||||
|
|
||||||
|
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
|
||||||
|
|
||||||
|
override predicate hasInstruction(Opcode opcode, InstructionTag tag, Type t, boolean isGLValue) {
|
||||||
|
isWrite() and
|
||||||
|
hasSpecificWriteSideEffect(opcode) and
|
||||||
|
tag = OnlyInstructionTag() and
|
||||||
|
(
|
||||||
|
opcode instanceof BufferAccessOpcode and
|
||||||
|
t instanceof UnknownType
|
||||||
|
or
|
||||||
|
not opcode instanceof BufferAccessOpcode and
|
||||||
|
(
|
||||||
|
t = arg.getUnspecifiedType().(DerivedType).getBaseType() and
|
||||||
|
not t instanceof VoidType
|
||||||
|
or
|
||||||
|
arg.getUnspecifiedType().(DerivedType).getBaseType() instanceof VoidType and
|
||||||
|
t instanceof UnknownType
|
||||||
|
)
|
||||||
|
or
|
||||||
|
index = -1 and
|
||||||
|
not arg.getUnspecifiedType() instanceof DerivedType and
|
||||||
|
t = arg.getUnspecifiedType()
|
||||||
|
) and
|
||||||
|
isGLValue = false
|
||||||
|
or
|
||||||
|
not isWrite() and
|
||||||
|
hasSpecificReadSideEffect(opcode) and
|
||||||
|
tag = OnlyInstructionTag() and
|
||||||
|
t instanceof VoidType and
|
||||||
|
isGLValue = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||||
|
result = getParent().getChildSuccessor(this) and
|
||||||
|
tag = OnlyInstructionTag() and
|
||||||
|
kind instanceof GotoEdge
|
||||||
|
}
|
||||||
|
|
||||||
|
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||||
|
tag instanceof OnlyInstructionTag and
|
||||||
|
operandTag instanceof AddressOperandTag and
|
||||||
|
result = getTranslatedExpr(arg).getResult()
|
||||||
|
or
|
||||||
|
tag instanceof OnlyInstructionTag and
|
||||||
|
operandTag instanceof SideEffectOperandTag and
|
||||||
|
not isWrite() and
|
||||||
|
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
|
||||||
|
or
|
||||||
|
tag instanceof OnlyInstructionTag and
|
||||||
|
operandTag instanceof BufferSizeOperandTag and
|
||||||
|
result = getTranslatedExpr(call
|
||||||
|
.getArgument(call.getTarget().(SideEffectFunction).getParameterSizeIndex(index))
|
||||||
|
.getFullyConverted()).getResult()
|
||||||
|
}
|
||||||
|
|
||||||
|
override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
||||||
|
tag instanceof OnlyInstructionTag and
|
||||||
|
result = arg.getType().getUnspecifiedType().(DerivedType).getBaseType() and
|
||||||
|
operandTag instanceof SideEffectOperandTag
|
||||||
|
or
|
||||||
|
tag instanceof OnlyInstructionTag and
|
||||||
|
result = arg.getType().getUnspecifiedType() and
|
||||||
|
not result instanceof DerivedType and
|
||||||
|
operandTag instanceof SideEffectOperandTag
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate hasSpecificWriteSideEffect(Opcode op) {
|
||||||
|
exists(boolean buffer, boolean mustWrite |
|
||||||
|
if exists(call.getTarget().(SideEffectFunction).getParameterSizeIndex(index))
|
||||||
|
then
|
||||||
|
call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(index, true, mustWrite) and
|
||||||
|
buffer = true and
|
||||||
|
(
|
||||||
|
mustWrite = false and op instanceof Opcode::SizedBufferMayWriteSideEffect
|
||||||
|
or
|
||||||
|
mustWrite = true and op instanceof Opcode::SizedBufferMustWriteSideEffect
|
||||||
|
)
|
||||||
|
else (
|
||||||
|
call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(index, buffer, mustWrite) and
|
||||||
|
(
|
||||||
|
buffer = true and mustWrite = false and op instanceof Opcode::BufferMayWriteSideEffect
|
||||||
|
or
|
||||||
|
buffer = false and mustWrite = false and op instanceof Opcode::IndirectMayWriteSideEffect
|
||||||
|
or
|
||||||
|
buffer = true and mustWrite = true and op instanceof Opcode::BufferMustWriteSideEffect
|
||||||
|
or
|
||||||
|
buffer = false and mustWrite = true and op instanceof Opcode::IndirectMustWriteSideEffect
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
not call.getTarget() instanceof SideEffectFunction and
|
||||||
|
getArgumentIndex() != -1 and
|
||||||
|
op instanceof Opcode::BufferMayWriteSideEffect
|
||||||
|
or
|
||||||
|
not call.getTarget() instanceof SideEffectFunction and
|
||||||
|
getArgumentIndex() = -1 and
|
||||||
|
op instanceof Opcode::IndirectMayWriteSideEffect
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate hasSpecificReadSideEffect(Opcode op) {
|
||||||
|
exists(boolean buffer |
|
||||||
|
call.getTarget().(SideEffectFunction).hasSpecificReadSideEffect(index, buffer) and
|
||||||
|
if exists(call.getTarget().(SideEffectFunction).getParameterSizeIndex(index))
|
||||||
|
then buffer = true and op instanceof Opcode::SizedBufferReadSideEffect
|
||||||
|
else (
|
||||||
|
buffer = true and op instanceof Opcode::BufferReadSideEffect
|
||||||
|
or
|
||||||
|
buffer = false and op instanceof Opcode::IndirectReadSideEffect
|
||||||
|
)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
not call.getTarget() instanceof SideEffectFunction and
|
||||||
|
op instanceof Opcode::IndirectReadSideEffect
|
||||||
|
}
|
||||||
|
|
||||||
|
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
|
||||||
|
tag = OnlyInstructionTag() and
|
||||||
|
result = getTranslatedExpr(call).getInstruction(CallTag())
|
||||||
|
}
|
||||||
|
|
||||||
|
final override int getInstructionIndex(InstructionTag tag) {
|
||||||
|
tag = OnlyInstructionTag() and
|
||||||
|
result = index
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the `TranslatedFunction` containing this expression.
|
||||||
|
*/
|
||||||
|
final TranslatedFunction getEnclosingFunction() {
|
||||||
|
result = getTranslatedFunction(arg.getEnclosingFunction())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the `Function` containing this expression.
|
||||||
|
*/
|
||||||
|
override Function getFunction() { result = arg.getEnclosingFunction() }
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ private import TranslatedFunction
|
|||||||
private import TranslatedStmt
|
private import TranslatedStmt
|
||||||
private import TranslatedExpr
|
private import TranslatedExpr
|
||||||
private import IRConstruction
|
private import IRConstruction
|
||||||
|
private import semmle.code.cpp.models.interfaces.SideEffect
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the built-in `int` type.
|
* Gets the built-in `int` type.
|
||||||
@@ -379,7 +380,44 @@ newtype TTranslatedElement =
|
|||||||
// An allocation size for a `new` or `new[]` expression
|
// An allocation size for a `new` or `new[]` expression
|
||||||
TTranslatedAllocationSize(NewOrNewArrayExpr newExpr) { not ignoreExpr(newExpr) } or
|
TTranslatedAllocationSize(NewOrNewArrayExpr newExpr) { not ignoreExpr(newExpr) } or
|
||||||
// The declaration/initialization part of a `ConditionDeclExpr`
|
// The declaration/initialization part of a `ConditionDeclExpr`
|
||||||
TTranslatedConditionDecl(ConditionDeclExpr expr) { not ignoreExpr(expr) }
|
TTranslatedConditionDecl(ConditionDeclExpr expr) { not ignoreExpr(expr) } or
|
||||||
|
// The side effects of a `Call` {
|
||||||
|
TTranslatedSideEffects(Call expr) { exists(TTranslatedArgumentSideEffect(expr, _, _, _)) } or // A precise side effect of an argument to a `Call` {
|
||||||
|
TTranslatedArgumentSideEffect(Call call, Expr expr, int n, boolean isWrite) {
|
||||||
|
(
|
||||||
|
expr = call.getArgument(n).getFullyConverted()
|
||||||
|
or
|
||||||
|
expr = call.getQualifier().getFullyConverted() and
|
||||||
|
n = -1
|
||||||
|
) and
|
||||||
|
(
|
||||||
|
call.getTarget().(SideEffectFunction).hasSpecificReadSideEffect(n, _) and
|
||||||
|
isWrite = false
|
||||||
|
or
|
||||||
|
call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(n, _, _) and
|
||||||
|
isWrite = true
|
||||||
|
or
|
||||||
|
not call.getTarget() instanceof SideEffectFunction and
|
||||||
|
exists(Type t | t = expr.getUnspecifiedType() |
|
||||||
|
t instanceof ArrayType or
|
||||||
|
t instanceof PointerType or
|
||||||
|
t instanceof ReferenceType
|
||||||
|
) and
|
||||||
|
(
|
||||||
|
isWrite = true or
|
||||||
|
isWrite = false
|
||||||
|
)
|
||||||
|
or
|
||||||
|
not call.getTarget() instanceof SideEffectFunction and
|
||||||
|
n = -1 and
|
||||||
|
(
|
||||||
|
isWrite = true or
|
||||||
|
isWrite = false
|
||||||
|
)
|
||||||
|
) and
|
||||||
|
not ignoreExpr(expr) and
|
||||||
|
not ignoreExpr(call)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the index of the first explicitly initialized element in `initList`
|
* Gets the index of the first explicitly initialized element in `initList`
|
||||||
@@ -572,6 +610,12 @@ abstract class TranslatedElement extends TTranslatedElement {
|
|||||||
*/
|
*/
|
||||||
string getInstructionConstantValue(InstructionTag tag) { none() }
|
string getInstructionConstantValue(InstructionTag tag) { none() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the instruction specified by `tag` is an `IndexedInstruction`, gets the
|
||||||
|
* index for that instruction.
|
||||||
|
*/
|
||||||
|
int getInstructionIndex(InstructionTag tag) { none() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the instruction specified by `tag` is a `PointerArithmeticInstruction`,
|
* If the instruction specified by `tag` is a `PointerArithmeticInstruction`,
|
||||||
* gets the size of the type pointed to by the pointer.
|
* gets the size of the type pointed to by the pointer.
|
||||||
|
|||||||
@@ -1690,6 +1690,10 @@ class TranslatedAllocatorCall extends TTranslatedAllocatorCall, TranslatedDirect
|
|||||||
any()
|
any()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final override int getNumberOfArguments() {
|
||||||
|
result = expr.getAllocatorCall().getNumberOfArguments()
|
||||||
|
}
|
||||||
|
|
||||||
final override TranslatedExpr getArgument(int index) {
|
final override TranslatedExpr getArgument(int index) {
|
||||||
// If the allocator is the default operator new(void*), there will be no
|
// If the allocator is the default operator new(void*), there will be no
|
||||||
// allocator call in the AST. Otherwise, there will be an allocator call
|
// allocator call in the AST. Otherwise, there will be an allocator call
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ module InstructionSanity {
|
|||||||
or
|
or
|
||||||
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag
|
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag
|
||||||
or
|
or
|
||||||
opcode instanceof BufferAccessOpcode and tag instanceof BufferSizeOperand
|
opcode instanceof SizedBufferAccessOpcode and tag instanceof BufferSizeOperandTag
|
||||||
or
|
or
|
||||||
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag
|
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag
|
||||||
or
|
or
|
||||||
@@ -48,8 +48,8 @@ module InstructionSanity {
|
|||||||
or
|
or
|
||||||
(
|
(
|
||||||
opcode instanceof ReadSideEffectOpcode or
|
opcode instanceof ReadSideEffectOpcode or
|
||||||
opcode instanceof MayWriteSideEffectOpcode or
|
opcode instanceof Opcode::InlineAsm or
|
||||||
opcode instanceof Opcode::InlineAsm
|
opcode instanceof Opcode::CallSideEffect
|
||||||
) and
|
) and
|
||||||
tag instanceof SideEffectOperandTag
|
tag instanceof SideEffectOperandTag
|
||||||
)
|
)
|
||||||
@@ -120,6 +120,15 @@ module InstructionSanity {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query predicate sideEffectWithoutPrimary(
|
||||||
|
SideEffectInstruction instr, string message, IRFunction func, string funcText
|
||||||
|
) {
|
||||||
|
not exists(instr.getPrimaryInstruction()) and
|
||||||
|
message = "Side effect instruction missing primary instruction in function $@" and
|
||||||
|
func = instr.getEnclosingIRFunction() and
|
||||||
|
funcText = Language::getIdentityString(func.getFunction())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
||||||
*/
|
*/
|
||||||
@@ -609,9 +618,14 @@ class VariableInstruction extends Instruction {
|
|||||||
|
|
||||||
VariableInstruction() { var = Construction::getInstructionVariable(this) }
|
VariableInstruction() { var = Construction::getInstructionVariable(this) }
|
||||||
|
|
||||||
final override string getImmediateString() { result = var.toString() }
|
override string getImmediateString() { result = var.toString() }
|
||||||
|
|
||||||
final IRVariable getVariable() { result = var }
|
final IRVariable getIRVariable() { result = var }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the AST variable that this instruction's IR variable refers to, if one exists.
|
||||||
|
*/
|
||||||
|
final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() }
|
||||||
}
|
}
|
||||||
|
|
||||||
class FieldInstruction extends Instruction {
|
class FieldInstruction extends Instruction {
|
||||||
@@ -644,6 +658,16 @@ class ConstantValueInstruction extends Instruction {
|
|||||||
final string getValue() { result = value }
|
final string getValue() { result = value }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class IndexedInstruction extends Instruction {
|
||||||
|
int index;
|
||||||
|
|
||||||
|
IndexedInstruction() { index = Construction::getInstructionIndex(this) }
|
||||||
|
|
||||||
|
final override string getImmediateString() { result = index.toString() }
|
||||||
|
|
||||||
|
final int getIndex() { result = index }
|
||||||
|
}
|
||||||
|
|
||||||
class EnterFunctionInstruction extends Instruction {
|
class EnterFunctionInstruction extends Instruction {
|
||||||
EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction }
|
EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction }
|
||||||
}
|
}
|
||||||
@@ -1175,6 +1199,8 @@ class CallReadSideEffectInstruction extends SideEffectInstruction {
|
|||||||
*/
|
*/
|
||||||
class IndirectReadSideEffectInstruction extends SideEffectInstruction {
|
class IndirectReadSideEffectInstruction extends SideEffectInstruction {
|
||||||
IndirectReadSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectReadSideEffect }
|
IndirectReadSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectReadSideEffect }
|
||||||
|
|
||||||
|
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1182,13 +1208,39 @@ class IndirectReadSideEffectInstruction extends SideEffectInstruction {
|
|||||||
*/
|
*/
|
||||||
class BufferReadSideEffectInstruction extends SideEffectInstruction {
|
class BufferReadSideEffectInstruction extends SideEffectInstruction {
|
||||||
BufferReadSideEffectInstruction() { getOpcode() instanceof Opcode::BufferReadSideEffect }
|
BufferReadSideEffectInstruction() { getOpcode() instanceof Opcode::BufferReadSideEffect }
|
||||||
|
|
||||||
|
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instruction representing the read of an indirect buffer parameter within a function call.
|
||||||
|
*/
|
||||||
|
class SizedBufferReadSideEffectInstruction extends SideEffectInstruction {
|
||||||
|
SizedBufferReadSideEffectInstruction() {
|
||||||
|
getOpcode() instanceof Opcode::SizedBufferReadSideEffect
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||||
|
|
||||||
|
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instruction representing a side effect of a function call.
|
||||||
|
*/
|
||||||
|
class WriteSideEffectInstruction extends SideEffectInstruction {
|
||||||
|
WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode }
|
||||||
|
|
||||||
|
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An instruction representing the write of an indirect parameter within a function call.
|
* An instruction representing the write of an indirect parameter within a function call.
|
||||||
*/
|
*/
|
||||||
class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
|
class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||||
IndirectWriteSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectWriteSideEffect }
|
IndirectMustWriteSideEffectInstruction() {
|
||||||
|
getOpcode() instanceof Opcode::IndirectMustWriteSideEffect
|
||||||
|
}
|
||||||
|
|
||||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||||
}
|
}
|
||||||
@@ -1197,18 +1249,34 @@ class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
|
|||||||
* An instruction representing the write of an indirect buffer parameter within a function call. The
|
* An instruction representing the write of an indirect buffer parameter within a function call. The
|
||||||
* entire buffer is overwritten.
|
* entire buffer is overwritten.
|
||||||
*/
|
*/
|
||||||
class BufferWriteSideEffectInstruction extends SideEffectInstruction {
|
class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||||
BufferWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferWriteSideEffect }
|
BufferMustWriteSideEffectInstruction() {
|
||||||
|
getOpcode() instanceof Opcode::BufferMustWriteSideEffect
|
||||||
|
}
|
||||||
|
|
||||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instruction representing the write of an indirect buffer parameter within a function call. The
|
||||||
|
* entire buffer is overwritten.
|
||||||
|
*/
|
||||||
|
class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||||
|
SizedBufferMustWriteSideEffectInstruction() {
|
||||||
|
getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect
|
||||||
|
}
|
||||||
|
|
||||||
|
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||||
|
|
||||||
|
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An instruction representing the potential write of an indirect parameter within a function call.
|
* An instruction representing the potential write of an indirect parameter within a function call.
|
||||||
* Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten.
|
* Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten.
|
||||||
* written.
|
* written.
|
||||||
*/
|
*/
|
||||||
class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
|
class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||||
IndirectMayWriteSideEffectInstruction() {
|
IndirectMayWriteSideEffectInstruction() {
|
||||||
getOpcode() instanceof Opcode::IndirectMayWriteSideEffect
|
getOpcode() instanceof Opcode::IndirectMayWriteSideEffect
|
||||||
}
|
}
|
||||||
@@ -1222,7 +1290,7 @@ class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
|
|||||||
* An instruction representing the write of an indirect buffer parameter within a function call.
|
* An instruction representing the write of an indirect buffer parameter within a function call.
|
||||||
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
|
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
|
||||||
*/
|
*/
|
||||||
class BufferMayWriteSideEffectInstruction extends SideEffectInstruction {
|
class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||||
BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect }
|
BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect }
|
||||||
|
|
||||||
final override MemoryAccessKind getResultMemoryAccess() {
|
final override MemoryAccessKind getResultMemoryAccess() {
|
||||||
@@ -1230,6 +1298,22 @@ class BufferMayWriteSideEffectInstruction extends SideEffectInstruction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instruction representing the write of an indirect buffer parameter within a function call.
|
||||||
|
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
|
||||||
|
*/
|
||||||
|
class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||||
|
SizedBufferMayWriteSideEffectInstruction() {
|
||||||
|
getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect
|
||||||
|
}
|
||||||
|
|
||||||
|
final override MemoryAccessKind getResultMemoryAccess() {
|
||||||
|
result instanceof BufferMayMemoryAccess
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An instruction representing a GNU or MSVC inline assembly statement.
|
* An instruction representing a GNU or MSVC inline assembly statement.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -254,6 +254,16 @@ class AddressOperand extends RegisterOperand {
|
|||||||
override string toString() { result = "Address" }
|
override string toString() { result = "Address" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The buffer size operand of an instruction that represents a read or write of
|
||||||
|
* a buffer.
|
||||||
|
*/
|
||||||
|
class BufferSizeOperand extends RegisterOperand {
|
||||||
|
override BufferSizeOperandTag tag;
|
||||||
|
|
||||||
|
override string toString() { result = "BufferSize" }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The source value operand of an instruction that loads a value from memory (e.g. `Load`,
|
* The source value operand of an instruction that loads a value from memory (e.g. `Load`,
|
||||||
* `ReturnValue`, `ThrowValue`).
|
* `ReturnValue`, `ThrowValue`).
|
||||||
@@ -390,10 +400,10 @@ class SideEffectOperand extends TypedOperand {
|
|||||||
useInstr instanceof BufferReadSideEffectInstruction and
|
useInstr instanceof BufferReadSideEffectInstruction and
|
||||||
result instanceof BufferMemoryAccess
|
result instanceof BufferMemoryAccess
|
||||||
or
|
or
|
||||||
useInstr instanceof IndirectWriteSideEffectInstruction and
|
useInstr instanceof IndirectMustWriteSideEffectInstruction and
|
||||||
result instanceof IndirectMemoryAccess
|
result instanceof IndirectMemoryAccess
|
||||||
or
|
or
|
||||||
useInstr instanceof BufferWriteSideEffectInstruction and
|
useInstr instanceof BufferMustWriteSideEffectInstruction and
|
||||||
result instanceof BufferMemoryAccess
|
result instanceof BufferMemoryAccess
|
||||||
or
|
or
|
||||||
useInstr instanceof IndirectMayWriteSideEffectInstruction and
|
useInstr instanceof IndirectMayWriteSideEffectInstruction and
|
||||||
|
|||||||
@@ -135,14 +135,14 @@ private predicate variableAddressValueNumber(
|
|||||||
VariableAddressInstruction instr, IRFunction irFunc, IRVariable var
|
VariableAddressInstruction instr, IRFunction irFunc, IRVariable var
|
||||||
) {
|
) {
|
||||||
instr.getEnclosingIRFunction() = irFunc and
|
instr.getEnclosingIRFunction() = irFunc and
|
||||||
instr.getVariable() = var
|
instr.getIRVariable() = var
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate initializeParameterValueNumber(
|
private predicate initializeParameterValueNumber(
|
||||||
InitializeParameterInstruction instr, IRFunction irFunc, IRVariable var
|
InitializeParameterInstruction instr, IRFunction irFunc, IRVariable var
|
||||||
) {
|
) {
|
||||||
instr.getEnclosingIRFunction() = irFunc and
|
instr.getEnclosingIRFunction() = irFunc and
|
||||||
instr.getVariable() = var
|
instr.getIRVariable() = var
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
|
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ private predicate automaticVariableAddressEscapes(IRAutomaticVariable var) {
|
|||||||
// The variable's address escapes if the result of any
|
// The variable's address escapes if the result of any
|
||||||
// VariableAddressInstruction that computes the variable's address escapes.
|
// VariableAddressInstruction that computes the variable's address escapes.
|
||||||
exists(VariableAddressInstruction instr |
|
exists(VariableAddressInstruction instr |
|
||||||
instr.getVariable() = var and
|
instr.getIRVariable() = var and
|
||||||
resultEscapesNonReturn(instr)
|
resultEscapesNonReturn(instr)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -305,7 +305,7 @@ predicate variableAddressEscapes(IRVariable var) {
|
|||||||
*/
|
*/
|
||||||
predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset) {
|
predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset) {
|
||||||
// The address of a variable points to that variable, at offset 0.
|
// The address of a variable points to that variable, at offset 0.
|
||||||
instr.(VariableAddressInstruction).getVariable() = var and
|
instr.(VariableAddressInstruction).getIRVariable() = var and
|
||||||
bitOffset = 0
|
bitOffset = 0
|
||||||
or
|
or
|
||||||
exists(Operand operand, IntValue originalBitOffset, IntValue propagatedBitOffset |
|
exists(Operand operand, IntValue originalBitOffset, IntValue propagatedBitOffset |
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ private module Cached {
|
|||||||
IRVariable getInstructionVariable(Instruction instruction) {
|
IRVariable getInstructionVariable(Instruction instruction) {
|
||||||
result = getNewIRVariable(getOldInstruction(instruction)
|
result = getNewIRVariable(getOldInstruction(instruction)
|
||||||
.(OldIR::VariableInstruction)
|
.(OldIR::VariableInstruction)
|
||||||
.getVariable())
|
.getIRVariable())
|
||||||
}
|
}
|
||||||
|
|
||||||
cached
|
cached
|
||||||
@@ -342,6 +342,11 @@ private module Cached {
|
|||||||
result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField()
|
result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cached
|
||||||
|
int getInstructionIndex(Instruction instruction) {
|
||||||
|
result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex()
|
||||||
|
}
|
||||||
|
|
||||||
cached
|
cached
|
||||||
Function getInstructionFunction(Instruction instruction) {
|
Function getInstructionFunction(Instruction instruction) {
|
||||||
result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
|
result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
private import implementations.IdentityFunction
|
private import implementations.IdentityFunction
|
||||||
private import implementations.Inet
|
private import implementations.Inet
|
||||||
private import implementations.Memcpy
|
private import implementations.Memcpy
|
||||||
|
private import implementations.Memset
|
||||||
private import implementations.Printf
|
private import implementations.Printf
|
||||||
private import implementations.Pure
|
private import implementations.Pure
|
||||||
private import implementations.Strcat
|
private import implementations.Strcat
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ class IdentityFunction extends DataFlowFunction, SideEffectFunction, AliasFuncti
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate neverReadsMemory() { any() }
|
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||||
|
|
||||||
override predicate neverWritesMemory() { any() }
|
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||||
|
|
||||||
override predicate parameterNeverEscapes(int index) { none() }
|
override predicate parameterNeverEscapes(int index) { none() }
|
||||||
|
|
||||||
@@ -34,6 +34,6 @@ class IdentityFunction extends DataFlowFunction, SideEffectFunction, AliasFuncti
|
|||||||
|
|
||||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||||
// These functions simply return the argument value.
|
// These functions simply return the argument value.
|
||||||
input.isInParameter(0) and output.isOutReturnValue()
|
input.isParameter(0) and output.isReturnValue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ class InetNtoa extends TaintFunction {
|
|||||||
InetNtoa() { hasGlobalName("inet_ntoa") }
|
InetNtoa() { hasGlobalName("inet_ntoa") }
|
||||||
|
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
input.isInParameter(0) and
|
input.isParameter(0) and
|
||||||
output.isOutReturnPointer()
|
output.isReturnValueDeref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,8 +14,8 @@ class InetAton extends TaintFunction, ArrayFunction {
|
|||||||
InetAton() { hasGlobalName("inet_aton") }
|
InetAton() { hasGlobalName("inet_aton") }
|
||||||
|
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
input.isInParameterPointer(0) and
|
input.isParameterDeref(0) and
|
||||||
output.isOutParameterPointer(1)
|
output.isParameterDeref(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
|
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
|
||||||
@@ -34,8 +34,8 @@ class InetAddr extends TaintFunction, ArrayFunction {
|
|||||||
InetAddr() { hasGlobalName("inet_addr") }
|
InetAddr() { hasGlobalName("inet_addr") }
|
||||||
|
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
input.isInParameterPointer(0) and
|
input.isParameterDeref(0) and
|
||||||
output.isOutReturnValue()
|
output.isReturnValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
|
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
|
||||||
@@ -47,8 +47,8 @@ class InetNetwork extends TaintFunction, ArrayFunction {
|
|||||||
InetNetwork() { hasGlobalName("inet_network") }
|
InetNetwork() { hasGlobalName("inet_network") }
|
||||||
|
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
input.isInParameterPointer(1) and
|
input.isParameterDeref(1) and
|
||||||
output.isOutReturnValue()
|
output.isReturnValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
|
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
|
||||||
@@ -61,10 +61,10 @@ class InetMakeaddr extends TaintFunction {
|
|||||||
|
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
(
|
(
|
||||||
input.isInParameter(0) or
|
input.isParameter(0) or
|
||||||
input.isInParameter(1)
|
input.isParameter(1)
|
||||||
) and
|
) and
|
||||||
output.isOutReturnValue()
|
output.isReturnValue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,8 +72,8 @@ class InetLnaof extends TaintFunction {
|
|||||||
InetLnaof() { hasGlobalName("inet_lnaof") }
|
InetLnaof() { hasGlobalName("inet_lnaof") }
|
||||||
|
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
input.isInParameter(0) and
|
input.isParameter(0) and
|
||||||
output.isOutReturnValue()
|
output.isReturnValue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,8 +81,8 @@ class InetNetof extends TaintFunction {
|
|||||||
InetNetof() { hasGlobalName("inet_netof") }
|
InetNetof() { hasGlobalName("inet_netof") }
|
||||||
|
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
input.isInParameter(0) and
|
input.isParameter(0) and
|
||||||
output.isOutReturnValue()
|
output.isReturnValue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,10 +91,10 @@ class InetPton extends TaintFunction, ArrayFunction {
|
|||||||
|
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
(
|
(
|
||||||
input.isInParameter(0) or
|
input.isParameter(0) or
|
||||||
input.isInParameterPointer(1)
|
input.isParameterDeref(1)
|
||||||
) and
|
) and
|
||||||
output.isOutParameterPointer(2)
|
output.isParameterDeref(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate hasArrayInput(int bufParam) { bufParam = 1 }
|
override predicate hasArrayInput(int bufParam) { bufParam = 1 }
|
||||||
@@ -110,8 +110,8 @@ class Gethostbyname extends TaintFunction, ArrayFunction {
|
|||||||
Gethostbyname() { hasGlobalName("gethostbyname") }
|
Gethostbyname() { hasGlobalName("gethostbyname") }
|
||||||
|
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
input.isInParameterPointer(0) and
|
input.isParameterDeref(0) and
|
||||||
output.isOutReturnPointer()
|
output.isReturnValueDeref()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
|
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
|
||||||
@@ -124,11 +124,11 @@ class Gethostbyaddr extends TaintFunction, ArrayFunction {
|
|||||||
|
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
(
|
(
|
||||||
input.isInParameterPointer(0) or
|
input.isParameterDeref(0) or
|
||||||
input.isInParameter(1) or
|
input.isParameter(1) or
|
||||||
input.isInParameter(2)
|
input.isParameter(2)
|
||||||
) and
|
) and
|
||||||
output.isOutReturnPointer()
|
output.isReturnValueDeref()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
|
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import semmle.code.cpp.Function
|
import semmle.code.cpp.Function
|
||||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||||
import semmle.code.cpp.models.interfaces.DataFlow
|
import semmle.code.cpp.models.interfaces.DataFlow
|
||||||
|
import semmle.code.cpp.models.interfaces.SideEffect
|
||||||
import semmle.code.cpp.models.interfaces.Taint
|
import semmle.code.cpp.models.interfaces.Taint
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The standard functions `memcpy` and `memmove`, and the gcc variant
|
* The standard functions `memcpy` and `memmove`, and the gcc variant
|
||||||
* `__builtin___memcpy_chk`
|
* `__builtin___memcpy_chk`
|
||||||
*/
|
*/
|
||||||
class MemcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction {
|
class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffectFunction, TaintFunction {
|
||||||
MemcpyFunction() {
|
MemcpyFunction() {
|
||||||
this.hasName("memcpy") or
|
this.hasName("memcpy") or
|
||||||
this.hasName("memmove") or
|
this.hasName("memmove") or
|
||||||
@@ -19,22 +20,22 @@ class MemcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction {
|
|||||||
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
|
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
|
||||||
|
|
||||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||||
input.isInParameterPointer(1) and
|
input.isParameterDeref(1) and
|
||||||
output.isOutParameterPointer(0)
|
output.isParameterDeref(0)
|
||||||
or
|
or
|
||||||
input.isInParameterPointer(1) and
|
input.isParameterDeref(1) and
|
||||||
output.isOutReturnPointer()
|
output.isReturnValueDeref()
|
||||||
or
|
or
|
||||||
input.isInParameter(0) and
|
input.isParameter(0) and
|
||||||
output.isOutReturnValue()
|
output.isReturnValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
input.isInParameter(2) and
|
input.isParameter(2) and
|
||||||
output.isOutParameterPointer(0)
|
output.isParameterDeref(0)
|
||||||
or
|
or
|
||||||
input.isInParameter(2) and
|
input.isParameter(2) and
|
||||||
output.isOutReturnPointer()
|
output.isReturnValueDeref()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
|
override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
|
||||||
@@ -44,4 +45,24 @@ class MemcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction {
|
|||||||
) and
|
) and
|
||||||
countParam = 2
|
countParam = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||||
|
|
||||||
|
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||||
|
|
||||||
|
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||||
|
i = 0 and buffer = true and mustWrite = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||||
|
i = 1 and buffer = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override ParameterIndex getParameterSizeIndex(ParameterIndex i) {
|
||||||
|
result = 2 and
|
||||||
|
(
|
||||||
|
i = 0 or
|
||||||
|
i = 1
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
55
cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll
Normal file
55
cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import semmle.code.cpp.Function
|
||||||
|
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||||
|
import semmle.code.cpp.models.interfaces.DataFlow
|
||||||
|
import semmle.code.cpp.models.interfaces.Alias
|
||||||
|
import semmle.code.cpp.models.interfaces.SideEffect
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The standard function `memset` and its assorted variants
|
||||||
|
*/
|
||||||
|
class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunction, SideEffectFunction {
|
||||||
|
MemsetFunction() {
|
||||||
|
hasGlobalName("memset") or
|
||||||
|
hasGlobalName("wmemset") or
|
||||||
|
hasGlobalName("bzero") or
|
||||||
|
hasGlobalName("__builtin_memset") or
|
||||||
|
hasGlobalName("__builtin_memset_chk") or
|
||||||
|
hasQualifiedName("std", "memset") or
|
||||||
|
hasQualifiedName("std", "wmemset")
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
|
||||||
|
|
||||||
|
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||||
|
input.isParameter(0) and
|
||||||
|
output.isReturnValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
|
||||||
|
bufParam = 0 and
|
||||||
|
(if hasGlobalName("bzero") then countParam = 1 else countParam = 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate parameterNeverEscapes(int index) { hasGlobalName("bzero") and index = 0 }
|
||||||
|
|
||||||
|
override predicate parameterEscapesOnlyViaReturn(int index) {
|
||||||
|
not hasGlobalName("bzero") and index = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate parameterIsAlwaysReturned(int index) {
|
||||||
|
not hasGlobalName("bzero") and index = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||||
|
|
||||||
|
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||||
|
|
||||||
|
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||||
|
i = 0 and buffer = true and mustWrite = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override ParameterIndex getParameterSizeIndex(ParameterIndex i) {
|
||||||
|
i = 0 and
|
||||||
|
if hasGlobalName("bzero") then result = 1 else result = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,17 +41,17 @@ class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunction, SideE
|
|||||||
|
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
exists(ParameterIndex i |
|
exists(ParameterIndex i |
|
||||||
input.isInParameter(i) and
|
input.isParameter(i) and
|
||||||
exists(getParameter(i))
|
exists(getParameter(i))
|
||||||
or
|
or
|
||||||
input.isInParameterPointer(i) and
|
input.isParameterDeref(i) and
|
||||||
getParameter(i).getUnspecifiedType() instanceof PointerType
|
getParameter(i).getUnspecifiedType() instanceof PointerType
|
||||||
) and
|
) and
|
||||||
(
|
(
|
||||||
output.isOutReturnPointer() and
|
output.isReturnValueDeref() and
|
||||||
getUnspecifiedType() instanceof PointerType
|
getUnspecifiedType() instanceof PointerType
|
||||||
or
|
or
|
||||||
output.isOutReturnValue()
|
output.isReturnValue()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,9 +67,9 @@ class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunction, SideE
|
|||||||
|
|
||||||
override predicate parameterIsAlwaysReturned(int i) { none() }
|
override predicate parameterIsAlwaysReturned(int i) { none() }
|
||||||
|
|
||||||
override predicate neverReadsMemory() { none() }
|
override predicate hasOnlySpecificReadSideEffects() { none() }
|
||||||
|
|
||||||
override predicate neverWritesMemory() { any() }
|
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||||
}
|
}
|
||||||
|
|
||||||
class PureFunction extends TaintFunction, SideEffectFunction {
|
class PureFunction extends TaintFunction, SideEffectFunction {
|
||||||
@@ -85,13 +85,13 @@ class PureFunction extends TaintFunction, SideEffectFunction {
|
|||||||
|
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
exists(ParameterIndex i |
|
exists(ParameterIndex i |
|
||||||
input.isInParameter(i) and
|
input.isParameter(i) and
|
||||||
exists(getParameter(i))
|
exists(getParameter(i))
|
||||||
) and
|
) and
|
||||||
output.isOutReturnValue()
|
output.isReturnValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate neverReadsMemory() { any() }
|
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||||
|
|
||||||
override predicate neverWritesMemory() { any() }
|
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||||
input.isInParameter(0) and
|
input.isParameter(0) and
|
||||||
output.isOutReturnValue()
|
output.isReturnValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
@@ -31,19 +31,19 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction {
|
|||||||
name = "_mbsncat" or
|
name = "_mbsncat" or
|
||||||
name = "_mbsncat_l"
|
name = "_mbsncat_l"
|
||||||
) and
|
) and
|
||||||
input.isInParameter(2) and
|
input.isParameter(2) and
|
||||||
output.isOutParameterPointer(0)
|
output.isParameterDeref(0)
|
||||||
or
|
or
|
||||||
name = "_mbsncat_l" and
|
name = "_mbsncat_l" and
|
||||||
input.isInParameter(3) and
|
input.isParameter(3) and
|
||||||
output.isOutParameterPointer(0)
|
output.isParameterDeref(0)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
input.isInParameterPointer(0) and
|
input.isParameterDeref(0) and
|
||||||
output.isOutParameterPointer(0)
|
output.isParameterDeref(0)
|
||||||
or
|
or
|
||||||
input.isInParameter(1) and
|
input.isParameter(1) and
|
||||||
output.isOutParameterPointer(0)
|
output.isParameterDeref(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate hasArrayInput(int param) {
|
override predicate hasArrayInput(int param) {
|
||||||
|
|||||||
@@ -55,15 +55,15 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction {
|
|||||||
this.hasName("wcscpy")
|
this.hasName("wcscpy")
|
||||||
) and
|
) and
|
||||||
(
|
(
|
||||||
input.isInParameterPointer(1) and
|
input.isParameterDeref(1) and
|
||||||
output.isOutParameterPointer(0)
|
output.isParameterDeref(0)
|
||||||
or
|
or
|
||||||
input.isInParameterPointer(1) and
|
input.isParameterDeref(1) and
|
||||||
output.isOutReturnPointer()
|
output.isReturnValueDeref()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
input.isInParameter(0) and
|
input.isParameter(0) and
|
||||||
output.isOutReturnValue()
|
output.isReturnValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
@@ -78,12 +78,12 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction {
|
|||||||
this.hasName("_wcsncpy_l")
|
this.hasName("_wcsncpy_l")
|
||||||
) and
|
) and
|
||||||
(
|
(
|
||||||
input.isInParameter(2) or
|
input.isParameter(2) or
|
||||||
input.isInParameterPointer(1)
|
input.isParameterDeref(1)
|
||||||
) and
|
) and
|
||||||
(
|
(
|
||||||
output.isOutParameterPointer(0) or
|
output.isParameterDeref(0) or
|
||||||
output.isOutReturnPointer()
|
output.isReturnValueDeref()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ class Strftime extends TaintFunction, ArrayFunction {
|
|||||||
|
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
(
|
(
|
||||||
input.isInParameter(1) or
|
input.isParameter(1) or
|
||||||
input.isInParameterPointer(2) or
|
input.isParameterDeref(2) or
|
||||||
input.isInParameterPointer(3)
|
input.isParameterDeref(3)
|
||||||
) and
|
) and
|
||||||
(
|
(
|
||||||
output.isOutParameterPointer(0) or
|
output.isParameterDeref(0) or
|
||||||
output.isOutReturnValue()
|
output.isReturnValue()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ class Swap extends DataFlowFunction {
|
|||||||
Swap() { this.hasQualifiedName("std", "swap") }
|
Swap() { this.hasQualifiedName("std", "swap") }
|
||||||
|
|
||||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||||
input.isInParameterPointer(0) and
|
input.isParameterDeref(0) and
|
||||||
output.isOutParameterPointer(1)
|
output.isParameterDeref(1)
|
||||||
or
|
or
|
||||||
input.isInParameterPointer(1) and
|
input.isParameterDeref(1) and
|
||||||
output.isOutParameterPointer(0)
|
output.isParameterDeref(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
/**
|
/**
|
||||||
* Provides an abstract class for accurate modeling of input and output buffers
|
* Provides an abstract class for accurate modeling of input and output buffers
|
||||||
* in library functions when source code is not available. To use this QL
|
* in library functions when source code is not available. To use this QL
|
||||||
* library, create a QL class extending `BufferFunction` with a characteristic
|
* library, create a QL class extending `ArrayFunction` with a characteristic
|
||||||
* predicate that selects the function or set of functions you are trying to
|
* predicate that selects the function or set of functions you are trying to
|
||||||
* model. Within that class, override the predicates provided by `BufferFunction`
|
* model. Within that class, override the predicates provided by `ArrayFunction`
|
||||||
* to match the flow within that function. Finally, add a private import
|
* to match the flow within that function. Finally, add a private import
|
||||||
* statement to `CustomModels.qll`
|
* statement to `Models.qll`
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import semmle.code.cpp.Function
|
import semmle.code.cpp.Function
|
||||||
|
|||||||
@@ -115,7 +115,19 @@ abstract class FormattingFunction extends Function {
|
|||||||
* Gets the position of the first format argument, corresponding with
|
* Gets the position of the first format argument, corresponding with
|
||||||
* the first format specifier in the format string.
|
* the first format specifier in the format string.
|
||||||
*/
|
*/
|
||||||
int getFirstFormatArgumentIndex() { result = getNumberOfParameters() }
|
int getFirstFormatArgumentIndex() {
|
||||||
|
result = getNumberOfParameters() and
|
||||||
|
// the formatting function either has a definition in the snapshot, or all
|
||||||
|
// `DeclarationEntry`s agree on the number of parameters (otherwise we don't
|
||||||
|
// really know the correct number)
|
||||||
|
(
|
||||||
|
hasDefinition()
|
||||||
|
or
|
||||||
|
forall(FunctionDeclarationEntry fde | fde = getADeclarationEntry() |
|
||||||
|
result = fde.getNumberOfParameters()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the position of the buffer size argument, if any.
|
* Gets the position of the buffer size argument, if any.
|
||||||
|
|||||||
@@ -6,26 +6,106 @@
|
|||||||
|
|
||||||
import semmle.code.cpp.Parameter
|
import semmle.code.cpp.Parameter
|
||||||
|
|
||||||
/**
|
private newtype TFunctionInput =
|
||||||
* An `int` that is a parameter index for some function. This is needed for binding in certain cases.
|
|
||||||
*/
|
|
||||||
class ParameterIndex extends int {
|
|
||||||
ParameterIndex() { exists(Parameter p | this = p.getIndex()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
newtype TFunctionInput =
|
|
||||||
TInParameter(ParameterIndex i) or
|
TInParameter(ParameterIndex i) or
|
||||||
TInParameterPointer(ParameterIndex i) or
|
TInParameterDeref(ParameterIndex i) or
|
||||||
TInQualifier()
|
TInQualifierObject() or
|
||||||
|
TInQualifierAddress()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An input to a function. This can be:
|
||||||
|
* - The value of one of the function's parameters
|
||||||
|
* - The value pointed to by one of function's pointer or reference parameters
|
||||||
|
* - The value of the function's `this` pointer
|
||||||
|
* - The value pointed to by the function's `this` pointer
|
||||||
|
*/
|
||||||
class FunctionInput extends TFunctionInput {
|
class FunctionInput extends TFunctionInput {
|
||||||
abstract string toString();
|
abstract string toString();
|
||||||
|
|
||||||
predicate isInParameter(ParameterIndex index) { none() }
|
/**
|
||||||
|
* Holds if this is the input value of the parameter with index `index`.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```
|
||||||
|
* void func(int n, char* p, float& r);
|
||||||
|
* ```
|
||||||
|
* - `isParameter(0)` holds for the `FunctionInput` that represents the value of `n` (with type
|
||||||
|
* `int`) on entry to the function.
|
||||||
|
* - `isParameter(1)` holds for the `FunctionInput` that represents the value of `p` (with type
|
||||||
|
* `char*`) on entry to the function.
|
||||||
|
* - `isParameter(2)` holds for the `FunctionInput` that represents the "value" of the reference
|
||||||
|
* `r` (with type `float&`) on entry to the function, _not_ the value of the referred-to
|
||||||
|
* `float`.
|
||||||
|
*/
|
||||||
|
predicate isParameter(ParameterIndex index) { none() }
|
||||||
|
|
||||||
predicate isInParameterPointer(ParameterIndex index) { none() }
|
/**
|
||||||
|
* Holds if this is the input value of the parameter with index `index`.
|
||||||
|
* DEPRECATED: Use `isParameter(index)` instead.
|
||||||
|
*/
|
||||||
|
deprecated final predicate isInParameter(ParameterIndex index) { isParameter(index) }
|
||||||
|
|
||||||
predicate isInQualifier() { none() }
|
/**
|
||||||
|
* Holds if this is the input value pointed to by a pointer parameter to a function, or the input
|
||||||
|
* value referred to by a reference parameter to a function, where the parameter has index
|
||||||
|
* `index`.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```
|
||||||
|
* void func(int n, char* p, float& r);
|
||||||
|
* ```
|
||||||
|
* - `isParameterDeref(1)` holds for the `FunctionInput` that represents the value of `*p` (with
|
||||||
|
* type `char`) on entry to the function.
|
||||||
|
* - `isParameterDeref(2)` holds for the `FunctionInput` that represents the value of `r` (with type
|
||||||
|
* `float`) on entry to the function.
|
||||||
|
* - There is no `FunctionInput` for which `isParameterDeref(0)` holds, because `n` is neither a
|
||||||
|
* pointer nor a reference.
|
||||||
|
*/
|
||||||
|
predicate isParameterDeref(ParameterIndex index) { none() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this is the input value pointed to by a pointer parameter to a function, or the input
|
||||||
|
* value referred to by a reference parameter to a function, where the parameter has index
|
||||||
|
* `index`.
|
||||||
|
* DEPRECATED: Use `isParameterDeref(index)` instead.
|
||||||
|
*/
|
||||||
|
deprecated final predicate isInParameterPointer(ParameterIndex index) { isParameterDeref(index) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this is the input value pointed to by the `this` pointer of an instance member
|
||||||
|
* function.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```
|
||||||
|
* struct C {
|
||||||
|
* void mfunc(int n, char* p, float& r) const;
|
||||||
|
* };
|
||||||
|
* ```
|
||||||
|
* - `isQualifierObject()` holds for the `FunctionInput` that represents the value of `*this`
|
||||||
|
* (with type `C const`) on entry to the function.
|
||||||
|
*/
|
||||||
|
predicate isQualifierObject() { none() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this is the input value pointed to by the `this` pointer of an instance member
|
||||||
|
* function.
|
||||||
|
* DEPRECATED: Use `isQualifierObject()` instead.
|
||||||
|
*/
|
||||||
|
deprecated final predicate isInQualifier() { isQualifierObject() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this is the input value of the `this` pointer of an instance member function.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```
|
||||||
|
* struct C {
|
||||||
|
* void mfunc(int n, char* p, float& r) const;
|
||||||
|
* };
|
||||||
|
* ```
|
||||||
|
* - `isQualifierAddress()` holds for the `FunctionInput` that represents the value of `this`
|
||||||
|
* (with type `C const *`) on entry to the function.
|
||||||
|
*/
|
||||||
|
predicate isQualifierAddress() { none() }
|
||||||
}
|
}
|
||||||
|
|
||||||
class InParameter extends FunctionInput, TInParameter {
|
class InParameter extends FunctionInput, TInParameter {
|
||||||
@@ -35,73 +115,182 @@ class InParameter extends FunctionInput, TInParameter {
|
|||||||
|
|
||||||
override string toString() { result = "InParameter " + index.toString() }
|
override string toString() { result = "InParameter " + index.toString() }
|
||||||
|
|
||||||
|
/** Gets the zero-based index of the parameter. */
|
||||||
ParameterIndex getIndex() { result = index }
|
ParameterIndex getIndex() { result = index }
|
||||||
|
|
||||||
override predicate isInParameter(ParameterIndex i) { i = index }
|
override predicate isParameter(ParameterIndex i) { i = index }
|
||||||
}
|
}
|
||||||
|
|
||||||
class InParameterPointer extends FunctionInput, TInParameterPointer {
|
class InParameterDeref extends FunctionInput, TInParameterDeref {
|
||||||
ParameterIndex index;
|
ParameterIndex index;
|
||||||
|
|
||||||
InParameterPointer() { this = TInParameterPointer(index) }
|
InParameterDeref() { this = TInParameterDeref(index) }
|
||||||
|
|
||||||
override string toString() { result = "InParameterPointer " + index.toString() }
|
override string toString() { result = "InParameterDeref " + index.toString() }
|
||||||
|
|
||||||
|
/** Gets the zero-based index of the parameter. */
|
||||||
ParameterIndex getIndex() { result = index }
|
ParameterIndex getIndex() { result = index }
|
||||||
|
|
||||||
override predicate isInParameterPointer(ParameterIndex i) { i = index }
|
override predicate isParameterDeref(ParameterIndex i) { i = index }
|
||||||
}
|
}
|
||||||
|
|
||||||
class InQualifier extends FunctionInput, TInQualifier {
|
class InQualifierObject extends FunctionInput, TInQualifierObject {
|
||||||
override string toString() { result = "InQualifier" }
|
override string toString() { result = "InQualifierObject" }
|
||||||
|
|
||||||
override predicate isInQualifier() { any() }
|
override predicate isQualifierObject() { any() }
|
||||||
}
|
}
|
||||||
|
|
||||||
newtype TFunctionOutput =
|
class InQualifierAddress extends FunctionInput, TInQualifierAddress {
|
||||||
TOutParameterPointer(ParameterIndex i) or
|
override string toString() { result = "InQualifierAddress" }
|
||||||
TOutQualifier() or
|
|
||||||
|
override predicate isQualifierAddress() { any() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private newtype TFunctionOutput =
|
||||||
|
TOutParameterDeref(ParameterIndex i) or
|
||||||
|
TOutQualifierObject() or
|
||||||
TOutReturnValue() or
|
TOutReturnValue() or
|
||||||
TOutReturnPointer()
|
TOutReturnValueDeref()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An output from a function. This can be:
|
||||||
|
* - The value pointed to by one of function's pointer or reference parameters
|
||||||
|
* - The value pointed to by the function's `this` pointer
|
||||||
|
* - The function's return value
|
||||||
|
* - The value pointed to by the function's return value, if the return value is a pointer or
|
||||||
|
* reference
|
||||||
|
*/
|
||||||
class FunctionOutput extends TFunctionOutput {
|
class FunctionOutput extends TFunctionOutput {
|
||||||
abstract string toString();
|
abstract string toString();
|
||||||
|
|
||||||
predicate isOutParameterPointer(ParameterIndex i) { none() }
|
/**
|
||||||
|
* Holds if this is the output value pointed to by a pointer parameter to a function, or the
|
||||||
|
* output value referred to by a reference parameter to a function, where the parameter has
|
||||||
|
* index `index`.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```
|
||||||
|
* void func(int n, char* p, float& r);
|
||||||
|
* ```
|
||||||
|
* - `isParameterDeref(1)` holds for the `FunctionOutput` that represents the value of `*p` (with
|
||||||
|
* type `char`) on return from the function.
|
||||||
|
* - `isParameterDeref(2)` holds for the `FunctionOutput` that represents the value of `r` (with
|
||||||
|
* type `float`) on return from the function.
|
||||||
|
* - There is no `FunctionOutput` for which `isParameterDeref(0)` holds, because `n` is neither a
|
||||||
|
* pointer nor a reference.
|
||||||
|
*/
|
||||||
|
predicate isParameterDeref(ParameterIndex i) { none() }
|
||||||
|
|
||||||
predicate isOutQualifier() { none() }
|
/**
|
||||||
|
* Holds if this is the output value pointed to by a pointer parameter to a function, or the
|
||||||
|
* output value referred to by a reference parameter to a function, where the parameter has
|
||||||
|
* index `index`.
|
||||||
|
* DEPRECATED: Use `isParameterDeref(index)` instead.
|
||||||
|
*/
|
||||||
|
deprecated final predicate isOutParameterPointer(ParameterIndex index) { isParameterDeref(index) }
|
||||||
|
|
||||||
predicate isOutReturnValue() { none() }
|
/**
|
||||||
|
* Holds if this is the output value pointed to by the `this` pointer of an instance member
|
||||||
|
* function.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```
|
||||||
|
* struct C {
|
||||||
|
* void mfunc(int n, char* p, float& r);
|
||||||
|
* };
|
||||||
|
* ```
|
||||||
|
* - `isQualifierObject()` holds for the `FunctionOutput` that represents the value of `*this`
|
||||||
|
* (with type `C`) on return from the function.
|
||||||
|
*/
|
||||||
|
predicate isQualifierObject() { none() }
|
||||||
|
|
||||||
predicate isOutReturnPointer() { none() }
|
/**
|
||||||
|
* Holds if this is the output value pointed to by the `this` pointer of an instance member
|
||||||
|
* function.
|
||||||
|
* DEPRECATED: Use `isQualifierObject()` instead.
|
||||||
|
*/
|
||||||
|
deprecated final predicate isOutQualifier() { isQualifierObject() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this is the value returned by a function.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```
|
||||||
|
* int getInt();
|
||||||
|
* char* getPointer();
|
||||||
|
* float& getReference();
|
||||||
|
* ```
|
||||||
|
* - `isReturnValue()` holds for the `FunctionOutput` that represents the value returned by
|
||||||
|
* `getInt()` (with type `int`).
|
||||||
|
* - `isReturnValue()` holds for the `FunctionOutput` that represents the value returned by
|
||||||
|
* `getPointer()` (with type `char*`).
|
||||||
|
* - `isReturnValue()` holds for the `FunctionOutput` that represents the "value" of the reference
|
||||||
|
* returned by `getReference()` (with type `float&`), _not_ the value of the referred-to
|
||||||
|
* `float`.
|
||||||
|
*/
|
||||||
|
predicate isReturnValue() { none() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this is the value returned by a function.
|
||||||
|
* DEPRECATED: Use `isReturnValue()` instead.
|
||||||
|
*/
|
||||||
|
deprecated final predicate isOutReturnValue() { isReturnValue() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this is the output value pointed to by the return value of a function, if the function
|
||||||
|
* returns a pointer, or the output value referred to by the return value of a function, if the
|
||||||
|
* function returns a reference.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```
|
||||||
|
* char* getPointer();
|
||||||
|
* float& getReference();
|
||||||
|
* int getInt();
|
||||||
|
* ```
|
||||||
|
* - `isReturnValueDeref()` holds for the `FunctionOutput` that represents the value of
|
||||||
|
* `*getPointer()` (with type `char`).
|
||||||
|
* - `isReturnValueDeref()` holds for the `FunctionOutput` that represents the value of
|
||||||
|
* `getReference()` (with type `float`).
|
||||||
|
* - There is no `FunctionOutput` of `getInt()` for which `isReturnValueDeref()` holds because the
|
||||||
|
* return type of `getInt()` is neither a pointer nor a reference.
|
||||||
|
*/
|
||||||
|
predicate isReturnValueDeref() { none() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this is the output value pointed to by the return value of a function, if the function
|
||||||
|
* returns a pointer, or the output value referred to by the return value of a function, if the
|
||||||
|
* function returns a reference.
|
||||||
|
* DEPRECATED: Use `isReturnValueDeref()` instead.
|
||||||
|
*/
|
||||||
|
deprecated final predicate isOutReturnPointer() { isReturnValueDeref() }
|
||||||
}
|
}
|
||||||
|
|
||||||
class OutParameterPointer extends FunctionOutput, TOutParameterPointer {
|
class OutParameterDeref extends FunctionOutput, TOutParameterDeref {
|
||||||
ParameterIndex index;
|
ParameterIndex index;
|
||||||
|
|
||||||
OutParameterPointer() { this = TOutParameterPointer(index) }
|
OutParameterDeref() { this = TOutParameterDeref(index) }
|
||||||
|
|
||||||
override string toString() { result = "OutParameterPointer " + index.toString() }
|
override string toString() { result = "OutParameterDeref " + index.toString() }
|
||||||
|
|
||||||
ParameterIndex getIndex() { result = index }
|
ParameterIndex getIndex() { result = index }
|
||||||
|
|
||||||
override predicate isOutParameterPointer(ParameterIndex i) { i = index }
|
override predicate isParameterDeref(ParameterIndex i) { i = index }
|
||||||
}
|
}
|
||||||
|
|
||||||
class OutQualifier extends FunctionOutput, TOutQualifier {
|
class OutQualifierObject extends FunctionOutput, TOutQualifierObject {
|
||||||
override string toString() { result = "OutQualifier" }
|
override string toString() { result = "OutQualifierObject" }
|
||||||
|
|
||||||
override predicate isOutQualifier() { any() }
|
override predicate isQualifierObject() { any() }
|
||||||
}
|
}
|
||||||
|
|
||||||
class OutReturnValue extends FunctionOutput, TOutReturnValue {
|
class OutReturnValue extends FunctionOutput, TOutReturnValue {
|
||||||
override string toString() { result = "OutReturnValue" }
|
override string toString() { result = "OutReturnValue" }
|
||||||
|
|
||||||
override predicate isOutReturnValue() { any() }
|
override predicate isReturnValue() { any() }
|
||||||
}
|
}
|
||||||
|
|
||||||
class OutReturnPointer extends FunctionOutput, TOutReturnPointer {
|
class OutReturnValueDeref extends FunctionOutput, TOutReturnValueDeref {
|
||||||
override string toString() { result = "OutReturnPointer" }
|
override string toString() { result = "OutReturnValueDeref" }
|
||||||
|
|
||||||
override predicate isOutReturnPointer() { any() }
|
override predicate isReturnValueDeref() { any() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
import semmle.code.cpp.Function
|
import semmle.code.cpp.Function
|
||||||
import semmle.code.cpp.models.Models
|
import semmle.code.cpp.models.Models
|
||||||
|
import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Models the side effects of a library function.
|
* Models the side effects of a library function.
|
||||||
@@ -19,12 +20,33 @@ abstract class SideEffectFunction extends Function {
|
|||||||
* This memory could be from global variables, or from other memory that was reachable from a
|
* This memory could be from global variables, or from other memory that was reachable from a
|
||||||
* pointer that was passed into the function.
|
* pointer that was passed into the function.
|
||||||
*/
|
*/
|
||||||
abstract predicate neverReadsMemory();
|
abstract predicate hasOnlySpecificReadSideEffects();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if the function never writes to memory that remains allocated after the function
|
* Holds if the function never writes to memory that remains allocated after the function
|
||||||
* returns. This memory could be from global variables, or from other memory that was reachable
|
* returns. This memory could be from global variables, or from other memory that was reachable
|
||||||
* from a pointer that was passed into the function.
|
* from a pointer that was passed into the function.
|
||||||
*/
|
*/
|
||||||
abstract predicate neverWritesMemory();
|
abstract predicate hasOnlySpecificWriteSideEffects();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if the value pointed to by the parameter at index `i` is written to. `buffer` is true
|
||||||
|
* if the write may be at an offset. `mustWrite` is true if the write is unconditional.
|
||||||
|
*/
|
||||||
|
predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||||
|
none()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if the value pointed to by the parameter at index `i` is read from. `buffer` is true
|
||||||
|
* if the read may be at an offset.
|
||||||
|
*/
|
||||||
|
predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) { none() }
|
||||||
|
|
||||||
|
// TODO: name?
|
||||||
|
/**
|
||||||
|
* Gets the index of the parameter that indicates the size of the buffer pointed to by the
|
||||||
|
* parameter at index `i`.
|
||||||
|
*/
|
||||||
|
ParameterIndex getParameterSizeIndex(ParameterIndex i) { none() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import cpp
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Align the specified offset up to the specified alignment boundary.
|
* Align the specified offset up to the specified alignment boundary.
|
||||||
* The result is the smallest integer i such that (i % alignment) = 0
|
* The result is the smallest integer `i` such that `(i % alignment) = 0`
|
||||||
* and (i >= offset)
|
* and `(i >= offset)`.
|
||||||
*/
|
*/
|
||||||
bindingset[offset, alignment]
|
bindingset[offset, alignment]
|
||||||
private int alignUp(int offset, int alignment) {
|
private int alignUp(int offset, int alignment) {
|
||||||
@@ -30,16 +30,16 @@ abstract class Architecture extends string {
|
|||||||
/** Gets the size of a pointer, in bits. */
|
/** Gets the size of a pointer, in bits. */
|
||||||
abstract int pointerSize();
|
abstract int pointerSize();
|
||||||
|
|
||||||
/** Gets the size of a 'long int', in bits. */
|
/** Gets the size of a `long int`, in bits. */
|
||||||
abstract int longSize();
|
abstract int longSize();
|
||||||
|
|
||||||
/** Gets the size of a 'long double', in bits. */
|
/** Gets the size of a `long double`, in bits. */
|
||||||
abstract int longDoubleSize();
|
abstract int longDoubleSize();
|
||||||
|
|
||||||
/** Gets the size of a 'long long', in bits. */
|
/** Gets the size of a `long long`, in bits. */
|
||||||
abstract int longLongSize();
|
abstract int longLongSize();
|
||||||
|
|
||||||
/** Gets the size of a 'wchar_t', in bits. */
|
/** Gets the size of a `wchar_t`, in bits. */
|
||||||
abstract int wideCharSize();
|
abstract int wideCharSize();
|
||||||
|
|
||||||
/** Gets the alignment boundary for doubles, in bits. */
|
/** Gets the alignment boundary for doubles, in bits. */
|
||||||
@@ -479,8 +479,10 @@ class PaddedType extends Class {
|
|||||||
int typeBitSize(Architecture arch) {
|
int typeBitSize(Architecture arch) {
|
||||||
if this instanceof Union
|
if this instanceof Union
|
||||||
then
|
then
|
||||||
// A correct implementation for unions would be
|
// A correct implementation for unions would be:
|
||||||
|
// ```
|
||||||
// result = max(fieldSize(_, arch))
|
// result = max(fieldSize(_, arch))
|
||||||
|
// ```
|
||||||
// but that uses a recursive aggregate, which isn't supported in
|
// but that uses a recursive aggregate, which isn't supported in
|
||||||
// QL. We therefore use this slightly more complex implementation
|
// QL. We therefore use this slightly more complex implementation
|
||||||
// instead.
|
// instead.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* This library is a clone of semmle.code.cpp.controlflow.SSA, with
|
* This library is a clone of semmle.code.cpp.controlflow.SSA, with
|
||||||
* only one difference: extra phi definitions are added after
|
* only one difference: extra phi definitions are added after
|
||||||
* guards. For example:
|
* guards. For example:
|
||||||
*
|
* ```
|
||||||
* x = f();
|
* x = f();
|
||||||
* if (x < 10) {
|
* if (x < 10) {
|
||||||
* // Block 1
|
* // Block 1
|
||||||
@@ -11,12 +11,12 @@
|
|||||||
* // Block 2
|
* // Block 2
|
||||||
* ...
|
* ...
|
||||||
* }
|
* }
|
||||||
*
|
* ```
|
||||||
* In standard SSA, basic blocks 1 and 2 do not need phi definitions
|
* In standard SSA, basic blocks 1 and 2 do not need phi definitions
|
||||||
* for x, because they are dominated by the definition of x on the
|
* for `x`, because they are dominated by the definition of `x` on the
|
||||||
* first line. In RangeSSA, however, we add phi definitions for x at
|
* first line. In RangeSSA, however, we add phi definitions for `x` at
|
||||||
* the beginning of blocks 1 and 2. This is useful for range analysis
|
* the beginning of blocks 1 and 2. This is useful for range analysis
|
||||||
* because it enables us to deduce a more accurate range for x in the
|
* because it enables us to deduce a more accurate range for `x` in the
|
||||||
* two branches of the if-statement.
|
* two branches of the if-statement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -74,19 +74,19 @@ class RangeSsaDefinition extends ControlFlowNodeBase {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A string representation of the SSA variable represented by the pair
|
* A string representation of the SSA variable represented by the pair
|
||||||
* (this, v).
|
* `(this, v)`.
|
||||||
*/
|
*/
|
||||||
string toString(LocalScopeVariable v) { exists(RangeSSA x | result = x.toString(this, v)) }
|
string toString(LocalScopeVariable v) { exists(RangeSSA x | result = x.toString(this, v)) }
|
||||||
|
|
||||||
/** Gets a use of the SSA variable represented by the pair (this, v) */
|
/** Gets a use of the SSA variable represented by the pair `(this, v)`. */
|
||||||
VariableAccess getAUse(LocalScopeVariable v) { exists(RangeSSA x | result = x.getAUse(this, v)) }
|
VariableAccess getAUse(LocalScopeVariable v) { exists(RangeSSA x | result = x.getAUse(this, v)) }
|
||||||
|
|
||||||
/** Gets the control flow node for this definition */
|
/** Gets the control flow node for this definition. */
|
||||||
ControlFlowNode getDefinition() { result = this }
|
ControlFlowNode getDefinition() { result = this }
|
||||||
|
|
||||||
BasicBlock getBasicBlock() { result.contains(getDefinition()) }
|
BasicBlock getBasicBlock() { result.contains(getDefinition()) }
|
||||||
|
|
||||||
/** Whether this definition is a phi node for variable v */
|
/** Whether this definition is a phi node for variable `v`. */
|
||||||
predicate isPhiNode(LocalScopeVariable v) {
|
predicate isPhiNode(LocalScopeVariable v) {
|
||||||
exists(RangeSSA x | x.phi_node(v, this.(BasicBlock)))
|
exists(RangeSSA x | x.phi_node(v, this.(BasicBlock)))
|
||||||
}
|
}
|
||||||
@@ -136,7 +136,7 @@ class RangeSsaDefinition extends ControlFlowNodeBase {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the expression assigned to this SsaDefinition */
|
/** Gets the expression assigned to this SsaDefinition. */
|
||||||
Expr getDefiningValue(LocalScopeVariable v) {
|
Expr getDefiningValue(LocalScopeVariable v) {
|
||||||
exists(ControlFlowNode def | def = this.getDefinition() |
|
exists(ControlFlowNode def | def = this.getDefinition() |
|
||||||
def = v.getInitializer().getExpr() and def = result
|
def = v.getInitializer().getExpr() and def = result
|
||||||
|
|||||||
487
cpp/ql/src/semmle/code/cpp/security/boostorg/asio/protocols.qll
Normal file
487
cpp/ql/src/semmle/code/cpp/security/boostorg/asio/protocols.qll
Normal file
@@ -0,0 +1,487 @@
|
|||||||
|
import cpp
|
||||||
|
import semmle.code.cpp.dataflow.DataFlow
|
||||||
|
|
||||||
|
module BoostorgAsio {
|
||||||
|
/**
|
||||||
|
* Represents boost::asio::ssl::context enum
|
||||||
|
*/
|
||||||
|
class SslContextMethod extends Enum {
|
||||||
|
SslContextMethod() {
|
||||||
|
this.getName().toString() = "method" and
|
||||||
|
this.getQualifiedName().toString().matches("boost::asio::ssl::context%")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the value for a banned protocol
|
||||||
|
*/
|
||||||
|
EnumConstant getABannedProtocolConstant() {
|
||||||
|
result = this.getAnEnumConstant() and
|
||||||
|
(
|
||||||
|
/// Generic SSL version 2.
|
||||||
|
result.getName() = "sslv2"
|
||||||
|
or
|
||||||
|
/// SSL version 2 client.
|
||||||
|
result.getName() = "sslv2_client"
|
||||||
|
or
|
||||||
|
/// SSL version 2 server.
|
||||||
|
result.getName() = "sslv2_server"
|
||||||
|
or
|
||||||
|
/// Generic SSL version 3.
|
||||||
|
result.getName() = "sslv3"
|
||||||
|
or
|
||||||
|
/// SSL version 3 client.
|
||||||
|
result.getName() = "sslv3_client"
|
||||||
|
or
|
||||||
|
/// SSL version 3 server.
|
||||||
|
result.getName() = "sslv3_server"
|
||||||
|
or
|
||||||
|
/// Generic TLS version 1.
|
||||||
|
result.getName() = "tlsv1"
|
||||||
|
or
|
||||||
|
/// TLS version 1 client.
|
||||||
|
result.getName() = "tlsv1_client"
|
||||||
|
or
|
||||||
|
/// TLS version 1 server.
|
||||||
|
result.getName() = "tlsv1_server"
|
||||||
|
or
|
||||||
|
/// Generic TLS version 1.1.
|
||||||
|
result.getName() = "tlsv11"
|
||||||
|
or
|
||||||
|
/// TLS version 1.1 client.
|
||||||
|
result.getName() = "tlsv11_client"
|
||||||
|
or
|
||||||
|
/// TLS version 1.1 server.
|
||||||
|
result.getName() = "tlsv11_server"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the value for a approved protocols, but that are hard-coded (i.e. no protocol negotiation)
|
||||||
|
*/
|
||||||
|
EnumConstant getAnApprovedButHardcodedProtocolConstant() {
|
||||||
|
result = this.getATls12ProtocolConstant()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the value for a TLS v1.2 protocol
|
||||||
|
*/
|
||||||
|
EnumConstant getATls12ProtocolConstant() {
|
||||||
|
result = this.getAnEnumConstant() and
|
||||||
|
(
|
||||||
|
/// Generic TLS version 1.2.
|
||||||
|
result.getName() = "tlsv12"
|
||||||
|
or
|
||||||
|
/// TLS version 1.2 client.
|
||||||
|
result.getName() = "tlsv12_client"
|
||||||
|
or
|
||||||
|
/// TLS version 1.2 server.
|
||||||
|
result.getName() = "tlsv12_server"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the value for a TLS v1.3 protocol
|
||||||
|
*/
|
||||||
|
EnumConstant getATls13ProtocolConstant() {
|
||||||
|
result = this.getAnEnumConstant() and
|
||||||
|
(
|
||||||
|
/// Generic TLS version 1.3.
|
||||||
|
result.getName() = "tlsv13"
|
||||||
|
or
|
||||||
|
/// TLS version 1.3 client.
|
||||||
|
result.getName() = "tlsv13_client"
|
||||||
|
or
|
||||||
|
/// TLS version 1.3 server.
|
||||||
|
result.getName() = "tlsv13_server"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the value of a generic TLS or SSL/TLS protocol
|
||||||
|
*/
|
||||||
|
EnumConstant getAGenericTlsProtocolConstant() {
|
||||||
|
result = this.getAnEnumConstant() and
|
||||||
|
(
|
||||||
|
/// Generic TLS
|
||||||
|
result.getName() = "tls"
|
||||||
|
or
|
||||||
|
/// TLS client.
|
||||||
|
result.getName() = "tls_client"
|
||||||
|
or
|
||||||
|
/// TLS server.
|
||||||
|
result.getName() = "tls_server"
|
||||||
|
)
|
||||||
|
or
|
||||||
|
result = getASslv23ProtocolConstant()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the value of a generic SSL/TLS protocol
|
||||||
|
*/
|
||||||
|
EnumConstant getASslv23ProtocolConstant() {
|
||||||
|
result = this.getAnEnumConstant() and
|
||||||
|
(
|
||||||
|
/// OpenSSL - SSLv23 == A TLS/SSL connection established with these methods may understand the SSLv2, SSLv3, TLSv1, TLSv1.1 and TLSv1.2 protocols.
|
||||||
|
/// Generic SSL/TLS.
|
||||||
|
result.getName() = "sslv23"
|
||||||
|
or
|
||||||
|
/// SSL/TLS client.
|
||||||
|
result.getName() = "sslv23_client"
|
||||||
|
or
|
||||||
|
/// SSL/TLS server.
|
||||||
|
result.getName() = "sslv23_server"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NOTE: ignore - Modern versions of OpenSSL do not support SSL v2 anymore, so this option is for backwards compatibility only
|
||||||
|
*/
|
||||||
|
int getShiftedSslOptionsNoSsl2() {
|
||||||
|
// SSL_OP_NO_SSLv2 was removed from modern OpenSSL versions
|
||||||
|
result = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RightShift(16) value for no_sslv3 constant
|
||||||
|
*/
|
||||||
|
int getShiftedSslOptionsNoSsl3() {
|
||||||
|
// SSL_OP_NO_SSLv3 == 0x02000000U
|
||||||
|
result = 512
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RightShift(16) value for no_tlsv1 constant
|
||||||
|
*/
|
||||||
|
int getShiftedSslOptionsNoTls1() {
|
||||||
|
// SSL_OP_NO_TLSv1 == 0x04000000U
|
||||||
|
result = 1024
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RightShift(16) value for no_tlsv1_1 constant
|
||||||
|
*/
|
||||||
|
int getShiftedSslOptionsNoTls1_1() {
|
||||||
|
// SSL_OP_NO_TLSv1_1 == 0x10000000U
|
||||||
|
result = 4096
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RightShift(16) value for no_tlsv1_2 constant
|
||||||
|
*/
|
||||||
|
int getShiftedSslOptionsNoTls1_2() {
|
||||||
|
// SSL_OP_NO_TLSv1_2 == 0x08000000U
|
||||||
|
result = 2048
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RightShift(16) value for no_tlsv1_3 constant
|
||||||
|
*/
|
||||||
|
int getShiftedSslOptionsNoTls1_3() {
|
||||||
|
// SSL_OP_NO_TLSv1_2 == 0x20000000U
|
||||||
|
result = 8192
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents boost::asio::ssl::context class
|
||||||
|
*/
|
||||||
|
class SslContextClass extends Class {
|
||||||
|
SslContextClass() { this.getQualifiedName() = "boost::asio::ssl::context" }
|
||||||
|
|
||||||
|
ConstructorCall getAContructorCall() {
|
||||||
|
this.getAConstructor().getACallToThisFunction() = result and
|
||||||
|
not result.getLocation().getFile().toString().matches("%/boost/asio/%") and
|
||||||
|
result.fromSource()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents boost::asio::ssl::context::set_options member function
|
||||||
|
*/
|
||||||
|
class SslSetOptionsFunction extends Function {
|
||||||
|
SslSetOptionsFunction() {
|
||||||
|
this.getQualifiedName().matches("boost::asio::ssl::context::set_options")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* holds if the expression represents a banned protocol
|
||||||
|
*/
|
||||||
|
predicate isExprBannedBoostProtocol(Expr e) {
|
||||||
|
exists(Literal va | va = e |
|
||||||
|
va.getValue().toInt() = 0 or
|
||||||
|
va.getValue().toInt() = 1 or
|
||||||
|
va.getValue().toInt() = 2 or
|
||||||
|
va.getValue().toInt() = 3 or
|
||||||
|
va.getValue().toInt() = 4 or
|
||||||
|
va.getValue().toInt() = 5 or
|
||||||
|
va.getValue().toInt() = 6 or
|
||||||
|
va.getValue().toInt() = 7 or
|
||||||
|
va.getValue().toInt() = 8 or
|
||||||
|
va.getValue().toInt() = 12 or
|
||||||
|
va.getValue().toInt() = 13 or
|
||||||
|
va.getValue().toInt() = 14
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(VariableAccess va | va = e |
|
||||||
|
va.getValue().toInt() = 0 or
|
||||||
|
va.getValue().toInt() = 1 or
|
||||||
|
va.getValue().toInt() = 2 or
|
||||||
|
va.getValue().toInt() = 3 or
|
||||||
|
va.getValue().toInt() = 4 or
|
||||||
|
va.getValue().toInt() = 5 or
|
||||||
|
va.getValue().toInt() = 6 or
|
||||||
|
va.getValue().toInt() = 7 or
|
||||||
|
va.getValue().toInt() = 8 or
|
||||||
|
va.getValue().toInt() = 12 or
|
||||||
|
va.getValue().toInt() = 13 or
|
||||||
|
va.getValue().toInt() = 14
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(EnumConstantAccess eca, SslContextMethod enum | e = eca |
|
||||||
|
enum.getABannedProtocolConstant().getAnAccess() = eca
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* holds if the expression represents a TLS v1.2 protocol
|
||||||
|
*/
|
||||||
|
predicate isExprTls12BoostProtocol(Expr e) {
|
||||||
|
exists(Literal va | va = e |
|
||||||
|
(
|
||||||
|
va.getValue().toInt() = 15 or /// Generic TLS version 1.2.
|
||||||
|
va.getValue().toInt() = 16 or /// TLS version 1.2 client.
|
||||||
|
va.getValue().toInt() = 17 /// TLS version 1.2 server.
|
||||||
|
)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(VariableAccess va | va = e |
|
||||||
|
(
|
||||||
|
va.getValue().toInt() = 15 or /// Generic TLS version 1.2.
|
||||||
|
va.getValue().toInt() = 16 or /// TLS version 1.2 client.
|
||||||
|
va.getValue().toInt() = 17 /// TLS version 1.2 server.
|
||||||
|
)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(EnumConstantAccess eca, SslContextMethod enum | e = eca |
|
||||||
|
enum.getATls12ProtocolConstant().getAnAccess() = eca
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* holds if the expression represents a protocol that requires Crypto Board approval
|
||||||
|
*/
|
||||||
|
predicate isExprTls13BoostProtocol(Expr e) {
|
||||||
|
exists(Literal va | va = e |
|
||||||
|
(
|
||||||
|
va.getValue().toInt() = 18 or
|
||||||
|
va.getValue().toInt() = 19 or
|
||||||
|
va.getValue().toInt() = 20
|
||||||
|
)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(VariableAccess va | va = e |
|
||||||
|
(
|
||||||
|
va.getValue().toInt() = 18 or
|
||||||
|
va.getValue().toInt() = 19 or
|
||||||
|
va.getValue().toInt() = 20
|
||||||
|
)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(EnumConstantAccess eca, SslContextMethod enum | e = eca |
|
||||||
|
enum.getATls13ProtocolConstant().getAnAccess() = eca
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* holds if the expression represents a generic TLS or SSL/TLS protocol
|
||||||
|
*/
|
||||||
|
predicate isExprTlsBoostProtocol(Expr e) {
|
||||||
|
exists(Literal va | va = e |
|
||||||
|
(
|
||||||
|
va.getValue().toInt() = 9 or /// Generic SSL/TLS.
|
||||||
|
va.getValue().toInt() = 10 or /// SSL/TLS client.
|
||||||
|
va.getValue().toInt() = 11 or /// SSL/TLS server.
|
||||||
|
va.getValue().toInt() = 21 or /// Generic TLS.
|
||||||
|
va.getValue().toInt() = 22 or /// TLS client.
|
||||||
|
va.getValue().toInt() = 23 /// TLS server.
|
||||||
|
)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(VariableAccess va | va = e |
|
||||||
|
(
|
||||||
|
va.getValue().toInt() = 9 or /// Generic SSL/TLS.
|
||||||
|
va.getValue().toInt() = 10 or /// SSL/TLS client.
|
||||||
|
va.getValue().toInt() = 11 or /// SSL/TLS server.
|
||||||
|
va.getValue().toInt() = 21 or /// Generic TLS.
|
||||||
|
va.getValue().toInt() = 22 or /// TLS client.
|
||||||
|
va.getValue().toInt() = 23 /// TLS server.
|
||||||
|
)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(EnumConstantAccess eca, SslContextMethod enum | e = eca |
|
||||||
|
enum.getAGenericTlsProtocolConstant().getAnAccess() = eca
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* holds if the expression represents a generic SSl/TLS protocol
|
||||||
|
*/
|
||||||
|
predicate isExprSslV23BoostProtocol(Expr e) {
|
||||||
|
exists(Literal va | va = e |
|
||||||
|
(
|
||||||
|
va.getValue().toInt() = 9 or /// Generic SSL/TLS.
|
||||||
|
va.getValue().toInt() = 10 or /// SSL/TLS client.
|
||||||
|
va.getValue().toInt() = 11 /// SSL/TLS server.
|
||||||
|
)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(VariableAccess va | va = e |
|
||||||
|
(
|
||||||
|
va.getValue().toInt() = 9 or /// Generic SSL/TLS.
|
||||||
|
va.getValue().toInt() = 10 or /// SSL/TLS client.
|
||||||
|
va.getValue().toInt() = 11 /// SSL/TLS server.
|
||||||
|
)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(EnumConstantAccess eca, SslContextMethod enum | e = eca |
|
||||||
|
enum.getASslv23ProtocolConstant().getAnAccess() = eca
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////// Dataflow /////////////////////
|
||||||
|
/**
|
||||||
|
* Abstract - Protocol value Flows to the first argument of the context constructor
|
||||||
|
*/
|
||||||
|
abstract class SslContextCallAbstractConfig extends DataFlow::Configuration {
|
||||||
|
bindingset[this]
|
||||||
|
SslContextCallAbstractConfig() { any() }
|
||||||
|
|
||||||
|
override predicate isSink(DataFlow::Node sink) {
|
||||||
|
exists(ConstructorCall cc, SslContextClass c, Expr e | e = sink.asExpr() |
|
||||||
|
c.getAContructorCall() = cc and
|
||||||
|
cc.getArgument(0) = e
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* any Protocol value Flows to the first argument of the context constructor
|
||||||
|
*/
|
||||||
|
class SslContextCallConfig extends SslContextCallAbstractConfig {
|
||||||
|
SslContextCallConfig() { this = "SslContextCallConfig" }
|
||||||
|
|
||||||
|
override predicate isSource(DataFlow::Node source) {
|
||||||
|
exists(Expr e | e = source.asExpr() |
|
||||||
|
e.fromSource() and
|
||||||
|
not e.getLocation().getFile().toString().matches("%/boost/asio/%")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a banned protocol value Flows to the first argument of the context constructor
|
||||||
|
*/
|
||||||
|
class SslContextCallBannedProtocolConfig extends SslContextCallAbstractConfig {
|
||||||
|
SslContextCallBannedProtocolConfig() { this = "SslContextCallBannedProtocolConfig" }
|
||||||
|
|
||||||
|
override predicate isSource(DataFlow::Node source) {
|
||||||
|
exists(Expr e | e = source.asExpr() |
|
||||||
|
e.fromSource() and
|
||||||
|
not e.getLocation().getFile().toString().matches("%/boost/asio/%") and
|
||||||
|
isExprBannedBoostProtocol(e)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a TLS 1.2 protocol value Flows to the first argument of the context constructor
|
||||||
|
*/
|
||||||
|
class SslContextCallTls12ProtocolConfig extends SslContextCallAbstractConfig {
|
||||||
|
SslContextCallTls12ProtocolConfig() { this = "SslContextCallTls12ProtocolConfig" }
|
||||||
|
|
||||||
|
override predicate isSource(DataFlow::Node source) {
|
||||||
|
exists(Expr e | e = source.asExpr() |
|
||||||
|
e.fromSource() and
|
||||||
|
not e.getLocation().getFile().toString().matches("%/boost/asio/%") and
|
||||||
|
isExprTls12BoostProtocol(e)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a TLS 1.3 protocol value Flows to the first argument of the context constructor
|
||||||
|
*/
|
||||||
|
class SslContextCallTls13ProtocolConfig extends SslContextCallAbstractConfig {
|
||||||
|
SslContextCallTls13ProtocolConfig() { this = "SslContextCallTls12ProtocolConfig" }
|
||||||
|
|
||||||
|
override predicate isSource(DataFlow::Node source) {
|
||||||
|
exists(Expr e | e = source.asExpr() |
|
||||||
|
e.fromSource() and
|
||||||
|
not e.getLocation().getFile().toString().matches("%/boost/asio/%") and
|
||||||
|
isExprTls13BoostProtocol(e)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a generic TLS protocol value Flows to the first argument of the context constructor
|
||||||
|
*/
|
||||||
|
class SslContextCallTlsProtocolConfig extends SslContextCallAbstractConfig {
|
||||||
|
SslContextCallTlsProtocolConfig() { this = "SslContextCallTlsProtocolConfig" }
|
||||||
|
|
||||||
|
override predicate isSource(DataFlow::Node source) {
|
||||||
|
exists(Expr e | e = source.asExpr() |
|
||||||
|
e.fromSource() and
|
||||||
|
not e.getLocation().getFile().toString().matches("%/boost/asio/%") and
|
||||||
|
isExprTlsBoostProtocol(e)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a context constructor call flows to a call calling SetOptions()
|
||||||
|
*/
|
||||||
|
class SslContextFlowsToSetOptionConfig extends DataFlow::Configuration {
|
||||||
|
SslContextFlowsToSetOptionConfig() { this = "SslContextFlowsToSetOptionConfig" }
|
||||||
|
|
||||||
|
override predicate isSource(DataFlow::Node source) {
|
||||||
|
exists(SslContextClass c, ConstructorCall cc |
|
||||||
|
cc = source.asExpr() and
|
||||||
|
c.getAContructorCall() = cc
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate isSink(DataFlow::Node sink) {
|
||||||
|
exists(FunctionCall fc, SslSetOptionsFunction f, Variable v, VariableAccess va |
|
||||||
|
va = sink.asExpr()
|
||||||
|
|
|
||||||
|
f.getACallToThisFunction() = fc and
|
||||||
|
v.getAnAccess() = va and
|
||||||
|
va = fc.getQualifier()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* an option value flows to the 1st parameter of SetOptions()
|
||||||
|
*/
|
||||||
|
class SslOptionConfig extends DataFlow::Configuration {
|
||||||
|
SslOptionConfig() { this = "SslOptionConfig" }
|
||||||
|
|
||||||
|
override predicate isSource(DataFlow::Node source) {
|
||||||
|
exists(Expr e | e = source.asExpr() |
|
||||||
|
e.fromSource() and
|
||||||
|
not e.getLocation().getFile().toString().matches("%/boost/asio/%")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate isSink(DataFlow::Node sink) {
|
||||||
|
exists(SslSetOptionsFunction f, FunctionCall call |
|
||||||
|
sink.asExpr() = call.getArgument(0) and
|
||||||
|
f.getACallToThisFunction() = call and
|
||||||
|
not sink.getLocation().getFile().toString().matches("%/boost/asio/%")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -294,6 +294,8 @@ class IfStmt extends ConditionalStmt, @stmt_if {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if {
|
class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if {
|
||||||
|
override string getCanonicalQLClass() { result = "ConstexprIfStmt" }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the condition expression of this 'constexpr if' statement.
|
* Gets the condition expression of this 'constexpr if' statement.
|
||||||
*
|
*
|
||||||
@@ -1645,6 +1647,7 @@ class EnumSwitch extends SwitchStmt {
|
|||||||
* } catch (std::exception &e) {
|
* } catch (std::exception &e) {
|
||||||
* g();
|
* g();
|
||||||
* }
|
* }
|
||||||
|
* ```
|
||||||
* there is a handler that's associated with the `catch` block and controls
|
* there is a handler that's associated with the `catch` block and controls
|
||||||
* entry to it.
|
* entry to it.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -729,6 +729,18 @@ private predicate mk_AlignofExpr(HashCons child, AlignofExprOperator e) {
|
|||||||
child = hashCons(e.getAChild())
|
child = hashCons(e.getAChild())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the hash cons of field initializer expressions [0..i), where i > 0, for
|
||||||
|
* the class aggregate literal `cal` of type `c`, where `head` is the hash cons
|
||||||
|
* of the i'th initializer expression.
|
||||||
|
*/
|
||||||
|
HC_Fields aggInitExprsUpTo(ClassAggregateLiteral cal, Class c, int i) {
|
||||||
|
exists(Field f, HashCons head, HC_Fields tail |
|
||||||
|
result = HC_FieldCons(c, i - 1, f, head, tail) and
|
||||||
|
mk_FieldCons(c, i - 1, f, head, tail, cal)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private predicate mk_FieldCons(
|
private predicate mk_FieldCons(
|
||||||
Class c, int i, Field f, HashCons hc, HC_Fields hcf, ClassAggregateLiteral cal
|
Class c, int i, Field f, HashCons hc, HC_Fields hcf, ClassAggregateLiteral cal
|
||||||
) {
|
) {
|
||||||
@@ -738,12 +750,8 @@ private predicate mk_FieldCons(
|
|||||||
e = cal.getFieldExpr(f).getFullyConverted() and
|
e = cal.getFieldExpr(f).getFullyConverted() and
|
||||||
f.getInitializationOrder() = i and
|
f.getInitializationOrder() = i and
|
||||||
(
|
(
|
||||||
exists(HashCons head, Field f2, HC_Fields tail |
|
|
||||||
hc = hashCons(e) and
|
hc = hashCons(e) and
|
||||||
hcf = HC_FieldCons(c, i - 1, f2, head, tail) and
|
hcf = aggInitExprsUpTo(cal, c, i)
|
||||||
f2.getInitializationOrder() = i - 1 and
|
|
||||||
mk_FieldCons(c, i - 1, f2, head, tail, cal)
|
|
||||||
)
|
|
||||||
or
|
or
|
||||||
hc = hashCons(e) and
|
hc = hashCons(e) and
|
||||||
i = 0 and
|
i = 0 and
|
||||||
@@ -766,14 +774,7 @@ private predicate mk_ClassAggregateLiteral(Class c, HC_Fields hcf, ClassAggregat
|
|||||||
analyzableClassAggregateLiteral(cal) and
|
analyzableClassAggregateLiteral(cal) and
|
||||||
c = cal.getUnspecifiedType() and
|
c = cal.getUnspecifiedType() and
|
||||||
(
|
(
|
||||||
exists(HC_Fields tail, Expr e, Field f, int numChildren, HashCons eCons |
|
hcf = aggInitExprsUpTo(cal, c, cal.getNumChild())
|
||||||
f.getInitializationOrder() = cal.getNumChild() - 1 and
|
|
||||||
e = cal.getFieldExpr(f).getFullyConverted() and
|
|
||||||
eCons = hashCons(e) and
|
|
||||||
numChildren = cal.getNumChild() and
|
|
||||||
hcf = HC_FieldCons(c, numChildren - 1, f, eCons, tail) and
|
|
||||||
mk_FieldCons(c, numChildren - 1, f, eCons, tail, cal)
|
|
||||||
)
|
|
||||||
or
|
or
|
||||||
cal.getNumChild() = 0 and
|
cal.getNumChild() = 0 and
|
||||||
hcf = HC_EmptyFields(c)
|
hcf = HC_EmptyFields(c)
|
||||||
@@ -785,15 +786,23 @@ private predicate analyzableArrayAggregateLiteral(ArrayAggregateLiteral aal) {
|
|||||||
strictcount(aal.getUnspecifiedType()) = 1
|
strictcount(aal.getUnspecifiedType()) = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the hash cons of array elements in [0..i), where i > 0, for
|
||||||
|
* the array aggregate literal `aal` of type `t`.
|
||||||
|
*/
|
||||||
|
private HC_Array arrayElemsUpTo(ArrayAggregateLiteral aal, Type t, int i) {
|
||||||
|
exists(HC_Array tail, HashCons head |
|
||||||
|
result = HC_ArrayCons(t, i - 1, head, tail) and
|
||||||
|
mk_ArrayCons(t, i - 1, head, tail, aal)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private predicate mk_ArrayCons(Type t, int i, HashCons hc, HC_Array hca, ArrayAggregateLiteral aal) {
|
private predicate mk_ArrayCons(Type t, int i, HashCons hc, HC_Array hca, ArrayAggregateLiteral aal) {
|
||||||
analyzableArrayAggregateLiteral(aal) and
|
analyzableArrayAggregateLiteral(aal) and
|
||||||
t = aal.getUnspecifiedType() and
|
t = aal.getUnspecifiedType() and
|
||||||
hc = hashCons(aal.getChild(i)) and
|
hc = hashCons(aal.getChild(i)) and
|
||||||
(
|
(
|
||||||
exists(HC_Array tail, HashCons head |
|
hca = arrayElemsUpTo(aal, t, i)
|
||||||
hca = HC_ArrayCons(t, i - 1, head, tail) and
|
|
||||||
mk_ArrayCons(t, i - 1, head, tail, aal)
|
|
||||||
)
|
|
||||||
or
|
or
|
||||||
i = 0 and
|
i = 0 and
|
||||||
hca = HC_EmptyArray(t)
|
hca = HC_EmptyArray(t)
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ compilation_time(
|
|||||||
*/
|
*/
|
||||||
#keyset[compilation, file_number, file_number_diagnostic_number]
|
#keyset[compilation, file_number, file_number_diagnostic_number]
|
||||||
diagnostic_for(
|
diagnostic_for(
|
||||||
unique int diagnostic : @diagnostic ref,
|
int diagnostic : @diagnostic ref,
|
||||||
int compilation : @compilation ref,
|
int compilation : @compilation ref,
|
||||||
int file_number : int ref,
|
int file_number : int ref,
|
||||||
int file_number_diagnostic_number : int ref
|
int file_number_diagnostic_number : int ref
|
||||||
@@ -422,8 +422,9 @@ function_defaulted(unique int id: @function ref);
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#keyset[id, type_id]
|
||||||
fun_decls(
|
fun_decls(
|
||||||
unique int id: @fun_decl,
|
int id: @fun_decl,
|
||||||
int function: @function ref,
|
int function: @function ref,
|
||||||
int type_id: @type ref,
|
int type_id: @type ref,
|
||||||
string name: string ref,
|
string name: string ref,
|
||||||
@@ -460,8 +461,9 @@ param_decl_bind(
|
|||||||
int fun_decl: @fun_decl ref
|
int fun_decl: @fun_decl ref
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#keyset[id, type_id]
|
||||||
var_decls(
|
var_decls(
|
||||||
unique int id: @var_decl,
|
int id: @var_decl,
|
||||||
int variable: @variable ref,
|
int variable: @variable ref,
|
||||||
int type_id: @type ref,
|
int type_id: @type ref,
|
||||||
string name: string ref,
|
string name: string ref,
|
||||||
@@ -521,18 +523,21 @@ params(
|
|||||||
|
|
||||||
overrides(int new: @function ref, int old: @function ref);
|
overrides(int new: @function ref, int old: @function ref);
|
||||||
|
|
||||||
|
#keyset[id, type_id]
|
||||||
membervariables(
|
membervariables(
|
||||||
unique int id: @membervariable,
|
int id: @membervariable,
|
||||||
int type_id: @type ref,
|
int type_id: @type ref,
|
||||||
string name: string ref
|
string name: string ref
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#keyset[id, type_id]
|
||||||
globalvariables(
|
globalvariables(
|
||||||
unique int id: @globalvariable,
|
int id: @globalvariable,
|
||||||
int type_id: @type ref,
|
int type_id: @type ref,
|
||||||
string name: string ref
|
string name: string ref
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#keyset[id, type_id]
|
||||||
localvariables(
|
localvariables(
|
||||||
int id: @localvariable,
|
int id: @localvariable,
|
||||||
int type_id: @type ref,
|
int type_id: @type ref,
|
||||||
@@ -675,7 +680,7 @@ decltypes(
|
|||||||
| 2 = class
|
| 2 = class
|
||||||
| 3 = union
|
| 3 = union
|
||||||
| 4 = enum
|
| 4 = enum
|
||||||
| 5 = typedef
|
| 5 = typedef // classic C: typedef typedef type name
|
||||||
| 6 = template
|
| 6 = template
|
||||||
| 7 = template_parameter
|
| 7 = template_parameter
|
||||||
| 8 = template_template_parameter
|
| 8 = template_template_parameter
|
||||||
@@ -684,6 +689,7 @@ decltypes(
|
|||||||
// ... 11 objc_protocol deprecated
|
// ... 11 objc_protocol deprecated
|
||||||
// ... 12 objc_category deprecated
|
// ... 12 objc_category deprecated
|
||||||
| 13 = scoped_enum
|
| 13 = scoped_enum
|
||||||
|
| 14 = using_alias // a using name = type style typedef
|
||||||
;
|
;
|
||||||
*/
|
*/
|
||||||
usertypes(
|
usertypes(
|
||||||
@@ -1060,10 +1066,12 @@ compgenerated(unique int id: @element ref);
|
|||||||
* destructed in reverse construction order, so for a given `element`
|
* destructed in reverse construction order, so for a given `element`
|
||||||
* these should be called from highest to lowest `i`.
|
* these should be called from highest to lowest `i`.
|
||||||
*/
|
*/
|
||||||
|
#keyset[element, destructor_call]
|
||||||
|
#keyset[element, i]
|
||||||
synthetic_destructor_call(
|
synthetic_destructor_call(
|
||||||
int element: @element ref,
|
int element: @element ref,
|
||||||
int i: int ref,
|
int i: int ref,
|
||||||
unique int destructor_call: @routineexpr ref
|
int destructor_call: @routineexpr ref
|
||||||
);
|
);
|
||||||
|
|
||||||
namespaces(
|
namespaces(
|
||||||
@@ -1509,6 +1517,7 @@ case @expr.kind of
|
|||||||
| 322 = @builtinaddressof
|
| 322 = @builtinaddressof
|
||||||
| 323 = @vec_fill
|
| 323 = @vec_fill
|
||||||
| 324 = @builtinconvertvector
|
| 324 = @builtinconvertvector
|
||||||
|
| 325 = @builtincomplex
|
||||||
;
|
;
|
||||||
|
|
||||||
new_allocated_type(
|
new_allocated_type(
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
12
cpp/ql/test/duplication-tests/constants/constants.cpp
Normal file
12
cpp/ql/test/duplication-tests/constants/constants.cpp
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
int x = int();
|
||||||
|
float y = float();
|
||||||
|
double z = double();
|
||||||
|
|
||||||
|
/* This produces a getValueText() of 0 for R() in line 9, which is debatable. */
|
||||||
|
struct R {};
|
||||||
|
struct S {
|
||||||
|
S() : S(R()) { }
|
||||||
|
S(R) { }
|
||||||
|
};
|
||||||
|
S s;
|
||||||
4
cpp/ql/test/duplication-tests/constants/expr.expected
Normal file
4
cpp/ql/test/duplication-tests/constants/expr.expected
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
| constants.cpp:2:9:2:13 | 0 | int() |
|
||||||
|
| constants.cpp:3:11:3:17 | 0.0 | float() |
|
||||||
|
| constants.cpp:4:12:4:19 | 0.0 | double() |
|
||||||
|
| constants.cpp:9:11:9:13 | 0 | 0 |
|
||||||
4
cpp/ql/test/duplication-tests/constants/expr.ql
Normal file
4
cpp/ql/test/duplication-tests/constants/expr.ql
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import cpp
|
||||||
|
|
||||||
|
from Expr e
|
||||||
|
select e, e.getValueText()
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user