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:
Jonas Jensen
2019-10-23 08:46:39 +02:00
764 changed files with 47816 additions and 11145 deletions

3
.codeqlmanifest.json Normal file
View 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
View 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
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View 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

View File

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

View File

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

View File

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

@@ -0,0 +1,4 @@
name: codeql-cpp
version: 0.0.0
dbscheme: semmlecode.cpp.dbscheme
suites: codeql-suites

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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/%")
)
}
}
}

View File

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

View File

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

View File

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

View 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;

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

View 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