mirror of
https://github.com/github/codeql.git
synced 2026-05-02 20:25:13 +02:00
4
.codeqlmanifest.json
Normal file
4
.codeqlmanifest.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{ "provide": [ "*/ql/src/qlpack.yml",
|
||||
"misc/legacy-support/*/qlpack.yml",
|
||||
"misc/suite-helpers/qlpack.yml",
|
||||
"codeql/.codeqlmanifest.json" ] }
|
||||
14
.github/ISSUE_TEMPLATE/ql---general.md
vendored
Normal file
14
.github/ISSUE_TEMPLATE/ql---general.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
name: General issue
|
||||
about: Tell us if you think something is wrong or if you have a question
|
||||
title: General issue
|
||||
labels: question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Description of the issue**
|
||||
|
||||
<!-- Please explain briefly what is the problem.
|
||||
If it is about an LGTM project, please include its URL.-->
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -12,3 +12,6 @@
|
||||
# Visual studio temporaries, except a file used by QL4VS
|
||||
.vs/*
|
||||
!.vs/VSWorkspaceSettings.json
|
||||
|
||||
# It's useful (though not required) to be able to unpack codeql in the ql checkout itself
|
||||
/codeql/
|
||||
|
||||
10
CODEOWNERS
10
CODEOWNERS
@@ -2,9 +2,9 @@
|
||||
/java/ @Semmle/java
|
||||
/javascript/ @Semmle/js
|
||||
/cpp/ @Semmle/cpp-analysis
|
||||
/cpp/**/*.qhelp @semmledocs-ac
|
||||
/cpp/**/*.qhelp @hubwriter
|
||||
/csharp/**/*.qhelp @jf205
|
||||
/java/**/*.qhelp @felicity-semmle
|
||||
/javascript/**/*.qhelp @mc-semmle
|
||||
/python/**/*.qhelp @felicity-semmle
|
||||
/docs/language/ @felicity-semmle @jf205
|
||||
/java/**/*.qhelp @felicitymay
|
||||
/javascript/**/*.qhelp @mchammer01
|
||||
/python/**/*.qhelp @felicitymay
|
||||
/docs/language/ @shati-patel @jf205
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
| Shift out of range (`js/shift-out-of-range`| Fewer false positive results | This rule now correctly handles BigInt shift operands. |
|
||||
| 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. |
|
||||
| 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
|
||||
|
||||
|
||||
@@ -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 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). |
|
||||
| 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
|
||||
|
||||
@@ -28,6 +32,8 @@ 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
|
||||
disabled by default and can be enabled for individual configurations by
|
||||
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 `DataFlow::DefinitionByReferenceNode` class now considers `f(x)` to be a
|
||||
definition of `x` when `x` is a variable of pointer type. It no longer
|
||||
considers deep paths such as `f(&x.myField)` to be definitions of `x`. These
|
||||
@@ -35,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
|
||||
`TaintTracking::localExprTaint` predicate to make it easy to use the most
|
||||
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.
|
||||
|
||||
@@ -15,6 +15,8 @@ The following changes in version 1.23 affect C# analysis in all applications.
|
||||
|
||||
| **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) ...`. |
|
||||
| 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
|
||||
|
||||
@@ -37,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
|
||||
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).
|
||||
* 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
|
||||
|
||||
@@ -2,13 +2,22 @@
|
||||
|
||||
The following changes in version 1.23 affect Java analysis in all applications.
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
| Continue statement that does not continue (`java/continue-in-false-loop`) | correctness | Finds `continue` statements in `do { ... } while (false)` loops. |
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **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 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. |
|
||||
| Useless comparison test (`java/constant-comparison`) | Fewer false positives | Additional overflow check patterns are now recognized and no longer reported. |
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
|
||||
@@ -2,34 +2,66 @@
|
||||
|
||||
## General improvements
|
||||
|
||||
* Suppor for `globalThis` has been added.
|
||||
|
||||
* Support for the following frameworks and libraries has been improved:
|
||||
- [firebase](https://www.npmjs.com/package/firebase)
|
||||
- [mongodb](https://www.npmjs.com/package/mongodb)
|
||||
- [mongoose](https://www.npmjs.com/package/mongoose)
|
||||
- [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible)
|
||||
|
||||
* The call graph has been improved to resolve method calls in more cases. This may produce more security alerts.
|
||||
|
||||
* TypeScript 3.6 features are supported.
|
||||
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|---------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Unused index variable (`js/unused-index-variable`) | correctness | Highlights loops that iterate over an array, but do not use the index variable to access array elements, indicating a possible typo or logic error. |
|
||||
|
||||
| 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 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
|
||||
|
||||
| **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. |
|
||||
| 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. |
|
||||
| Hard-coded credentials (`js/hardcoded-credentials`) | Fewer false-positive results | This rule now flags fewer password examples. |
|
||||
| Incorrect suffix check (`js/incorrect-suffix-check`) | Fewer false-positive results | The query recognizes valid checks in more cases.
|
||||
| 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. |
|
||||
| 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. |
|
||||
| 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. |
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
* `Expr.getDocumentation()` now handles chain assignments.
|
||||
|
||||
## Removal of deprecated queries
|
||||
|
||||
The following queries (deprecated since 1.17) are no longer available in the distribution:
|
||||
|
||||
* Builtin redefined (js/builtin-redefinition)
|
||||
* Inefficient method definition (js/method-definition-in-constructor)
|
||||
* Bad parity check (js/incomplete-parity-check)
|
||||
* Potentially misspelled property or variable name (js/wrong-capitalization)
|
||||
* Unknown JSDoc tag (js/jsdoc/unknown-tag-type)
|
||||
* Invalid JSLint directive (js/jslint/invalid-directive)
|
||||
* Malformed JSLint directive (js/jslint/malformed-directive)
|
||||
* Use of HTML comments (js/html-comment)
|
||||
* Multi-line string literal (js/multi-line-string)
|
||||
* Octal literal (js/octal-literal)
|
||||
* Reserved word used as variable name (js/use-of-reserved-word)
|
||||
* Trailing comma in array or object expressions (js/trailing-comma-in-array-or-object)
|
||||
* Call to parseInt without radix (js/parseint-without-radix)
|
||||
|
||||
@@ -11,4 +11,12 @@
|
||||
|-----------|----------|-------------|
|
||||
| Clear-text logging of sensitive information (`py/clear-text-logging-sensitive-data`) | security, external/cwe/cwe-312 | Finds instances where sensitive information is logged without encryption or hashing. Results are shown on LGTM by default. |
|
||||
| Clear-text 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. |
|
||||
| `__iter__` method returns a non-iterator | Better alert message | Alert now highlights which class is expected to be an iterator. |
|
||||
|
||||
@@ -5,4 +5,8 @@
|
||||
## Changes to code extraction
|
||||
|
||||
* Asynchronous generator methods are now parsed correctly and no longer cause a spurious syntax error.
|
||||
* Recognition of CommonJS modules has improved. As a result, some files that were previously extracted as
|
||||
global scripts are now extracted as modules.
|
||||
* Top-level `await` is now supported.
|
||||
* A bug was fixed in how the TypeScript extractor handles default-exported anonymous classes.
|
||||
* A bug was fixed in how the TypeScript extractor handles computed instance field names.
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
Now that all of the QL documentation is in this repository,
|
||||
notes on the languages, compilers, and frameworks supported have moved.
|
||||
They're now stored as part of the Sphinx ``support`` project with the other documentation:
|
||||
``docs/ql-documentation/support``.
|
||||
``docs/language/support``.
|
||||
|
||||
@@ -47,31 +47,40 @@
|
||||
"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/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": [
|
||||
"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/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": [
|
||||
"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/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": [
|
||||
"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/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": [
|
||||
"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/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 IRType": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/IRType.qll"
|
||||
],
|
||||
"IR Operand Tag": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
|
||||
@@ -85,19 +94,22 @@
|
||||
"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/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": [
|
||||
"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/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": [
|
||||
"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/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": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll",
|
||||
@@ -161,22 +173,39 @@
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintIRImports.qll"
|
||||
],
|
||||
"C++ SSA SSAConstructionImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll"
|
||||
],
|
||||
"C++ SSA AliasAnalysis": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll"
|
||||
],
|
||||
"C++ SSA SSAConstruction": [
|
||||
"C++ IR ValueNumberingImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingImports.qll"
|
||||
],
|
||||
"IR SSA SimpleSSA": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll"
|
||||
],
|
||||
"IR SSA SSAConstruction": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll"
|
||||
],
|
||||
"C++ SSA PrintSSA": [
|
||||
"IR SSA PrintSSA": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll"
|
||||
],
|
||||
"C++ IR ValueNumber": [
|
||||
"IR ValueNumber": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll"
|
||||
],
|
||||
"C++ IR ConstantAnalysis": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll",
|
||||
@@ -205,21 +234,31 @@
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
|
||||
],
|
||||
"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": [
|
||||
"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": [
|
||||
"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": [
|
||||
"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": [
|
||||
"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": [
|
||||
"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"
|
||||
],
|
||||
"C# IR ValueNumberingImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -28,7 +28,8 @@ where
|
||||
not bf.getType().hasName("BOOL") and
|
||||
// If this is true, then there cannot be unsigned sign extension or overflow.
|
||||
not bf.getDeclaredNumBits() = bf.getType().getSize() * 8 and
|
||||
not bf.isAnonymous()
|
||||
not bf.isAnonymous() and
|
||||
not bf.isFromUninstantiatedTemplate(_)
|
||||
select bf,
|
||||
"Bit field " + bf.getName() + " of type " + bf.getUnderlyingType().getName() +
|
||||
" should have explicitly unsigned integral, explicitly signed integral, or enumeration type."
|
||||
|
||||
@@ -2,36 +2,39 @@
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
Checking for overflow of integer addition needs to be done with
|
||||
care, because automatic type promotion can prevent the check
|
||||
from working correctly.
|
||||
</p>
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>
|
||||
Use an explicit cast to make sure that the result of the addition is
|
||||
not implicitly converted to a larger type.
|
||||
</p>
|
||||
</recommendation>
|
||||
<example>
|
||||
<sample src="BadAdditionOverflowCheckExample1.cpp" />
|
||||
<p>
|
||||
On a typical architecture where <tt>short</tt> is 16 bits
|
||||
and <tt>int</tt> is 32 bits, the operands of the addition are
|
||||
automatically promoted to <tt>int</tt>, so it cannot overflow
|
||||
and the result of the comparison is always false.
|
||||
</p>
|
||||
<p>
|
||||
The code below implements the check correctly, by using an
|
||||
explicit cast to make sure that the result of the addition
|
||||
is <tt>unsigned short</tt>.
|
||||
</p>
|
||||
<sample src="BadAdditionOverflowCheckExample2.cpp" />
|
||||
</example>
|
||||
<references>
|
||||
<li><a href="http://c-faq.com/expr/preservingrules.html">Preserving Rules</a></li>
|
||||
<li><a href="https://www.securecoding.cert.org/confluence/plugins/servlet/mobile#content/view/20086942">Understand integer conversion rules</a></li>
|
||||
</references>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Checking for overflow of integer addition needs to be done with
|
||||
care, because automatic type promotion can prevent the check
|
||||
from working as intended, with the same value (<code>true</code>
|
||||
or <code>false</code>) always being returned.
|
||||
</p>
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>
|
||||
Use an explicit cast to make sure that the result of the addition is
|
||||
not implicitly converted to a larger type.
|
||||
</p>
|
||||
</recommendation>
|
||||
<example>
|
||||
<sample src="BadAdditionOverflowCheckExample1.cpp" />
|
||||
<p>
|
||||
On a typical architecture where <code>short</code> is 16 bits
|
||||
and <code>int</code> is 32 bits, the operands of the addition are
|
||||
automatically promoted to <code>int</code>, so it cannot overflow
|
||||
and the result of the comparison is always false.
|
||||
</p>
|
||||
<p>
|
||||
The code below implements the check correctly, by using an
|
||||
explicit cast to make sure that the result of the addition
|
||||
is <code>unsigned short</code> (which may overflow, in which case
|
||||
the comparison would evaluate to <code>true</code>).
|
||||
</p>
|
||||
<sample src="BadAdditionOverflowCheckExample2.cpp" />
|
||||
</example>
|
||||
<references>
|
||||
<li><a href="http://c-faq.com/expr/preservingrules.html">Preserving Rules</a></li>
|
||||
<li><a href="https://www.securecoding.cert.org/confluence/plugins/servlet/mobile#content/view/20086942">Understand integer conversion rules</a></li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
bool checkOverflow(unsigned short x, unsigned short y) {
|
||||
return (x + y < x); // BAD: x and y are automatically promoted to int.
|
||||
// BAD: comparison is always false due to type promotion
|
||||
return (x + y < x);
|
||||
}
|
||||
|
||||
@@ -14,9 +14,16 @@ import cpp
|
||||
|
||||
from RelationalOperation e, BinaryBitwiseOperation lhs
|
||||
where
|
||||
lhs = e.getGreaterOperand() and
|
||||
lhs.getActualType().(IntegralType).isSigned() and
|
||||
forall(int op | op = lhs.(BitwiseAndExpr).getAnOperand().getValue().toInt() | op < 0) and
|
||||
// `lhs > 0` (or `0 < lhs`)
|
||||
// (note that `lhs < 0`, `lhs >= 0` or `lhs <= 0` all imply that the signedness of
|
||||
// `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
|
||||
// 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()
|
||||
select e, "Potential unsafe sign check of a bitwise operation."
|
||||
|
||||
@@ -14,5 +14,8 @@
|
||||
import cpp
|
||||
|
||||
from ComparisonOperation co, ComparisonOperation chco
|
||||
where co.getAChild() = chco and not chco.isParenthesised()
|
||||
select co, "Check the comparison operator precedence."
|
||||
where
|
||||
co.getAChild() = chco and
|
||||
not chco.isParenthesised() and
|
||||
not co.isFromUninstantiatedTemplate(_)
|
||||
select co, "Comparison as an operand to another comparison."
|
||||
|
||||
@@ -157,7 +157,8 @@ where
|
||||
formatOtherArgType(ffc, n, expected, arg, actual) and
|
||||
not actual.getUnspecifiedType().(IntegralType).getSize() = sizeof_IntType()
|
||||
) and
|
||||
not arg.isAffectedByMacro()
|
||||
not arg.isAffectedByMacro() and
|
||||
not arg.isFromUninstantiatedTemplate(_)
|
||||
select arg,
|
||||
"This argument should be of type '" + expected.getName() + "' but is of type '" +
|
||||
actual.getUnspecifiedType().getName() + "'"
|
||||
|
||||
@@ -30,7 +30,7 @@ private predicate additionalLogicalCheck(Expr e, string operation, int valueToCh
|
||||
/**
|
||||
* An `Operation` that seems to be checking for leap year.
|
||||
*/
|
||||
class CheckForLeapYearOperation extends Operation {
|
||||
class CheckForLeapYearOperation extends Expr {
|
||||
CheckForLeapYearOperation() {
|
||||
exists(BinaryArithmeticOperation bo | bo = this |
|
||||
bo.getAnOperand().getValue().toInt() = 4 and
|
||||
@@ -39,8 +39,6 @@ class CheckForLeapYearOperation extends Operation {
|
||||
additionalLogicalCheck(this.getEnclosingElement(), "%", 400)
|
||||
)
|
||||
}
|
||||
|
||||
override string getOperator() { result = "LeapYearCheck" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
private import semmle.code.cpp.dataflow.RecursionPrevention
|
||||
|
||||
/**
|
||||
* A buffer which includes an allocation size.
|
||||
*/
|
||||
abstract class BufferWithSize extends DataFlow::Node {
|
||||
abstract Expr getSizeExpr();
|
||||
|
||||
BufferAccess getAnAccess() {
|
||||
any(BufferWithSizeConfig bsc).hasFlow(this, DataFlow::exprNode(result.getPointer()))
|
||||
}
|
||||
}
|
||||
|
||||
/** An allocation function. */
|
||||
abstract class Alloc extends Function { }
|
||||
|
||||
/**
|
||||
* Allocation functions identified by the QL for C/C++ standard library.
|
||||
*/
|
||||
class DefaultAlloc extends Alloc {
|
||||
DefaultAlloc() { allocationFunction(this) }
|
||||
}
|
||||
|
||||
/** A buffer created through a call to an allocation function. */
|
||||
class AllocBuffer extends BufferWithSize {
|
||||
FunctionCall call;
|
||||
|
||||
AllocBuffer() {
|
||||
asExpr() = call and
|
||||
call.getTarget() instanceof Alloc
|
||||
}
|
||||
|
||||
override Expr getSizeExpr() { result = call.getArgument(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Find accesses of buffers for which we have a size expression.
|
||||
*/
|
||||
private class BufferWithSizeConfig extends TaintTracking::Configuration {
|
||||
BufferWithSizeConfig() { this = "BufferWithSize" }
|
||||
|
||||
override predicate isSource(DataFlow::Node n) { n = any(BufferWithSize b) }
|
||||
|
||||
override predicate isSink(DataFlow::Node n) { n.asExpr() = any(BufferAccess ae).getPointer() }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node s) {
|
||||
s = any(BufferWithSize b) and
|
||||
s.asExpr().getControlFlowScope() instanceof Alloc
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An access (read or write) to a buffer, provided as a pair of
|
||||
* a pointer to the buffer and the length of data to be read or written.
|
||||
* Extend this class to support different kinds of buffer access.
|
||||
*/
|
||||
abstract class BufferAccess extends Locatable {
|
||||
/** Gets the pointer to the buffer being accessed. */
|
||||
abstract Expr getPointer();
|
||||
|
||||
/** Gets the length of the data being read or written by this buffer access. */
|
||||
abstract Expr getAccessedLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* A buffer access through an array expression.
|
||||
*/
|
||||
class ArrayBufferAccess extends BufferAccess, ArrayExpr {
|
||||
override Expr getPointer() { result = this.getArrayBase() }
|
||||
|
||||
override Expr getAccessedLength() { result = this.getArrayOffset() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A buffer access through an overloaded array expression.
|
||||
*/
|
||||
class OverloadedArrayBufferAccess extends BufferAccess, OverloadedArrayExpr {
|
||||
override Expr getPointer() { result = this.getQualifier() }
|
||||
|
||||
override Expr getAccessedLength() { result = this.getAnArgument() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A buffer access through pointer arithmetic.
|
||||
*/
|
||||
class PointerArithmeticAccess extends BufferAccess, Expr {
|
||||
PointerArithmeticOperation p;
|
||||
|
||||
PointerArithmeticAccess() {
|
||||
this = p and
|
||||
p.getAnOperand().getType().getUnspecifiedType() instanceof IntegralType and
|
||||
not p.getParent() instanceof ComparisonOperation
|
||||
}
|
||||
|
||||
override Expr getPointer() {
|
||||
result = p.getAnOperand() and
|
||||
result.getType().getUnspecifiedType() instanceof PointerType
|
||||
}
|
||||
|
||||
override Expr getAccessedLength() {
|
||||
result = p.getAnOperand() and
|
||||
result.getType().getUnspecifiedType() instanceof IntegralType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A pair of buffer accesses through a call to memcpy.
|
||||
*/
|
||||
class MemCpy extends BufferAccess, FunctionCall {
|
||||
MemCpy() { getTarget().hasName("memcpy") }
|
||||
|
||||
override Expr getPointer() {
|
||||
result = getArgument(0) or
|
||||
result = getArgument(1)
|
||||
}
|
||||
|
||||
override Expr getAccessedLength() { result = getArgument(2) }
|
||||
}
|
||||
|
||||
class StrncpySizeExpr extends BufferAccess, FunctionCall {
|
||||
StrncpySizeExpr() { getTarget().hasName("strncpy") }
|
||||
|
||||
override Expr getPointer() {
|
||||
result = getArgument(0) or
|
||||
result = getArgument(1)
|
||||
}
|
||||
|
||||
override Expr getAccessedLength() { result = getArgument(2) }
|
||||
}
|
||||
|
||||
class RecvSizeExpr extends BufferAccess, FunctionCall {
|
||||
RecvSizeExpr() { getTarget().hasName("recv") }
|
||||
|
||||
override Expr getPointer() { result = getArgument(1) }
|
||||
|
||||
override Expr getAccessedLength() { result = getArgument(2) }
|
||||
}
|
||||
|
||||
class SendSizeExpr extends BufferAccess, FunctionCall {
|
||||
SendSizeExpr() { getTarget().hasName("send") }
|
||||
|
||||
override Expr getPointer() { result = getArgument(1) }
|
||||
|
||||
override Expr getAccessedLength() { result = getArgument(2) }
|
||||
}
|
||||
|
||||
class SnprintfSizeExpr extends BufferAccess, FunctionCall {
|
||||
SnprintfSizeExpr() { getTarget().hasName("snprintf") }
|
||||
|
||||
override Expr getPointer() { result = getArgument(0) }
|
||||
|
||||
override Expr getAccessedLength() { result = getArgument(1) }
|
||||
}
|
||||
|
||||
class MemcmpSizeExpr extends BufferAccess, FunctionCall {
|
||||
MemcmpSizeExpr() { getTarget().hasName("Memcmp") }
|
||||
|
||||
override Expr getPointer() {
|
||||
result = getArgument(0) or
|
||||
result = getArgument(1)
|
||||
}
|
||||
|
||||
override Expr getAccessedLength() { result = getArgument(2) }
|
||||
}
|
||||
|
||||
class MallocSizeExpr extends BufferAccess, FunctionCall {
|
||||
MallocSizeExpr() { getTarget().hasName("malloc") }
|
||||
|
||||
override Expr getPointer() { none() }
|
||||
|
||||
override Expr getAccessedLength() { result = getArgument(1) }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
int get_number_from_network();
|
||||
|
||||
int process_network(int[] buff, int buffSize) {
|
||||
int i = ntohl(get_number_from_network());
|
||||
return buff[i];
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
uint32_t get_number_from_network();
|
||||
|
||||
int process_network(int[] buff, uint32_t buffSize) {
|
||||
uint32_t i = ntohl(get_number_from_network());
|
||||
if (i < buffSize) {
|
||||
return buff[i];
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
import BufferAccess
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
class NetworkFunctionCall extends FunctionCall {
|
||||
NetworkFunctionCall() {
|
||||
getTarget().hasName("ntohd") or
|
||||
getTarget().hasName("ntohf") or
|
||||
getTarget().hasName("ntohl") or
|
||||
getTarget().hasName("ntohll") or
|
||||
getTarget().hasName("ntohs")
|
||||
}
|
||||
}
|
||||
|
||||
class NetworkToBufferSizeConfiguration extends DataFlow::Configuration {
|
||||
NetworkToBufferSizeConfiguration() { this = "NetworkToBufferSizeConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof NetworkFunctionCall }
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
node.asExpr() = any(BufferAccess ba).getAccessedLength()
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node) {
|
||||
exists(GuardCondition gc, GVN gvn |
|
||||
gc.getAChild*() = gvn.getAnExpr() and
|
||||
globalValueNumber(node.asExpr()) = gvn and
|
||||
gc.controls(node.asExpr().getBasicBlock(), _)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Data received over a network connection may be received in a different byte order than
|
||||
the byte order used by the local host, making the data difficult to process. To address this,
|
||||
data received over the wire is usually converted to host byte order by a call to a network-to-host
|
||||
byte order function, such as <code>ntohl</code>.
|
||||
</p>
|
||||
<p>
|
||||
The use of a network-to-host byte order function is therefore a good indicator that the returned
|
||||
value is unvalidated data retrieved from the network, and should not be used without further
|
||||
validation. In particular, the returned value should not be used as an array index or array length
|
||||
value without validation, which may result in a buffer overflow vulnerability.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Validate data returned by network-to-host byte order functions before use and especially before
|
||||
using the value as an array index or bound.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>In the example below, network data is retrieved and passed to <code>ntohl</code> to convert
|
||||
it to host byte order. The data is then used as an index in an array access expression. However,
|
||||
there is no validation that the data returned by <code>ntohl</code> is within the bounds of the array,
|
||||
which could lead to reading outside the bounds of the buffer.
|
||||
</p>
|
||||
<sample src="NtohlArrayBad.cpp" />
|
||||
<p>In the corrected example, the returned data is validated against the known size of the buffer,
|
||||
before being used as an array index.</p>
|
||||
<sample src="NtohlArrayGood.cpp" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
<a href="https://docs.microsoft.com/en-us/windows/desktop/api/winsock/nf-winsock-ntohl">
|
||||
ntohl - winsock reference
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://linux.die.net/man/3/ntohl">
|
||||
ntohl - Linux man page
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @id cpp/network-to-host-function-as-array-bound
|
||||
* @name Untrusted network-to-host usage
|
||||
* @description Using the result of a network-to-host byte order function, such as ntohl, as an
|
||||
* array bound or length value without checking it may result in buffer overflows or
|
||||
* other vulnerabilties.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import NtohlArrayNoBound
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
|
||||
from NetworkToBufferSizeConfiguration bufConfig, DataFlow::Node source, DataFlow::Node sink
|
||||
where bufConfig.hasFlow(source, sink)
|
||||
select sink, "Unchecked use of data from network function $@", source, source.toString()
|
||||
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using TLS or SSLv23 protool from the boost::asio library, but not disabling deprecated protocols or disabling minimum-recommended protocols.</p>
|
||||
</overview>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
<a href="https://www.boost.org/doc/libs/1_71_0/doc/html/boost_asio.html">Boost.Asio documentation</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* @name Boost_asio TLS Settings Misconfiguration
|
||||
* @description Using TLS or SSLv23 protool from the boost::asio library, but not disabling deprecated protocols or disabling minimum-recommended protocols
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @id cpp/boost/tls_settings_misconfiguration
|
||||
* @tags security
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.boostorg.asio.protocols
|
||||
|
||||
class ExistsAnyFlowConfig extends DataFlow::Configuration {
|
||||
ExistsAnyFlowConfig() { this = "ExistsAnyFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { any() }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { any() }
|
||||
}
|
||||
|
||||
bindingset[flag]
|
||||
predicate isOptionSet(ConstructorCall cc, int flag, FunctionCall fcSetOptions) {
|
||||
exists(
|
||||
BoostorgAsio::SslContextFlowsToSetOptionConfig config, ExistsAnyFlowConfig testConfig,
|
||||
Expr optionsSink
|
||||
|
|
||||
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
|
||||
exists(VariableAccess contextSetOptions |
|
||||
testConfig.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(contextSetOptions)) and
|
||||
exists(BoostorgAsio::SslSetOptionsFunction f | f.getACallToThisFunction() = fcSetOptions |
|
||||
contextSetOptions = fcSetOptions.getQualifier() and
|
||||
forall(
|
||||
Expr optionArgument, BoostorgAsio::SslOptionConfig optionArgConfig,
|
||||
Expr optionArgumentSource
|
||||
|
|
||||
optionArgument = fcSetOptions.getArgument(0) and
|
||||
optionArgConfig
|
||||
.hasFlow(DataFlow::exprNode(optionArgumentSource), DataFlow::exprNode(optionArgument))
|
||||
|
|
||||
optionArgument.getValue().toInt().bitShiftRight(16).bitAnd(flag) = flag
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[flag]
|
||||
predicate isOptionNotSet(ConstructorCall cc, int flag) {
|
||||
not exists(
|
||||
BoostorgAsio::SslContextFlowsToSetOptionConfig config, ExistsAnyFlowConfig testConfig,
|
||||
Expr optionsSink
|
||||
|
|
||||
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
|
||||
exists(VariableAccess contextSetOptions |
|
||||
testConfig.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(contextSetOptions)) and
|
||||
exists(FunctionCall fcSetOptions, BoostorgAsio::SslSetOptionsFunction f |
|
||||
f.getACallToThisFunction() = fcSetOptions
|
||||
|
|
||||
contextSetOptions = fcSetOptions.getQualifier() and
|
||||
forall(
|
||||
Expr optionArgument, BoostorgAsio::SslOptionConfig optionArgConfig,
|
||||
Expr optionArgumentSource
|
||||
|
|
||||
optionArgument = fcSetOptions.getArgument(0) and
|
||||
optionArgConfig
|
||||
.hasFlow(DataFlow::exprNode(optionArgumentSource), DataFlow::exprNode(optionArgument))
|
||||
|
|
||||
optionArgument.getValue().toInt().bitShiftRight(16).bitAnd(flag) = flag
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
from
|
||||
BoostorgAsio::SslContextCallTlsProtocolConfig configConstructor,
|
||||
BoostorgAsio::SslContextFlowsToSetOptionConfig config, Expr protocolSource, Expr protocolSink,
|
||||
ConstructorCall cc, Expr e, string msg
|
||||
where
|
||||
configConstructor.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink)) and
|
||||
cc.getArgument(0) = protocolSink and
|
||||
(
|
||||
BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
|
||||
not exists(Expr optionsSink |
|
||||
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoSsl3(), _) and
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1(), _) and
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1(), _) and
|
||||
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2())
|
||||
)
|
||||
or
|
||||
BoostorgAsio::isExprTlsBoostProtocol(protocolSource) and
|
||||
not BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
|
||||
not exists(Expr optionsSink |
|
||||
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1(), _) and
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1(), _) and
|
||||
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2())
|
||||
)
|
||||
) and
|
||||
(
|
||||
BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
|
||||
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoSsl3()) and
|
||||
e = cc and
|
||||
msg = "no_sslv3 has not been set"
|
||||
or
|
||||
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1()) and
|
||||
e = cc and
|
||||
msg = "no_tlsv1 has not been set"
|
||||
or
|
||||
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1()) and
|
||||
e = cc and
|
||||
msg = "no_tlsv1_1 has not been set"
|
||||
or
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2(), e) and
|
||||
msg = "no_tlsv1_2 was set"
|
||||
)
|
||||
select cc, "Usage of $@ with protocol $@ is not configured correctly: The option $@.", cc,
|
||||
"boost::asio::ssl::context::context", protocolSource, protocolSource.toString(), e, msg
|
||||
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using boost::asio library but specifying a deprecated hardcoded protocol.</p>
|
||||
<p>Using a deprecated hardcoded protocol instead of negotiting would lock your application to a protocol that has known vulnerabilities or weaknesses.</p>
|
||||
</overview>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
<a href="https://www.boost.org/doc/libs/1_71_0/doc/html/boost_asio.html">Boost.Asio documentation</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @name boost::asio Use of deprecated hardcoded Protocol
|
||||
* @description Using a deprecated hard-coded protocol using the boost::asio library.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @id cpp/boost/use-of-deprecated-hardcoded-security-protocol
|
||||
* @tags security
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.boostorg.asio.protocols
|
||||
|
||||
from
|
||||
BoostorgAsio::SslContextCallConfig config, Expr protocolSource, Expr protocolSink,
|
||||
ConstructorCall cc
|
||||
where
|
||||
config.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink)) and
|
||||
not exists(BoostorgAsio::SslContextCallTlsProtocolConfig tlsConfig |
|
||||
tlsConfig.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink))
|
||||
) and
|
||||
cc.getArgument(0) = protocolSink and
|
||||
exists(BoostorgAsio::SslContextCallBannedProtocolConfig bannedConfig |
|
||||
bannedConfig.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink))
|
||||
)
|
||||
select protocolSink, "Usage of $@ specifying a deprecated hardcoded protocol $@ in function $@.",
|
||||
cc, "boost::asio::ssl::context::context", protocolSource, protocolSource.toString(),
|
||||
cc.getEnclosingFunction(), cc.getEnclosingFunction().toString()
|
||||
@@ -2,29 +2,45 @@ import cpp
|
||||
|
||||
class SALMacro extends Macro {
|
||||
SALMacro() {
|
||||
this.getFile().getBaseName() = "sal.h" or
|
||||
this.getFile().getBaseName() = "specstrings_strict.h" or
|
||||
this.getFile().getBaseName() = "specstrings.h"
|
||||
exists(string filename | filename = this.getFile().getBaseName() |
|
||||
filename = "sal.h" or
|
||||
filename = "specstrings_strict.h" or
|
||||
filename = "specstrings.h" or
|
||||
filename = "w32p.h" or
|
||||
filename = "minwindef.h"
|
||||
) and
|
||||
(
|
||||
// Dialect for Windows 8 and above
|
||||
this.getName().matches("\\_%\\_")
|
||||
or
|
||||
// Dialect for Windows 7
|
||||
this.getName().matches("\\_\\_%")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate isTopLevelMacroAccess(MacroAccess ma) { not exists(ma.getParentInvocation()) }
|
||||
|
||||
class SALAnnotation extends MacroInvocation {
|
||||
SALAnnotation() {
|
||||
this.getMacro() instanceof SALMacro and
|
||||
not exists(this.getParentInvocation())
|
||||
isTopLevelMacroAccess(this)
|
||||
}
|
||||
|
||||
/** Returns the `Declaration` annotated by `this`. */
|
||||
Declaration getDeclaration() { annotatesAt(this, result.getADeclarationEntry(), _, _) }
|
||||
Declaration getDeclaration() {
|
||||
annotatesAt(this, result.getADeclarationEntry(), _, _) and
|
||||
not result instanceof Type // exclude typedefs
|
||||
}
|
||||
|
||||
/** Returns the `DeclarationEntry` annotated by `this`. */
|
||||
DeclarationEntry getDeclarationEntry() { annotatesAt(this, result, _, _) }
|
||||
DeclarationEntry getDeclarationEntry() {
|
||||
annotatesAt(this, result, _, _) and
|
||||
not result instanceof TypeDeclarationEntry // exclude typedefs
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Particular SAL annotations of interest
|
||||
*/
|
||||
|
||||
class SALCheckReturn extends SALAnnotation {
|
||||
SALCheckReturn() {
|
||||
exists(SALMacro m | m = this.getMacro() |
|
||||
@@ -39,8 +55,8 @@ class SALNotNull extends SALAnnotation {
|
||||
exists(SALMacro m | m = this.getMacro() |
|
||||
not m.getName().matches("%\\_opt\\_%") and
|
||||
(
|
||||
m.getName().matches("\\_In%") or
|
||||
m.getName().matches("\\_Out%") or
|
||||
m.getName().matches("_In%") or
|
||||
m.getName().matches("_Out%") or
|
||||
m.getName() = "_Ret_notnull_"
|
||||
)
|
||||
) and
|
||||
@@ -63,42 +79,124 @@ class SALMaybeNull extends SALAnnotation {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation details
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Implementation details
|
||||
/**
|
||||
* Holds if `a` annotates the declaration entry `d` and
|
||||
* its start position is the `idx`th position in `file` that holds a SAL element.
|
||||
*/
|
||||
|
||||
private predicate annotatesAt(SALAnnotation a, DeclarationEntry e, File file, int idx) {
|
||||
a = salElementAt(file, idx) and
|
||||
(
|
||||
// Base case: `a` right before `e`
|
||||
e = salElementAt(file, idx + 1)
|
||||
or
|
||||
// Recursive case: `a` right before some annotation on `e`
|
||||
annotatesAt(_, e, file, idx + 1)
|
||||
)
|
||||
}
|
||||
|
||||
library class SALElement extends Element {
|
||||
SALElement() {
|
||||
this instanceof DeclarationEntry or
|
||||
this instanceof SALAnnotation
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the `idx`th `SALElement` in `file`. */
|
||||
private SALElement salElementAt(File file, int idx) {
|
||||
interestingLoc(file, result, interestingStartPos(file, idx))
|
||||
predicate annotatesAt(SALAnnotation a, DeclarationEntry d, File file, int idx) {
|
||||
annotatesAtPosition(a.(SALElement).getStartPosition(), d, file, idx)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an SALElement element at character `result` comes at
|
||||
* position `idx` in `file`.
|
||||
* Holds if `pos` is the `idx`th position in `file` that holds a SAL element,
|
||||
* which annotates the declaration entry `d` (by occurring before it without
|
||||
* any other declaration entries in between).
|
||||
*/
|
||||
private int interestingStartPos(File file, int idx) {
|
||||
result = rank[idx](int otherStart | interestingLoc(file, _, otherStart))
|
||||
// For performance reasons, do not mention the annotation itself here,
|
||||
// but compute with positions instead. This performs better on databases
|
||||
// with many annotations at the same position.
|
||||
private predicate annotatesAtPosition(SALPosition pos, DeclarationEntry d, File file, int idx) {
|
||||
pos = salRelevantPositionAt(file, idx) and
|
||||
salAnnotationPos(pos) and
|
||||
(
|
||||
// Base case: `pos` right before `d`
|
||||
d.(SALElement).getStartPosition() = salRelevantPositionAt(file, idx + 1)
|
||||
or
|
||||
// Recursive case: `pos` right before some annotation on `d`
|
||||
annotatesAtPosition(_, d, file, idx + 1)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `element` in `file` is at character `startPos`. */
|
||||
private predicate interestingLoc(File file, SALElement element, int startPos) {
|
||||
element.getLocation().charLoc(file, startPos, _)
|
||||
/**
|
||||
* A parameter annotated by one or more SAL annotations.
|
||||
*/
|
||||
class SALParameter extends Parameter {
|
||||
/** One of this parameter's annotations. */
|
||||
SALAnnotation a;
|
||||
|
||||
SALParameter() { annotatesAt(a, this.getADeclarationEntry(), _, _) }
|
||||
|
||||
predicate isIn() { a.getMacroName().toLowerCase().matches("%\\_in%") }
|
||||
|
||||
predicate isOut() { a.getMacroName().toLowerCase().matches("%\\_out%") }
|
||||
|
||||
predicate isInOut() { a.getMacroName().toLowerCase().matches("%\\_inout%") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A SAL element, i.e. a SAL annotation or a declaration entry
|
||||
* that may have SAL annotations.
|
||||
*/
|
||||
library class SALElement extends Element {
|
||||
SALElement() {
|
||||
containsSALAnnotation(this.(DeclarationEntry).getFile()) or
|
||||
this instanceof SALAnnotation
|
||||
}
|
||||
|
||||
predicate hasStartPosition(File file, int line, int col) {
|
||||
exists(Location loc | loc = this.getLocation() |
|
||||
file = loc.getFile() and
|
||||
line = loc.getStartLine() and
|
||||
col = loc.getStartColumn()
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasEndPosition(File file, int line, int col) {
|
||||
exists(Location loc |
|
||||
loc = this.(FunctionDeclarationEntry).getBlock().getLocation()
|
||||
or
|
||||
this = any(VariableDeclarationEntry vde |
|
||||
vde.isDefinition() and
|
||||
loc = vde.getVariable().getInitializer().getLocation()
|
||||
)
|
||||
|
|
||||
file = loc.getFile() and
|
||||
line = loc.getEndLine() and
|
||||
col = loc.getEndColumn()
|
||||
)
|
||||
}
|
||||
|
||||
SALPosition getStartPosition() {
|
||||
exists(File file, int line, int col |
|
||||
this.hasStartPosition(file, line, col) and
|
||||
result = MkSALPosition(file, line, col)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `file` contains a SAL annotation. */
|
||||
pragma[noinline]
|
||||
private predicate containsSALAnnotation(File file) { any(SALAnnotation a).getFile() = file }
|
||||
|
||||
/**
|
||||
* A source-file position of a `SALElement`. Unlike location, this denotes a
|
||||
* point in the file rather than a range.
|
||||
*/
|
||||
private newtype SALPosition =
|
||||
MkSALPosition(File file, int line, int col) {
|
||||
exists(SALElement e |
|
||||
e.hasStartPosition(file, line, col)
|
||||
or
|
||||
e.hasEndPosition(file, line, col)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `pos` is the start position of a SAL annotation. */
|
||||
pragma[noinline]
|
||||
private predicate salAnnotationPos(SALPosition pos) {
|
||||
any(SALAnnotation a).(SALElement).getStartPosition() = pos
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `idx`th position in `file` that holds a SAL element,
|
||||
* ordering positions lexicographically by their start line and start column.
|
||||
*/
|
||||
private SALPosition salRelevantPositionAt(File file, int idx) {
|
||||
result = rank[idx](SALPosition pos, int line, int col |
|
||||
pos = MkSALPosition(file, line, col)
|
||||
|
|
||||
pos order by line, col
|
||||
)
|
||||
}
|
||||
|
||||
25
cpp/ql/src/Microsoft/SAL/IgnoreReturnValueSAL.ql
Normal file
25
cpp/ql/src/Microsoft/SAL/IgnoreReturnValueSAL.ql
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @name SAL requires inspecting return value
|
||||
* @description When a return value is discarded even though the SAL annotation
|
||||
* requires inspecting it, a recoverable error may turn into a
|
||||
* whole-program crash.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @tags reliability
|
||||
* external/cwe/cwe-573
|
||||
* external/cwe/cwe-252
|
||||
* @opaque-id SM02344
|
||||
* @microsoft.severity Important
|
||||
* @id cpp/ignorereturnvaluesal
|
||||
*/
|
||||
|
||||
import Microsoft.SAL
|
||||
|
||||
from Function f, FunctionCall call
|
||||
where
|
||||
call.getTarget() = f and
|
||||
call instanceof ExprInVoidContext and
|
||||
any(SALCheckReturn a).getDeclaration() = f and
|
||||
not any(Options o).okToIgnoreReturnValue(call)
|
||||
select call, "Return value of $@ discarded although a SAL annotation " + "requires inspecting it.",
|
||||
f, f.getName()
|
||||
@@ -0,0 +1,59 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>A common pattern is to initialize a local variable by calling another function (an
|
||||
"initialization" function) with the address of the local variable as a pointer argument. That
|
||||
function is then responsible for writing to the memory location referenced by the pointer.</p>
|
||||
|
||||
<p>In some cases, the called function may not always write to the memory pointed to by the
|
||||
pointer argument. In such cases, the function will typically return a "status" code, informing the
|
||||
caller as to whether the initialization succeeded or not. If the caller does not check the status
|
||||
code before reading the local variable, it may read unitialized memory, which can result in
|
||||
unexpected behavior.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>When using a initialization function that does not guarantee to initialize the memory pointed to
|
||||
by the passed pointer, and returns a status code to indicate whether such initialization occurred,
|
||||
the status code should be checked before reading from the local variable.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>In this hypothetical example we have code for managing a series of devices. The code
|
||||
includes a <code>DeviceConfig</code> struct that can represent properties about each device.
|
||||
The <code>initDeviceConfig</code> function can be called to initialize one of these structures, by
|
||||
providing a "device number", which can be used to look up the appropriate properties in some data
|
||||
store. If an invalid device number is provided, the function returns a status code of
|
||||
<code>-1</code>, and does not initialize the provided pointer.</p>
|
||||
|
||||
<p>In the first code sample below, the <code>notify</code> function calls the
|
||||
<code>initDeviceConfig</code> function with a pointer to the local variable <code>config</code>,
|
||||
which is then subsequently accessed to fetch properties of the device. However, the code does not
|
||||
check the return value from the function call to <code>initDeviceConfig</code>. If the
|
||||
device number passed to the <code>notify</code> function was invalid, the
|
||||
<code>initDeviceConfig</code> function will leave the <code>config</code> variable uninitialized,
|
||||
which would result in the <code>notify</code> function accessing uninitialized memory.</p>
|
||||
|
||||
<sample src="ConditionallyUninitializedVariableBad.c" />
|
||||
|
||||
<p>To fix this, the code needs to check that the return value of the call to
|
||||
<code>initDeviceConfig</code> is zero. If that is true, then the calling code can safely assume
|
||||
that the local variable has been initialized.</p>
|
||||
|
||||
<sample src="ConditionallyUninitializedVariableGood.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
Wikipedia:
|
||||
<a href="https://en.wikipedia.org/wiki/Uninitialized_variable">Uninitialized variable</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @name Conditionally uninitialized variable
|
||||
* @description When an initialization function is used to initialize a local variable, but the
|
||||
* returned status code is not checked, the variable may be left in an uninitialized
|
||||
* state, and reading the variable may result in undefined behaviour.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @opaque-id SM02313
|
||||
* @id cpp/conditionally-uninitialized-variable
|
||||
* @tags security
|
||||
* external/cwe/cwe-457
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.SSA
|
||||
private import UninitializedVariables
|
||||
|
||||
from
|
||||
ConditionallyInitializedVariable v, ConditionalInitializationFunction f,
|
||||
ConditionalInitializationCall call, string defined, Evidence e
|
||||
where
|
||||
exists(v.getARiskyAccess(f, call, e)) and
|
||||
(
|
||||
if e = DefinitionInSnapshot()
|
||||
then defined = ""
|
||||
else
|
||||
if e = SuggestiveSALAnnotation()
|
||||
then defined = "externally defined (SAL) "
|
||||
else defined = "externally defined (CSV) "
|
||||
)
|
||||
select call,
|
||||
"The status of this call to " + defined +
|
||||
"$@ is not checked, potentially leaving $@ uninitialized.", f, f.getName(), v, v.getName()
|
||||
@@ -0,0 +1,25 @@
|
||||
struct DeviceConfig {
|
||||
bool isEnabled;
|
||||
int channel;
|
||||
};
|
||||
|
||||
int initDeviceConfig(DeviceConfig *ref, int deviceNumber) {
|
||||
if (deviceNumber >= getMaxDevices()) {
|
||||
// No device with that number, return -1 to indicate failure
|
||||
return -1;
|
||||
}
|
||||
// Device with that number, fetch parameters and initialize struct
|
||||
ref->isEnabled = fetchIsDeviceEnabled(deviceNumber);
|
||||
ref->channel = fetchDeviceChannel(deviceNumber);
|
||||
// Return 0 to indicate success
|
||||
return 0;
|
||||
}
|
||||
|
||||
int notify(int deviceNumber) {
|
||||
DeviceConfig config;
|
||||
initDeviceConfig(&config, deviceNumber);
|
||||
// BAD: Using config without checking the status code that is returned
|
||||
if (config->isEnabled) {
|
||||
notifyChannel(config->channel);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
struct DeviceConfig {
|
||||
bool isEnabled;
|
||||
int channel;
|
||||
};
|
||||
|
||||
int initDeviceConfig(DeviceConfig *ref, int deviceNumber) {
|
||||
if (deviceNumber >= getMaxDevices()) {
|
||||
// No device with that number, return -1 to indicate failure
|
||||
return -1;
|
||||
}
|
||||
// Device with that number, fetch parameters and initialize struct
|
||||
ref->isEnabled = fetchIsDeviceEnabled(deviceNumber);
|
||||
ref->channel = fetchDeviceChannel(deviceNumber);
|
||||
// Return 0 to indicate success
|
||||
return 0;
|
||||
}
|
||||
|
||||
void notify(int deviceNumber) {
|
||||
DeviceConfig config;
|
||||
int statusCode = initDeviceConfig(&config, deviceNumber);
|
||||
if (statusCode == 0) {
|
||||
// GOOD: Status code returned by initialization function is checked, so this is safe
|
||||
if (config->isEnabled) {
|
||||
notifyChannel(config->channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
690
cpp/ql/src/Security/CWE/CWE-457/InitializationFunctions.qll
Normal file
690
cpp/ql/src/Security/CWE/CWE-457/InitializationFunctions.qll
Normal file
@@ -0,0 +1,690 @@
|
||||
/**
|
||||
* Provides classes and predicates for identifying functions that initialize their arguments.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import external.ExternalArtifact
|
||||
private import semmle.code.cpp.dispatch.VirtualDispatch
|
||||
import semmle.code.cpp.NestedFields
|
||||
import Microsoft.SAL
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
/** A context under which a function may be called. */
|
||||
private newtype TContext =
|
||||
/** No specific call context. */
|
||||
NoContext() or
|
||||
/**
|
||||
* The call context is that the given other parameter is null.
|
||||
*
|
||||
* This context is created for all parameters that are null checked in the body of the function.
|
||||
*/
|
||||
ParamNull(Parameter p) { p = any(ParameterNullCheck pnc).getParameter() } or
|
||||
/**
|
||||
* The call context is that the given other parameter is not null.
|
||||
*
|
||||
* This context is created for all parameters that are null checked in the body of the function.
|
||||
*/
|
||||
ParamNotNull(Parameter p) { p = any(ParameterNullCheck pnc).getParameter() }
|
||||
|
||||
/**
|
||||
* A context under which a function may be called.
|
||||
*
|
||||
* Some functions may conditionally initialize a parameter depending on the value of another
|
||||
* parameter. Consider:
|
||||
* ```
|
||||
* int MyInitFunction(int* paramToBeInitialized, int* paramToCheck) {
|
||||
* if (!paramToCheck) {
|
||||
* // fail!
|
||||
* return -1;
|
||||
* }
|
||||
* paramToBeInitialized = 0;
|
||||
* }
|
||||
* ```
|
||||
* In this case, whether `paramToBeInitialized` is initialized when this function call completes
|
||||
* depends on whether `paramToCheck` is or is not null. A call-context insensitive analysis will
|
||||
* determine that any call to this function may leave the parameter uninitialized, even if the
|
||||
* argument to paramToCheck is guaranteed to be non-null (`&foo`, for example).
|
||||
*
|
||||
* This class models call contexts that can be considered when calculating whether a given parameter
|
||||
* initializes or not. The supported contexts are:
|
||||
* - `ParamNull(otherParam)` - the given `otherParam` is considered to be null. Applies when
|
||||
* exactly one parameter other than this one is null checked.
|
||||
* - `ParamNotNull(otherParam)` - the given `otherParam` is considered to be not null. Applies when
|
||||
* exactly one parameter other than this one is null checked.
|
||||
* - `NoContext()` - applies in all other circumstances.
|
||||
*/
|
||||
class Context extends TContext {
|
||||
string toString() {
|
||||
this = NoContext() and result = "NoContext"
|
||||
or
|
||||
this = ParamNull(any(Parameter p | result = "ParamNull(" + p.getName() + ")"))
|
||||
or
|
||||
this = ParamNotNull(any(Parameter p | result = "ParamNotNull(" + p.getName() + ")"))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A check against a parameter.
|
||||
*/
|
||||
abstract class ParameterCheck extends Expr {
|
||||
/**
|
||||
* Gets a successor of this check that should be ignored for the given context.
|
||||
*/
|
||||
abstract ControlFlowNode getIgnoredSuccessorForContext(Context c);
|
||||
}
|
||||
|
||||
/** A null-check expression on a parameter. */
|
||||
class ParameterNullCheck extends ParameterCheck {
|
||||
Parameter p;
|
||||
ControlFlowNode nullSuccessor;
|
||||
ControlFlowNode notNullSuccessor;
|
||||
|
||||
ParameterNullCheck() {
|
||||
this.isCondition() and
|
||||
p.getFunction() instanceof InitializationFunction and
|
||||
p.getType().getUnspecifiedType() instanceof PointerType and
|
||||
exists(VariableAccess va | va = p.getAnAccess() |
|
||||
nullSuccessor = getATrueSuccessor() and
|
||||
notNullSuccessor = getAFalseSuccessor() and
|
||||
(
|
||||
va = this.(NotExpr).getOperand() or
|
||||
va = any(EQExpr eq | eq = this and eq.getAnOperand().getValue() = "0").getAnOperand() or
|
||||
va = getAssertedFalseCondition(this) or
|
||||
va = any(NEExpr eq |
|
||||
eq = getAssertedFalseCondition(this) and eq.getAnOperand().getValue() = "0"
|
||||
).getAnOperand()
|
||||
)
|
||||
or
|
||||
nullSuccessor = getAFalseSuccessor() and
|
||||
notNullSuccessor = getATrueSuccessor() and
|
||||
(
|
||||
va = this or
|
||||
va = any(NEExpr eq | eq = this and eq.getAnOperand().getValue() = "0").getAnOperand() or
|
||||
va = any(EQExpr eq |
|
||||
eq = getAssertedFalseCondition(this) and eq.getAnOperand().getValue() = "0"
|
||||
).getAnOperand()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** The parameter being null-checked. */
|
||||
Parameter getParameter() { result = p }
|
||||
|
||||
override ControlFlowNode getIgnoredSuccessorForContext(Context c) {
|
||||
c = ParamNull(p) and result = notNullSuccessor
|
||||
or
|
||||
c = ParamNotNull(p) and result = nullSuccessor
|
||||
}
|
||||
|
||||
/** The successor at which the parameter is confirmed to be null. */
|
||||
ControlFlowNode getNullSuccessor() { result = nullSuccessor }
|
||||
|
||||
/** The successor at which the parameter is confirmed to be not-null. */
|
||||
ControlFlowNode getNotNullSuccessor() { result = notNullSuccessor }
|
||||
}
|
||||
|
||||
/**
|
||||
* An entry in a CSV file in cond-init that contains externally defined functions that are
|
||||
* conditional initializers. These files are typically produced by running the
|
||||
* ConditionallyInitializedFunction companion query.
|
||||
*/
|
||||
class ValidatedExternalCondInitFunction extends ExternalData {
|
||||
ValidatedExternalCondInitFunction() { this.getDataPath().matches("%cond-init%.csv") }
|
||||
|
||||
predicate isExternallyVerified(Function f, int param) {
|
||||
functionSignature(f, getField(1), getField(2)) and param = getFieldAsInt(3)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of evidence used to determine whether a function initializes a parameter.
|
||||
*/
|
||||
newtype Evidence =
|
||||
/**
|
||||
* The function is defined in the snapshot, and the CFG has been analyzed to determine that the
|
||||
* parameter is not initialized on at least one path to the exit.
|
||||
*/
|
||||
DefinitionInSnapshot() or
|
||||
/**
|
||||
* The function is externally defined, but the parameter has an `_out` SAL annotation which
|
||||
* suggests that it is initialized in the function.
|
||||
*/
|
||||
SuggestiveSALAnnotation() or
|
||||
/**
|
||||
* We have been given a CSV file which indicates this parameter is conditionally initialized.
|
||||
*/
|
||||
ExternalEvidence()
|
||||
|
||||
/**
|
||||
* A call to an function which initializes one or more of its parameters.
|
||||
*/
|
||||
class InitializationFunctionCall extends FunctionCall {
|
||||
Expr initializedArgument;
|
||||
|
||||
InitializationFunctionCall() { initializedArgument = getAnInitializedArgument(this) }
|
||||
|
||||
/** Gets a parameter that is initialized by this call. */
|
||||
Parameter getAnInitParameter() { result.getAnAccess() = initializedArgument }
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable access which is dereferenced then assigned to.
|
||||
*/
|
||||
private predicate isPointerDereferenceAssignmentTarget(VariableAccess target) {
|
||||
target.getParent().(PointerDereferenceExpr) = any(Assignment e).getLValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* A function which initializes one or more of its parameters.
|
||||
*/
|
||||
class InitializationFunction extends Function {
|
||||
int i;
|
||||
Evidence evidence;
|
||||
|
||||
InitializationFunction() {
|
||||
evidence = DefinitionInSnapshot() and
|
||||
(
|
||||
// Assignment by pointer dereferencing the parameter
|
||||
isPointerDereferenceAssignmentTarget(this.getParameter(i).getAnAccess()) or
|
||||
// Field wise assignment to the parameter
|
||||
any(Assignment e).getLValue() = getAFieldAccess(this.getParameter(i)) or
|
||||
i = this
|
||||
.(MemberFunction)
|
||||
.getAnOverridingFunction+()
|
||||
.(InitializationFunction)
|
||||
.initializedParameter() or
|
||||
getParameter(i) = any(InitializationFunctionCall c).getAnInitParameter()
|
||||
)
|
||||
or
|
||||
// If we have no definition, we look at SAL annotations
|
||||
not this.isDefined() and
|
||||
this.getParameter(i).(SALParameter).isOut() and
|
||||
evidence = SuggestiveSALAnnotation()
|
||||
or
|
||||
// We have some external information that this function conditionally initializes
|
||||
not this.isDefined() and
|
||||
any(ValidatedExternalCondInitFunction vc).isExternallyVerified(this, i) and
|
||||
evidence = ExternalEvidence()
|
||||
}
|
||||
|
||||
/** Gets a parameter index which is initialized by this function. */
|
||||
int initializedParameter() { result = i }
|
||||
|
||||
/** Gets a `ControlFlowNode` which assigns a new value to the parameter with the given index. */
|
||||
ControlFlowNode paramReassignment(int index) {
|
||||
index = i and
|
||||
(
|
||||
result = this.getParameter(i).getAnAccess() and
|
||||
(
|
||||
result = any(Assignment a).getLValue().(PointerDereferenceExpr).getOperand()
|
||||
or
|
||||
// Field wise assignment to the parameter
|
||||
result = any(Assignment a).getLValue().(FieldAccess).getQualifier()
|
||||
or
|
||||
// Assignment to a nested field of the parameter
|
||||
result = any(Assignment a).getLValue().(NestedFieldAccess).getUltimateQualifier()
|
||||
or
|
||||
result = getAnInitializedArgument(any(Call c))
|
||||
or
|
||||
exists(IfStmt check | result = check.getCondition().getAChild*() |
|
||||
paramReassignmentCondition(check)
|
||||
)
|
||||
)
|
||||
or
|
||||
result = any(AssumeExpr e |
|
||||
e.getEnclosingFunction() = this and e.getAChild().(Literal).getValue() = "0"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper predicate: holds if the `if` statement `check` contains a
|
||||
* reassignment to the `i`th parameter within its `then` statement.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate paramReassignmentCondition(IfStmt check) {
|
||||
this.paramReassignment(i).getEnclosingStmt().getParentStmt*() = check.getThen()
|
||||
}
|
||||
|
||||
/** Holds if `n` can be reached without the parameter at `index` being reassigned. */
|
||||
predicate paramNotReassignedAt(ControlFlowNode n, int index, Context c) {
|
||||
c = getAContext(index) and
|
||||
(
|
||||
not exists(this.getEntryPoint()) and index = i and n = this
|
||||
or
|
||||
n = this.getEntryPoint() and index = i
|
||||
or
|
||||
exists(ControlFlowNode mid | paramNotReassignedAt(mid, index, c) |
|
||||
n = mid.getASuccessor() and
|
||||
not n = paramReassignment(index) and
|
||||
/*
|
||||
* Ignore successor edges where the parameter is null, because it is then confirmed to be
|
||||
* initialized.
|
||||
*/
|
||||
|
||||
not exists(ParameterNullCheck nullCheck |
|
||||
nullCheck = mid and
|
||||
nullCheck = getANullCheck(index) and
|
||||
n = nullCheck.getNullSuccessor()
|
||||
) and
|
||||
/*
|
||||
* Ignore successor edges which are excluded by the given context
|
||||
*/
|
||||
|
||||
not exists(ParameterCheck paramCheck | paramCheck = mid |
|
||||
n = paramCheck.getIgnoredSuccessorForContext(c)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a null-check on the parameter at `index`. */
|
||||
private ParameterNullCheck getANullCheck(int index) {
|
||||
getParameter(index) = result.getParameter()
|
||||
}
|
||||
|
||||
/** Gets a parameter which is not at the given index. */
|
||||
private Parameter getOtherParameter(int index) {
|
||||
index = i and
|
||||
result = getAParameter() and
|
||||
not result.getIndex() = index
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a call `Context` that is applicable when considering whether parameter at the `index` can
|
||||
* be conditionally initialized.
|
||||
*/
|
||||
Context getAContext(int index) {
|
||||
index = i and
|
||||
/*
|
||||
* If there is one and only one other parameter which is null checked in the body of the method,
|
||||
* then we have two contexts to consider - that the other param is null, or that the other param
|
||||
* is not null.
|
||||
*/
|
||||
|
||||
if
|
||||
strictcount(Parameter p |
|
||||
exists(Context c | c = ParamNull(p) or c = ParamNotNull(p)) and
|
||||
p = getOtherParameter(index)
|
||||
) = 1
|
||||
then
|
||||
exists(Parameter p | p = getOtherParameter(index) |
|
||||
result = ParamNull(p) or result = ParamNotNull(p)
|
||||
)
|
||||
else
|
||||
// Otherwise, only consider NoContext.
|
||||
result = NoContext()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this function should be whitelisted - that is, not considered as conditionally
|
||||
* initializing its parameters.
|
||||
*/
|
||||
predicate whitelisted() {
|
||||
exists(string name | this.hasName(name) |
|
||||
// Return value is not a success code but the output functions never fail.
|
||||
name.matches("_Interlocked%")
|
||||
or
|
||||
// Functions that never fail, according to MSDN.
|
||||
name = "QueryPerformanceCounter"
|
||||
or
|
||||
name = "QueryPerformanceFrequency"
|
||||
or
|
||||
// Functions that never fail post-Vista, according to MSDN.
|
||||
name = "InitializeCriticalSectionAndSpinCount"
|
||||
or
|
||||
// `rand_s` writes 0 to a non-null argument if it fails, according to MSDN.
|
||||
name = "rand_s"
|
||||
or
|
||||
// IntersectRect initializes the argument regardless of whether the input intersects
|
||||
name = "IntersectRect"
|
||||
or
|
||||
name = "SetRect"
|
||||
or
|
||||
name = "UnionRect"
|
||||
or
|
||||
// These functions appears to have an incorrect CFG, which leads to false positives
|
||||
name = "PhysicalToLogicalDPIPoint"
|
||||
or
|
||||
name = "LogicalToPhysicalDPIPoint"
|
||||
or
|
||||
// Sets NtProductType to default on error
|
||||
name = "RtlGetNtProductType"
|
||||
or
|
||||
// Our CFG is not sophisticated enough to detect that the argument is always initialized
|
||||
name = "StringCchLengthA"
|
||||
or
|
||||
// All paths init the argument, and always returns SUCCESS.
|
||||
name = "RtlUnicodeToMultiByteSize"
|
||||
or
|
||||
// All paths init the argument, and always returns SUCCESS.
|
||||
name = "RtlMultiByteToUnicodeSize"
|
||||
or
|
||||
// All paths init the argument, and always returns SUCCESS.
|
||||
name = "RtlUnicodeToMultiByteN"
|
||||
or
|
||||
// Always initializes argument
|
||||
name = "RtlGetFirstRange"
|
||||
or
|
||||
// Destination range is zeroed out on failure, assuming first two parameters are valid
|
||||
name = "memcpy_s"
|
||||
or
|
||||
// This zeroes the memory unconditionally
|
||||
name = "SeCreateAccessState"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function which initializes one or more of its parameters, but not on all paths.
|
||||
*/
|
||||
class ConditionalInitializationFunction extends InitializationFunction {
|
||||
Context c;
|
||||
|
||||
ConditionalInitializationFunction() {
|
||||
c = this.getAContext(i) and
|
||||
not this.whitelisted() and
|
||||
exists(Type status | status = this.getType().getUnspecifiedType() |
|
||||
status instanceof IntegralType or
|
||||
status instanceof Enum
|
||||
) and
|
||||
not this.getType().getName().toLowerCase() = "size_t" and
|
||||
(
|
||||
/*
|
||||
* If there is no definition, consider this to be conditionally initializing (based on either
|
||||
* SAL or external data).
|
||||
*/
|
||||
|
||||
not evidence = DefinitionInSnapshot()
|
||||
or
|
||||
/*
|
||||
* If this function is defined in this snapshot, then it conditionally initializes if there
|
||||
* is at least one path through the function which doesn't initialize the parameter.
|
||||
*
|
||||
* Explicitly ignore pure virtual functions.
|
||||
*/
|
||||
|
||||
this.isDefined() and
|
||||
this.paramNotReassignedAt(this, i, c) and
|
||||
not this instanceof PureVirtualFunction
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the evidence associated with the given parameter. */
|
||||
Evidence getEvidence(int param) {
|
||||
/*
|
||||
* Note: due to the way the predicate dispatch interacts with fields, this needs to be
|
||||
* implemented on this class, not `InitializationFunction`. If implemented on the latter it
|
||||
* can return evidence that does not result in conditional initialization.
|
||||
*/
|
||||
|
||||
param = i and evidence = result
|
||||
}
|
||||
|
||||
/** Gets the index of a parameter which is conditionally initialized. */
|
||||
int conditionallyInitializedParameter(Context context) { result = i and context = c }
|
||||
}
|
||||
|
||||
/**
|
||||
* More elaborate tracking, flagging cases where the status is checked after
|
||||
* the potentially uninitialized variable has been used, and ignoring cases
|
||||
* where the status is not checked but there is no use of the potentially
|
||||
* uninitialized variable, may be obtained via `getARiskyAccess`.
|
||||
*/
|
||||
class ConditionalInitializationCall extends FunctionCall {
|
||||
ConditionalInitializationFunction target;
|
||||
|
||||
ConditionalInitializationCall() { target = getTarget(this) }
|
||||
|
||||
/** Gets the argument passed for the given parameter to this call. */
|
||||
Expr getArgumentForParameter(Parameter p) {
|
||||
p = getTarget().getAParameter() and
|
||||
result = getArgument(p.getIndex())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an argument conditionally initialized by this call.
|
||||
*/
|
||||
Expr getAConditionallyInitializedArgument(ConditionalInitializationFunction condTarget, Evidence e) {
|
||||
condTarget = target and
|
||||
exists(Context context |
|
||||
result = getAConditionallyInitializedArgument(this, condTarget, context, e)
|
||||
|
|
||||
context = NoContext()
|
||||
or
|
||||
exists(Parameter otherP, Expr otherArg |
|
||||
context = ParamNotNull(otherP) or
|
||||
context = ParamNull(otherP)
|
||||
|
|
||||
otherArg = getArgumentForParameter(otherP) and
|
||||
(otherArg instanceof AddressOfExpr implies context = ParamNotNull(otherP)) and
|
||||
(otherArg.getType() instanceof ArrayType implies context = ParamNotNull(otherP)) and
|
||||
(otherArg.getValue() = "0" implies context = ParamNull(otherP))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
VariableAccess getAConditionallyInitializedVariable() {
|
||||
not result.getTarget().getAnAssignedValue().getASuccessor+() = result and
|
||||
// Should not be assigned field-wise prior to the call.
|
||||
not exists(Assignment a, FieldAccess fa |
|
||||
fa.getQualifier() = result.getTarget().getAnAccess() and
|
||||
a.getLValue() = fa and
|
||||
fa.getASuccessor+() = result
|
||||
) and
|
||||
result = this
|
||||
.getArgument(getTarget(this)
|
||||
.(ConditionalInitializationFunction)
|
||||
.conditionallyInitializedParameter(_))
|
||||
.(AddressOfExpr)
|
||||
.getOperand()
|
||||
}
|
||||
|
||||
Variable getStatusVariable() {
|
||||
exists(AssignExpr a | a.getLValue() = result.getAnAccess() | a.getRValue() = this)
|
||||
or
|
||||
result.getInitializer().getExpr() = this
|
||||
}
|
||||
|
||||
Expr getSuccessCheck() {
|
||||
exists(this.getAFalseSuccessor()) and result = this
|
||||
or
|
||||
result = this.getParent() and
|
||||
(
|
||||
result instanceof NotExpr or
|
||||
result.(EQExpr).getAnOperand().getValue() = "0" or
|
||||
result.(GEExpr).getLesserOperand().getValue() = "0"
|
||||
)
|
||||
}
|
||||
|
||||
Expr getFailureCheck() {
|
||||
result = this.getParent() and
|
||||
(
|
||||
result instanceof NotExpr or
|
||||
result.(NEExpr).getAnOperand().getValue() = "0" or
|
||||
result.(LTExpr).getLesserOperand().getValue() = "0"
|
||||
)
|
||||
}
|
||||
|
||||
private predicate inCheckedContext() {
|
||||
exists(Call parent | this = parent.getAnArgument() |
|
||||
parent.getTarget() instanceof Operator or
|
||||
parent.getTarget().hasName("VerifyOkCatastrophic")
|
||||
)
|
||||
}
|
||||
|
||||
ControlFlowNode uncheckedReaches(LocalVariable var) {
|
||||
(
|
||||
not exists(var.getInitializer()) and
|
||||
var = this.getAConditionallyInitializedVariable().getTarget() and
|
||||
if exists(this.getFailureCheck())
|
||||
then result = this.getFailureCheck().getATrueSuccessor()
|
||||
else
|
||||
if exists(this.getSuccessCheck())
|
||||
then result = this.getSuccessCheck().getAFalseSuccessor()
|
||||
else (
|
||||
result = this.getASuccessor() and not this.inCheckedContext()
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(ControlFlowNode mid | mid = uncheckedReaches(var) |
|
||||
not mid = getStatusVariable().getAnAccess() and
|
||||
not mid = var.getAnAccess() and
|
||||
not exists(VariableAccess write | result = write and write = var.getAnAccess() |
|
||||
write = any(AssignExpr a).getLValue() or
|
||||
write = any(AddressOfExpr a).getOperand()
|
||||
) and
|
||||
result = mid.getASuccessor()
|
||||
)
|
||||
}
|
||||
|
||||
VariableAccess getARiskyRead(Function f) {
|
||||
f = this.getTarget() and
|
||||
exists(this.getFile().getRelativePath()) and
|
||||
result = this.uncheckedReaches(result.getTarget()) and
|
||||
not this.(GuardCondition).controls(result.getBasicBlock(), _)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the position of an argument to the call which is initialized by the call.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
int initializedArgument(Call call) {
|
||||
exists(InitializationFunction target |
|
||||
target = getTarget(call) and
|
||||
result = target.initializedParameter()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an argument which is initialized by the call.
|
||||
*/
|
||||
Expr getAnInitializedArgument(Call call) { result = call.getArgument(initializedArgument(call)) }
|
||||
|
||||
/**
|
||||
* Gets the position of an argument to the call to the target which is conditionally initialized by
|
||||
* the call, under the given context and evidence.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
int conditionallyInitializedArgument(
|
||||
Call call, ConditionalInitializationFunction target, Context c, Evidence e
|
||||
) {
|
||||
target = getTarget(call) and
|
||||
c = target.getAContext(result) and
|
||||
e = target.getEvidence(result) and
|
||||
result = target.conditionallyInitializedParameter(c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an argument which is conditionally initialized by the call to the given target under the given context and evidence.
|
||||
*/
|
||||
Expr getAConditionallyInitializedArgument(
|
||||
Call call, ConditionalInitializationFunction target, Context c, Evidence e
|
||||
) {
|
||||
result = call.getArgument(conditionallyInitializedArgument(call, target, c, e))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type signature for the functions parameters.
|
||||
*/
|
||||
string typeSig(Function f) {
|
||||
result = concat(int i, Type pt |
|
||||
pt = f.getParameter(i).getType()
|
||||
|
|
||||
pt.getUnspecifiedType().toString(), "," order by i
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds where qualifiedName and typeSig make up the signature for the function.
|
||||
*/
|
||||
predicate functionSignature(Function f, string qualifiedName, string typeSig) {
|
||||
qualifiedName = f.getQualifiedName() and
|
||||
typeSig = typeSig(f)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible definition for the undefined function by matching the undefined function name
|
||||
* and parameter arity with a defined function.
|
||||
*
|
||||
* This is useful for identifying call to target dependencies across libraries, where the libraries
|
||||
* are never statically linked together.
|
||||
*/
|
||||
Function getAPossibleDefinition(Function undefinedFunction) {
|
||||
not undefinedFunction.isDefined() and
|
||||
exists(string qn, string typeSig |
|
||||
functionSignature(undefinedFunction, qn, typeSig) and functionSignature(result, qn, typeSig)
|
||||
) and
|
||||
result.isDefined()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible target for the Call, using the name and parameter matching if we did not associate
|
||||
* this call with a specific definition at link or compile time, and performing simple virtual
|
||||
* dispatch resolution.
|
||||
*/
|
||||
Function getTarget(Call c) {
|
||||
if VirtualDispatch::getAViableTarget(c).isDefined()
|
||||
then
|
||||
/*
|
||||
* If there is at least one defined target after performing some simple virtual dispatch
|
||||
* resolution, then the result is all the defined targets.
|
||||
*/
|
||||
|
||||
result = VirtualDispatch::getAViableTarget(c) and
|
||||
result.isDefined()
|
||||
else
|
||||
if exists(getAPossibleDefinition(VirtualDispatch::getAViableTarget(c)))
|
||||
then
|
||||
/*
|
||||
* If we can use the heuristic matching of functions to find definitions for some of the viable
|
||||
* targets, return those.
|
||||
*/
|
||||
|
||||
result = getAPossibleDefinition(VirtualDispatch::getAViableTarget(c))
|
||||
else
|
||||
// Otherwise, the result is the undefined `Function` instances
|
||||
result = VirtualDispatch::getAViableTarget(c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an access of a field on `Variable` v.
|
||||
*/
|
||||
FieldAccess getAFieldAccess(Variable v) {
|
||||
exists(VariableAccess va, Expr qualifierExpr |
|
||||
// Find an access of the variable, or an AddressOfExpr that has the access
|
||||
va = v.getAnAccess() and
|
||||
(
|
||||
qualifierExpr = va or
|
||||
qualifierExpr.(AddressOfExpr).getOperand() = va
|
||||
)
|
||||
|
|
||||
// Direct field access
|
||||
qualifierExpr = result.getQualifier()
|
||||
or
|
||||
// Nested field access
|
||||
qualifierExpr = result.(NestedFieldAccess).getUltimateQualifier()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a condition which is asserted to be false by the given `ne` expression, according to this pattern:
|
||||
* ```
|
||||
* int a = !!result;
|
||||
* if (!a) { // <- ne
|
||||
* ....
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
Expr getAssertedFalseCondition(NotExpr ne) {
|
||||
exists(LocalVariable v |
|
||||
result = v.getInitializer().getExpr().(NotExpr).getOperand().(NotExpr).getOperand() and
|
||||
ne.getOperand() = v.getAnAccess() and
|
||||
nonAssignedVariable(v)
|
||||
// and not passed by val?
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate nonAssignedVariable(Variable v) { not exists(v.getAnAssignment()) }
|
||||
190
cpp/ql/src/Security/CWE/CWE-457/UninitializedVariables.qll
Normal file
190
cpp/ql/src/Security/CWE/CWE-457/UninitializedVariables.qll
Normal file
@@ -0,0 +1,190 @@
|
||||
/**
|
||||
* A module for identifying conditionally initialized variables.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import InitializationFunctions
|
||||
|
||||
// Optimised reachability predicates
|
||||
private predicate reaches(ControlFlowNode a, ControlFlowNode b) = fastTC(successor/2)(a, b)
|
||||
|
||||
private predicate successor(ControlFlowNode a, ControlFlowNode b) { b = a.getASuccessor() }
|
||||
|
||||
class WhitelistedCallsConfig extends string {
|
||||
WhitelistedCallsConfig() { this = "config" }
|
||||
|
||||
abstract predicate isWhitelisted(Call c);
|
||||
}
|
||||
|
||||
abstract class WhitelistedCall extends Call {
|
||||
override Function getTarget() { none() }
|
||||
}
|
||||
|
||||
private predicate hasConditionalInitialization(
|
||||
ConditionalInitializationFunction f, ConditionalInitializationCall call, LocalVariable v,
|
||||
VariableAccess initAccess, Evidence e
|
||||
) {
|
||||
// Ignore whitelisted calls
|
||||
not call instanceof WhitelistedCall and
|
||||
f = getTarget(call) and
|
||||
initAccess = v.getAnAccess() and
|
||||
initAccess = call.getAConditionallyInitializedArgument(f, e).(AddressOfExpr).getOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable that can be conditionally initialized by a call.
|
||||
*/
|
||||
class ConditionallyInitializedVariable extends LocalVariable {
|
||||
ConditionalInitializationCall call;
|
||||
ConditionalInitializationFunction f;
|
||||
VariableAccess initAccess;
|
||||
Evidence e;
|
||||
|
||||
ConditionallyInitializedVariable() {
|
||||
// Find a call that conditionally initializes this variable
|
||||
hasConditionalInitialization(f, call, this, initAccess, e) and
|
||||
// Ignore cases where the variable is assigned prior to the call
|
||||
not reaches(getAnAssignedValue(), initAccess) and
|
||||
// Ignore cases where the variable is assigned field-wise prior to the call.
|
||||
not exists(FieldAccess fa |
|
||||
exists(Assignment a |
|
||||
fa = getAFieldAccess(this) and
|
||||
a.getLValue() = fa
|
||||
)
|
||||
|
|
||||
reaches(fa, initAccess)
|
||||
) and
|
||||
// Ignore cases where the variable is assigned by a prior call to an initialization function
|
||||
not exists(Call c |
|
||||
getAnAccess() = getAnInitializedArgument(c).(AddressOfExpr).getOperand() and
|
||||
reaches(c, initAccess)
|
||||
) and
|
||||
/*
|
||||
* Static local variables with constant initializers do not have the initializer expr as part of
|
||||
* the CFG, but should always be considered as initialized, so exclude them.
|
||||
*/
|
||||
|
||||
not exists(getInitializer().getExpr())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an access of the variable `v` which is not used as an lvalue, and not used as an argument
|
||||
* to an initialization function.
|
||||
*/
|
||||
private VariableAccess getAReadAccess() {
|
||||
result = this.getAnAccess() and
|
||||
// Not used as an lvalue
|
||||
not result = any(AssignExpr a).getLValue() and
|
||||
// Not passed to another initialization function
|
||||
not exists(Call c, int j | j = c.getTarget().(InitializationFunction).initializedParameter() |
|
||||
result = c.getArgument(j).(AddressOfExpr).getOperand()
|
||||
) and
|
||||
// Not a pointless read
|
||||
not result = any(ExprStmt es).getExpr()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a read access of variable `v` that occurs after the `initializingCall`.
|
||||
*/
|
||||
private VariableAccess getAReadAccessAfterCall(ConditionalInitializationCall initializingCall) {
|
||||
// Variable associated with this particular call
|
||||
call = initializingCall and
|
||||
// Access is a meaningful read access
|
||||
result = getAReadAccess() and
|
||||
// Which occurs after the call
|
||||
reaches(call, result) and
|
||||
/*
|
||||
* Ignore risky accesses which are arguments to calls which also include another parameter to
|
||||
* the original call. This is an attempt to eliminate results where the "status" can be checked
|
||||
* through another parameter that assigned as part of the original call.
|
||||
*/
|
||||
|
||||
not exists(Call c |
|
||||
c.getAnArgument() = result or
|
||||
c.getAnArgument().(AddressOfExpr).getOperand() = result
|
||||
|
|
||||
exists(LocalVariable lv |
|
||||
call.getAnArgument().(AddressOfExpr).getOperand() = lv.getAnAccess() and
|
||||
not lv = this
|
||||
|
|
||||
c.getAnArgument() = lv.getAnAccess()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an access to the variable that is risky because the variable may not be initialized after
|
||||
* the `call`, and the status of the call is never checked.
|
||||
*/
|
||||
VariableAccess getARiskyAccessWithNoStatusCheck(
|
||||
ConditionalInitializationFunction initializingFunction,
|
||||
ConditionalInitializationCall initializingCall, Evidence evidence
|
||||
) {
|
||||
// Variable associated with this particular call
|
||||
call = initializingCall and
|
||||
initializingFunction = f and
|
||||
e = evidence and
|
||||
result = getAReadAccessAfterCall(initializingCall) and
|
||||
(
|
||||
// Access is risky because status return code ignored completely
|
||||
call instanceof ExprInVoidContext
|
||||
or
|
||||
// Access is risky because status return code ignored completely
|
||||
exists(LocalVariable status | call = status.getAnAssignedValue() |
|
||||
not exists(status.getAnAccess())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an access to the variable that is risky because the variable may not be initialized after
|
||||
* the `call`, and the status of the call is only checked after the risky access.
|
||||
*/
|
||||
VariableAccess getARiskyAccessBeforeStatusCheck(
|
||||
ConditionalInitializationFunction initializingFunction,
|
||||
ConditionalInitializationCall initializingCall, Evidence evidence
|
||||
) {
|
||||
// Variable associated with this particular call
|
||||
call = initializingCall and
|
||||
initializingFunction = f and
|
||||
e = evidence and
|
||||
result = getAReadAccessAfterCall(initializingCall) and
|
||||
exists(LocalVariable status, Assignment a |
|
||||
a.getRValue() = call and
|
||||
call = status.getAnAssignedValue() and
|
||||
// There exists a check of the status code
|
||||
definitionUsePair(status, a, _) and
|
||||
// And the check of the status code does not occur before the risky access
|
||||
not exists(VariableAccess statusAccess |
|
||||
definitionUsePair(status, a, statusAccess) and
|
||||
reaches(statusAccess, result)
|
||||
) and
|
||||
// Ignore cases where the assignment to the status code is used directly
|
||||
a instanceof ExprInVoidContext and
|
||||
/*
|
||||
* Ignore risky accesses which are arguments to calls which also include the status code.
|
||||
* If both the risky value and status code are passed to a different function, that
|
||||
* function is responsible for checking the status code.
|
||||
*/
|
||||
|
||||
not exists(Call c |
|
||||
c.getAnArgument() = result or
|
||||
c.getAnArgument().(AddressOfExpr).getOperand() = result
|
||||
|
|
||||
definitionUsePair(status, a, c.getAnArgument())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an access to the variable that is risky because the variable may not be initialized after
|
||||
* the `call`.
|
||||
*/
|
||||
VariableAccess getARiskyAccess(
|
||||
ConditionalInitializationFunction initializingFunction,
|
||||
ConditionalInitializationCall initializingCall, Evidence evidence
|
||||
) {
|
||||
result = getARiskyAccessBeforeStatusCheck(initializingFunction, initializingCall, evidence) or
|
||||
result = getARiskyAccessWithNoStatusCheck(initializingFunction, initializingCall, evidence)
|
||||
}
|
||||
}
|
||||
14
cpp/ql/src/codeql-suites/cpp-lgtm-full.qls
Normal file
14
cpp/ql/src/codeql-suites/cpp-lgtm-full.qls
Normal file
@@ -0,0 +1,14 @@
|
||||
- description: Standard LGTM queries for C/C++, including ones not displayed by default
|
||||
- qlpack: codeql-cpp
|
||||
- apply: lgtm-selectors.yml
|
||||
from: codeql-suite-helpers
|
||||
# These queries are infeasible to compute on large projects:
|
||||
- exclude:
|
||||
query path:
|
||||
- Security/CWE/CWE-497/ExposedSystemData.ql
|
||||
- Critical/DescriptorMayNotBeClosed.ql
|
||||
- Critical/DescriptorNeverClosed.ql
|
||||
- Critical/FileMayNotBeClosed.ql
|
||||
- Critical/FileNeverClosed.ql
|
||||
- Critical/MemoryMayNotBeFreed.ql
|
||||
- Critical/MemoryNeverFreed.ql
|
||||
4
cpp/ql/src/codeql-suites/cpp-lgtm.qls
Normal file
4
cpp/ql/src/codeql-suites/cpp-lgtm.qls
Normal file
@@ -0,0 +1,4 @@
|
||||
- description: Standard LGTM queries for C/C++
|
||||
- apply: codeql-suites/cpp-lgtm-full.qls
|
||||
- apply: lgtm-displayed-only.yml
|
||||
from: codeql-suite-helpers
|
||||
@@ -51,7 +51,7 @@ class ReferenceCopyAssignmentOperator extends MemberFunction {
|
||||
|
||||
/**
|
||||
* A call to a function called swap. Note: could be a member,
|
||||
* 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
|
||||
*/
|
||||
FunctionCall getASwapCall() {
|
||||
|
||||
@@ -10,7 +10,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
|
||||
|
||||
@@ -15,11 +15,11 @@ import cpp
|
||||
/*
|
||||
* Interpretation and deviations:
|
||||
* - 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
|
||||
* RATIONALE: cf. MISRA, too many cases excluded otherwise
|
||||
* - 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 }
|
||||
|
||||
4
cpp/ql/src/qlpack.yml
Normal file
4
cpp/ql/src/qlpack.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
name: codeql-cpp
|
||||
version: 0.0.0
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
suites: codeql-suites
|
||||
@@ -14,8 +14,12 @@ private import semmle.code.cpp.internal.QualifiedName as Q
|
||||
* ```
|
||||
* extern int myglobal;
|
||||
* ```
|
||||
* Each of these declarations is given its own distinct `DeclarationEntry`,
|
||||
* but they all share the same `Declaration`.
|
||||
* and defined in one:
|
||||
* ```
|
||||
* 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
|
||||
* `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
|
||||
* explanation of the relationship between `Declaration` and
|
||||
* `DeclarationEntry`.
|
||||
* A C/C++ declaration entry. For example the following code contains five
|
||||
* declaration entries:
|
||||
* ```
|
||||
* 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 {
|
||||
/** 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
|
||||
* enclosing element. This comprises `Class` (they have access to their own
|
||||
* 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
|
||||
* the informal phrase "_R_ occurs in a member or friend of class C", where
|
||||
* `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
|
||||
* 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
|
||||
* `AccessHolder` -- that's more efficient because there are fewer
|
||||
* `DirectAccessHolder`s. If a `DirectAccessHolder` contains an `AccessHolder`,
|
||||
|
||||
41
cpp/ql/src/semmle/code/cpp/NestedFields.qll
Normal file
41
cpp/ql/src/semmle/code/cpp/NestedFields.qll
Normal file
@@ -0,0 +1,41 @@
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Gets a `Field` that is nested within the given `Struct`.
|
||||
*
|
||||
* This identifies `Field`s which are located in the same memory
|
||||
*/
|
||||
private Field getANestedField(Struct s) {
|
||||
result = s.getAField()
|
||||
or
|
||||
exists(NestedStruct ns |
|
||||
s = ns.getDeclaringType() and
|
||||
result = getANestedField(ns)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwraps a series of field accesses to determine the inner-most qualifier.
|
||||
*/
|
||||
private Expr getUltimateQualifier(FieldAccess fa) {
|
||||
exists(Expr qualifier | qualifier = fa.getQualifier() |
|
||||
result = getUltimateQualifier(qualifier)
|
||||
or
|
||||
not qualifier instanceof FieldAccess and result = qualifier
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Accesses to nested fields.
|
||||
*/
|
||||
class NestedFieldAccess extends FieldAccess {
|
||||
Expr ultimateQualifier;
|
||||
|
||||
NestedFieldAccess() {
|
||||
ultimateQualifier = getUltimateQualifier(this) and
|
||||
getTarget() = getANestedField(ultimateQualifier.getType().stripType())
|
||||
}
|
||||
|
||||
/** Gets the ultimate qualifier of this nested field access. */
|
||||
Expr getUltimateQualifier() { result = ultimateQualifier }
|
||||
}
|
||||
@@ -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()) }
|
||||
}
|
||||
|
||||
@@ -610,7 +610,9 @@ class FloatingPointType extends ArithmeticType {
|
||||
(
|
||||
kind >= 24 and kind <= 32
|
||||
or
|
||||
kind = 38
|
||||
kind >= 38 and kind <= 42
|
||||
or
|
||||
kind >= 45 and kind <= 50
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,11 +3,17 @@ private import semmle.code.cpp.internal.ResolveClass
|
||||
|
||||
/**
|
||||
* A C/C++ typedef type. See 4.9.1.
|
||||
*
|
||||
* Represents either of the following typedef styles:
|
||||
*
|
||||
* * CTypedefType: typedef <type> <name>;
|
||||
* * UsingAliasTypedefType: using <name> = <type>;
|
||||
*/
|
||||
class TypedefType extends UserType {
|
||||
TypedefType() { usertypes(underlyingElement(this), _, 5) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "TypedefType" }
|
||||
TypedefType() {
|
||||
usertypes(underlyingElement(this), _, 5) or
|
||||
usertypes(underlyingElement(this), _, 14)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the base type of this typedef type.
|
||||
@@ -26,10 +32,6 @@ class TypedefType extends UserType {
|
||||
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 isDeeplyConstBelow() { this.getBaseType().isDeeplyConstBelow() } // Just an alias
|
||||
@@ -45,6 +47,32 @@ class TypedefType extends UserType {
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -169,7 +169,11 @@ class FormattingFunctionCall extends Expr {
|
||||
* Gets the number of arguments to this call that are parameters to the
|
||||
* format string.
|
||||
*/
|
||||
int getNumFormatArgument() { result = count(this.getFormatArgument(_)) }
|
||||
int getNumFormatArgument() {
|
||||
result = count(this.getFormatArgument(_)) and
|
||||
// format arguments must be known
|
||||
exists(getTarget().(FormattingFunction).getFirstFormatArgumentIndex())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,7 +35,8 @@ private predicate exprInVoidContext(Expr e) {
|
||||
exists(CommaExpr c | c.getLeftOperand() = e)
|
||||
or
|
||||
exists(CommaExpr c | c.getRightOperand() = e and c instanceof ExprInVoidContext)
|
||||
or
|
||||
exists(ForStmt for | for.getUpdate() = e)
|
||||
) and
|
||||
not e instanceof Qualifier and
|
||||
not e.getActualType() instanceof VoidType
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import cpp
|
||||
import BasicBlocks
|
||||
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,
|
||||
@@ -86,11 +87,11 @@ import ControlFlowGraphPublic
|
||||
class ControlFlowNodeBase extends ElementBase, @cfgnode { }
|
||||
|
||||
predicate truecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
|
||||
truecond(unresolveElement(n1), unresolveElement(n2))
|
||||
qlCFGTrueSuccessor(n1, 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`.
|
||||
*/
|
||||
predicate successors_extended(ControlFlowNodeBase source, ControlFlowNodeBase target) {
|
||||
successors(unresolveElement(source), unresolveElement(target))
|
||||
qlCFGSuccessor(source, target)
|
||||
or
|
||||
source.(AdditionalControlFlowEdge).getAnEdgeTarget() = target
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ private predicate compares_eq(
|
||||
|
||||
/**
|
||||
* If `test => part` and `part => left == right + k` then `test => left == right + k`.
|
||||
* Similarly for the case where `test` is false.
|
||||
* Similarly for the case where `test` is false.
|
||||
*/
|
||||
private predicate logical_comparison_eq(
|
||||
BinaryLogicalOperation test, Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue
|
||||
@@ -275,7 +275,7 @@ private predicate compares_ge(
|
||||
|
||||
/**
|
||||
* If `test => part` and `part => left < right + k` then `test => left < right + k`.
|
||||
* Similarly for the case where `test` evaluates false.
|
||||
* Similarly for the case where `test` evaluates false.
|
||||
*/
|
||||
private predicate logical_comparison_lt(
|
||||
BinaryLogicalOperation test, Expr left, Expr right, int k, boolean isLt, boolean testIsTrue
|
||||
@@ -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) {
|
||||
e.getUnderlyingType() instanceof IntegralType and
|
||||
result = e.getValue().toInt()
|
||||
|
||||
@@ -29,11 +29,11 @@ class SsaDefinition extends ControlFlowNodeBase {
|
||||
|
||||
/**
|
||||
* 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)) }
|
||||
|
||||
/** 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(StandardSSA x | result = x.getAUse(this, v))
|
||||
}
|
||||
|
||||
@@ -173,49 +173,36 @@ predicate excludeNode(Node n) {
|
||||
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)`.
|
||||
* See the comment block at the top of this file.
|
||||
*/
|
||||
private class Pos extends AnyPos {
|
||||
// This is to make sure we get compile errors in code that forgets to restrict a `Pos`.
|
||||
private class Pos extends int {
|
||||
bindingset[this]
|
||||
Pos() { any() }
|
||||
|
||||
/** 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. */
|
||||
predicate isAt() { this = PosAt() }
|
||||
predicate isAt() { this = 1 }
|
||||
|
||||
/** 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
|
||||
* implicit destructor calls following `n`. The node `n` will be some node
|
||||
* 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
|
||||
* implicit destructor calls following `n`. The node `n` will be some node
|
||||
* that may be followed by local variables going out of scope.
|
||||
*/
|
||||
predicate isAfterDestructors() { this = PosAfterDestructors() }
|
||||
predicate isAfterDestructors() { this = 4 }
|
||||
|
||||
pragma[inline]
|
||||
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))))
|
||||
}
|
||||
|
||||
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
|
||||
* 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
|
||||
* _barrier_ value.
|
||||
*/
|
||||
private class Spec extends AnySpec {
|
||||
private class Spec extends Pos {
|
||||
bindingset[this]
|
||||
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
|
||||
* `(n2, p2)`, should add the following sub-edges.
|
||||
@@ -533,7 +494,7 @@ private class Spec extends AnySpec {
|
||||
* (n1, p1) ----> before(n)
|
||||
* 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
|
||||
@@ -542,16 +503,17 @@ private class Spec extends AnySpec {
|
||||
* (n1, p1) ----> beforeDestructors(n)
|
||||
* 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
|
||||
* can be inserted between nodes that should have no sub-edges between them.
|
||||
*/
|
||||
predicate isBarrier() { this = SpecBarrier() }
|
||||
predicate isBarrier() { this = 7 }
|
||||
|
||||
Pos getSourcePos() {
|
||||
this = SpecPos(result)
|
||||
this = [0 .. 4] and
|
||||
result = this
|
||||
or
|
||||
this.isAround() and
|
||||
result.isAfter()
|
||||
@@ -561,7 +523,8 @@ private class Spec extends AnySpec {
|
||||
}
|
||||
|
||||
Pos getTargetPos() {
|
||||
this = SpecPos(result)
|
||||
this = [0 .. 4] and
|
||||
result = this
|
||||
or
|
||||
this.isAround() and
|
||||
result.isBefore()
|
||||
@@ -888,6 +851,21 @@ private predicate subEdge(Pos p1, Node n1, Node n2, Pos p2) {
|
||||
p2.nodeAfter(n2, s)
|
||||
)
|
||||
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
|
||||
exists(WhileStmt s |
|
||||
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
|
||||
* have true/false sub-edges out of it, where the `truth`-sub-edge goes to
|
||||
* `(n2, p2)`.
|
||||
* Holds if `test` is the test of a control-flow construct where the `truth`
|
||||
* sub-edge goes to `(n2, p2)`.
|
||||
*/
|
||||
private predicate conditionJumpsTop(Expr test, boolean truth, Node n2, Pos p2) {
|
||||
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)
|
||||
)
|
||||
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 |
|
||||
(
|
||||
l instanceof WhileStmt
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplCommon::Public
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
@@ -905,30 +905,35 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate localFlowStepPlus(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||
) {
|
||||
localFlowEntry(node1, config) and
|
||||
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
localFlowEntry(node1, config) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -936,11 +941,11 @@ private predicate localFlowStepPlus(
|
||||
* 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.
|
||||
*/
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
||||
(
|
||||
any(Configuration c).isSource(this)
|
||||
or
|
||||
localFlowBigStep(_, this, false, _)
|
||||
localFlowBigStep(_, this, false, _, _)
|
||||
or
|
||||
additionalJumpStep(_, this, _)
|
||||
)
|
||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
(
|
||||
exists(Node mid |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(mid, fromArg, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
apf = node.(AccessPathFrontNilNode).getApf()
|
||||
)
|
||||
or
|
||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
flowCandFwd(mid, fromArg, _, config) and
|
||||
store(mid, f, node) and
|
||||
nodeCand(node, unbind(config)) and
|
||||
readStoreCand(f, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
apf instanceof AccessPathFrontNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flowCand(mid, toReturn, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(node, _, apf, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flowCand(mid, toReturn, nil, config) and
|
||||
apf instanceof AccessPathFrontNil
|
||||
)
|
||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandStore(node, f, toReturn, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCand(f, apf, unbind(config))
|
||||
consCand(f, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, apf0, config) and
|
||||
consCandFwd(f, apf0, unbind(config)) and
|
||||
consCandFwd(f, apf0, config) and
|
||||
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] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* 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
|
||||
* 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.
|
||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
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() {
|
||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
override string toString() {
|
||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||
// 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() {
|
||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||
if len = 2
|
||||
then result = f1.toString() + ", " + f2.toString()
|
||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
||||
(
|
||||
exists(Node mid |
|
||||
flowFwd(mid, fromArg, apf, ap, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(mid, fromArg, _, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
ap = node.(AccessPathNilNode).getAp() and
|
||||
apf = ap.(AccessPathNil).getFront()
|
||||
)
|
||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
ap instanceof AccessPathNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flow(mid, toReturn, ap, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(node, _, _, ap, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flow(mid, toReturn, nil, config) and
|
||||
ap instanceof AccessPathNil
|
||||
)
|
||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
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`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
or
|
||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||
ap = ap0
|
||||
or
|
||||
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||
ap0 instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
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() {
|
||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
if len = 1
|
||||
then result = f.toString()
|
||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2204,16 +2216,19 @@ private module FlowExploration {
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, config) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplCommon::Public
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
@@ -905,30 +905,35 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate localFlowStepPlus(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||
) {
|
||||
localFlowEntry(node1, config) and
|
||||
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
localFlowEntry(node1, config) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -936,11 +941,11 @@ private predicate localFlowStepPlus(
|
||||
* 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.
|
||||
*/
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
||||
(
|
||||
any(Configuration c).isSource(this)
|
||||
or
|
||||
localFlowBigStep(_, this, false, _)
|
||||
localFlowBigStep(_, this, false, _, _)
|
||||
or
|
||||
additionalJumpStep(_, this, _)
|
||||
)
|
||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
(
|
||||
exists(Node mid |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(mid, fromArg, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
apf = node.(AccessPathFrontNilNode).getApf()
|
||||
)
|
||||
or
|
||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
flowCandFwd(mid, fromArg, _, config) and
|
||||
store(mid, f, node) and
|
||||
nodeCand(node, unbind(config)) and
|
||||
readStoreCand(f, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
apf instanceof AccessPathFrontNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flowCand(mid, toReturn, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(node, _, apf, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flowCand(mid, toReturn, nil, config) and
|
||||
apf instanceof AccessPathFrontNil
|
||||
)
|
||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandStore(node, f, toReturn, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCand(f, apf, unbind(config))
|
||||
consCand(f, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, apf0, config) and
|
||||
consCandFwd(f, apf0, unbind(config)) and
|
||||
consCandFwd(f, apf0, config) and
|
||||
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] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* 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
|
||||
* 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.
|
||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
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() {
|
||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
override string toString() {
|
||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||
// 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() {
|
||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||
if len = 2
|
||||
then result = f1.toString() + ", " + f2.toString()
|
||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
||||
(
|
||||
exists(Node mid |
|
||||
flowFwd(mid, fromArg, apf, ap, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(mid, fromArg, _, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
ap = node.(AccessPathNilNode).getAp() and
|
||||
apf = ap.(AccessPathNil).getFront()
|
||||
)
|
||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
ap instanceof AccessPathNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flow(mid, toReturn, ap, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(node, _, _, ap, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flow(mid, toReturn, nil, config) and
|
||||
ap instanceof AccessPathNil
|
||||
)
|
||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
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`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
or
|
||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||
ap = ap0
|
||||
or
|
||||
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||
ap0 instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
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() {
|
||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
if len = 1
|
||||
then result = f.toString()
|
||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2204,16 +2216,19 @@ private module FlowExploration {
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, config) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplCommon::Public
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
@@ -905,30 +905,35 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate localFlowStepPlus(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||
) {
|
||||
localFlowEntry(node1, config) and
|
||||
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
localFlowEntry(node1, config) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -936,11 +941,11 @@ private predicate localFlowStepPlus(
|
||||
* 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.
|
||||
*/
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
||||
(
|
||||
any(Configuration c).isSource(this)
|
||||
or
|
||||
localFlowBigStep(_, this, false, _)
|
||||
localFlowBigStep(_, this, false, _, _)
|
||||
or
|
||||
additionalJumpStep(_, this, _)
|
||||
)
|
||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
(
|
||||
exists(Node mid |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(mid, fromArg, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
apf = node.(AccessPathFrontNilNode).getApf()
|
||||
)
|
||||
or
|
||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
flowCandFwd(mid, fromArg, _, config) and
|
||||
store(mid, f, node) and
|
||||
nodeCand(node, unbind(config)) and
|
||||
readStoreCand(f, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
apf instanceof AccessPathFrontNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flowCand(mid, toReturn, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(node, _, apf, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flowCand(mid, toReturn, nil, config) and
|
||||
apf instanceof AccessPathFrontNil
|
||||
)
|
||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandStore(node, f, toReturn, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCand(f, apf, unbind(config))
|
||||
consCand(f, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, apf0, config) and
|
||||
consCandFwd(f, apf0, unbind(config)) and
|
||||
consCandFwd(f, apf0, config) and
|
||||
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] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* 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
|
||||
* 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.
|
||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
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() {
|
||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
override string toString() {
|
||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||
// 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() {
|
||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||
if len = 2
|
||||
then result = f1.toString() + ", " + f2.toString()
|
||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
||||
(
|
||||
exists(Node mid |
|
||||
flowFwd(mid, fromArg, apf, ap, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(mid, fromArg, _, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
ap = node.(AccessPathNilNode).getAp() and
|
||||
apf = ap.(AccessPathNil).getFront()
|
||||
)
|
||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
ap instanceof AccessPathNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flow(mid, toReturn, ap, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(node, _, _, ap, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flow(mid, toReturn, nil, config) and
|
||||
ap instanceof AccessPathNil
|
||||
)
|
||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
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`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
or
|
||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||
ap = ap0
|
||||
or
|
||||
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||
ap0 instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
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() {
|
||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
if len = 1
|
||||
then result = f.toString()
|
||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2204,16 +2216,19 @@ private module FlowExploration {
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, config) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplCommon::Public
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
@@ -905,30 +905,35 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate localFlowStepPlus(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||
) {
|
||||
localFlowEntry(node1, config) and
|
||||
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
localFlowEntry(node1, config) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -936,11 +941,11 @@ private predicate localFlowStepPlus(
|
||||
* 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.
|
||||
*/
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
||||
(
|
||||
any(Configuration c).isSource(this)
|
||||
or
|
||||
localFlowBigStep(_, this, false, _)
|
||||
localFlowBigStep(_, this, false, _, _)
|
||||
or
|
||||
additionalJumpStep(_, this, _)
|
||||
)
|
||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
(
|
||||
exists(Node mid |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(mid, fromArg, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
apf = node.(AccessPathFrontNilNode).getApf()
|
||||
)
|
||||
or
|
||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
flowCandFwd(mid, fromArg, _, config) and
|
||||
store(mid, f, node) and
|
||||
nodeCand(node, unbind(config)) and
|
||||
readStoreCand(f, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
apf instanceof AccessPathFrontNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flowCand(mid, toReturn, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(node, _, apf, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flowCand(mid, toReturn, nil, config) and
|
||||
apf instanceof AccessPathFrontNil
|
||||
)
|
||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandStore(node, f, toReturn, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCand(f, apf, unbind(config))
|
||||
consCand(f, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, apf0, config) and
|
||||
consCandFwd(f, apf0, unbind(config)) and
|
||||
consCandFwd(f, apf0, config) and
|
||||
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] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* 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
|
||||
* 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.
|
||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
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() {
|
||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
override string toString() {
|
||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||
// 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() {
|
||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||
if len = 2
|
||||
then result = f1.toString() + ", " + f2.toString()
|
||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
||||
(
|
||||
exists(Node mid |
|
||||
flowFwd(mid, fromArg, apf, ap, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(mid, fromArg, _, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
ap = node.(AccessPathNilNode).getAp() and
|
||||
apf = ap.(AccessPathNil).getFront()
|
||||
)
|
||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
ap instanceof AccessPathNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flow(mid, toReturn, ap, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(node, _, _, ap, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flow(mid, toReturn, nil, config) and
|
||||
ap instanceof AccessPathNil
|
||||
)
|
||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
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`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
or
|
||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||
ap = ap0
|
||||
or
|
||||
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||
ap0 instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
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() {
|
||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
if len = 1
|
||||
then result = f.toString()
|
||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2204,16 +2216,19 @@ private module FlowExploration {
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, config) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplCommon::Public
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
@@ -905,30 +905,35 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate localFlowStepPlus(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||
) {
|
||||
localFlowEntry(node1, config) and
|
||||
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
localFlowEntry(node1, config) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -936,11 +941,11 @@ private predicate localFlowStepPlus(
|
||||
* 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.
|
||||
*/
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
||||
(
|
||||
any(Configuration c).isSource(this)
|
||||
or
|
||||
localFlowBigStep(_, this, false, _)
|
||||
localFlowBigStep(_, this, false, _, _)
|
||||
or
|
||||
additionalJumpStep(_, this, _)
|
||||
)
|
||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
(
|
||||
exists(Node mid |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(mid, fromArg, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
apf = node.(AccessPathFrontNilNode).getApf()
|
||||
)
|
||||
or
|
||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
flowCandFwd(mid, fromArg, _, config) and
|
||||
store(mid, f, node) and
|
||||
nodeCand(node, unbind(config)) and
|
||||
readStoreCand(f, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
apf instanceof AccessPathFrontNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flowCand(mid, toReturn, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(node, _, apf, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flowCand(mid, toReturn, nil, config) and
|
||||
apf instanceof AccessPathFrontNil
|
||||
)
|
||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandStore(node, f, toReturn, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCand(f, apf, unbind(config))
|
||||
consCand(f, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, apf0, config) and
|
||||
consCandFwd(f, apf0, unbind(config)) and
|
||||
consCandFwd(f, apf0, config) and
|
||||
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] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* 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
|
||||
* 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.
|
||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
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() {
|
||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
override string toString() {
|
||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||
// 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() {
|
||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||
if len = 2
|
||||
then result = f1.toString() + ", " + f2.toString()
|
||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
||||
(
|
||||
exists(Node mid |
|
||||
flowFwd(mid, fromArg, apf, ap, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(mid, fromArg, _, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
ap = node.(AccessPathNilNode).getAp() and
|
||||
apf = ap.(AccessPathNil).getFront()
|
||||
)
|
||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
ap instanceof AccessPathNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flow(mid, toReturn, ap, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(node, _, _, ap, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flow(mid, toReturn, nil, config) and
|
||||
ap instanceof AccessPathNil
|
||||
)
|
||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
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`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
or
|
||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||
ap = ap0
|
||||
or
|
||||
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||
ap0 instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
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() {
|
||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
if len = 1
|
||||
then result = f.toString()
|
||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2204,16 +2216,19 @@ private module FlowExploration {
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, config) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
|
||||
@@ -51,7 +51,9 @@ class ArgumentNode extends Node {
|
||||
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
|
||||
@@ -59,23 +61,54 @@ private newtype TReturnKind = TNormalReturnKind()
|
||||
*/
|
||||
class ReturnKind extends TReturnKind {
|
||||
/** 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`. */
|
||||
class ReturnNode extends ExprNode {
|
||||
ReturnNode() { exists(ReturnStmt ret | this.getExpr() = ret.getExpr()) }
|
||||
/** A data flow node that represents a returned value in the called function. */
|
||||
abstract class ReturnNode extends Node {
|
||||
/** 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. */
|
||||
ReturnKind getKind() { result = TNormalReturnKind() }
|
||||
override ReturnKind getKind() { result = TNormalReturnKind() }
|
||||
}
|
||||
|
||||
/** A data flow node that represents the output of a call. */
|
||||
class OutNode extends ExprNode {
|
||||
OutNode() { this.getExpr() instanceof Call }
|
||||
/**
|
||||
* A `ReturnNode` that occurs as a result of a definition of a reference
|
||||
* 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. */
|
||||
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) {
|
||||
result = call.getNode() and
|
||||
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. */
|
||||
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
|
||||
}
|
||||
|
||||
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
|
||||
|
||||
@@ -25,7 +25,8 @@ private newtype TNode =
|
||||
not c.getTarget().getParameter(i).getUnderlyingType().(PointerType).getBaseType().isConst()
|
||||
)
|
||||
} 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.
|
||||
@@ -248,6 +249,23 @@ class UninitializedNode extends Node, TUninitializedNode {
|
||||
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
|
||||
* changed its state.
|
||||
@@ -490,7 +508,7 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
or
|
||||
var.definedPartiallyAt(nodeFrom.asPartialDefinition())
|
||||
) and
|
||||
varToExprStep(var, nodeTo.asExpr())
|
||||
varToNodeStep(var, nodeTo)
|
||||
)
|
||||
or
|
||||
// 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
|
||||
@@ -553,6 +575,10 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) {
|
||||
or
|
||||
toExpr = any(StmtExpr stmtExpr | fromExpr = stmtExpr.getResultExpr())
|
||||
or
|
||||
toExpr.(AddressOfExpr).getOperand() = fromExpr
|
||||
or
|
||||
toExpr.(BuiltInOperationBuiltInAddressOf).getOperand() = fromExpr
|
||||
or
|
||||
// The following case is needed to track the qualifier object for flow
|
||||
// through fields. It gives flow from `T(x)` to `new T(x)`. That's not
|
||||
// strictly _data_ flow but _taint_ flow because the type of `fromExpr` is
|
||||
@@ -574,8 +600,8 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) {
|
||||
exists(DataFlowFunction f, FunctionInput inModel, FunctionOutput outModel, int iIn |
|
||||
call.getTarget() = f and
|
||||
f.hasDataFlow(inModel, outModel) and
|
||||
outModel.isOutReturnValue() and
|
||||
inModel.isInParameter(iIn) and
|
||||
outModel.isReturnValue() and
|
||||
inModel.isParameter(iIn) and
|
||||
fromExpr = call.getArgument(iIn)
|
||||
)
|
||||
)
|
||||
@@ -585,12 +611,12 @@ private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
|
||||
exists(DataFlowFunction f, Call call, FunctionOutput outModel, int argOutIndex |
|
||||
call.getTarget() = f and
|
||||
argOut = call.getArgument(argOutIndex) and
|
||||
outModel.isOutParameterPointer(argOutIndex) and
|
||||
outModel.isParameterDeref(argOutIndex) and
|
||||
exists(int argInIndex, FunctionInput inModel | f.hasDataFlow(inModel, outModel) |
|
||||
inModel.isInParameterPointer(argInIndex) and
|
||||
inModel.isParameterDeref(argInIndex) and
|
||||
call.passesByReference(argInIndex, exprIn)
|
||||
or
|
||||
inModel.isInParameter(argInIndex) and
|
||||
inModel.isParameter(argInIndex) and
|
||||
exprIn = call.getArgument(argInIndex)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -62,9 +62,20 @@ class FlowVar extends TFlowVar {
|
||||
cached
|
||||
abstract predicate definedByReference(Expr arg);
|
||||
|
||||
/**
|
||||
* Holds if this `FlowVar` is a `PartialDefinition` whose defined expression
|
||||
* is `e`.
|
||||
*/
|
||||
cached
|
||||
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
|
||||
* is an exhaustive list of cases where this may happen.
|
||||
@@ -338,6 +349,9 @@ module FlowVar_internal {
|
||||
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
|
||||
* 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()
|
||||
}
|
||||
|
||||
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) {
|
||||
blockVarDefinedByVariable(sbb, lsv) and
|
||||
lsv = v
|
||||
@@ -490,7 +511,7 @@ module FlowVar_internal {
|
||||
exists(VariableAccess va |
|
||||
va.getTarget() = result 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)
|
||||
}
|
||||
|
||||
predicate bbNotInLoop(BasicBlock bb) {
|
||||
not this.bbInLoop(bb) and
|
||||
bb.getEnclosingFunction() = this.getEnclosingFunction()
|
||||
}
|
||||
/** Holds if `sbb` is inside this loop. */
|
||||
predicate sbbInLoop(SubBasicBlock sbb) { this.bbInLoop(sbb.getBasicBlock()) }
|
||||
|
||||
/**
|
||||
* 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
|
||||
* from `bbInside` in its condition to `bbOutside` outside the loop, where
|
||||
* (`sbbDef`, `v`) is a `BlockVar` defined outside the loop. Also, `v` must
|
||||
* be used outside the loop.
|
||||
* Holds if `loop` always assigns to `v` before leaving through an edge
|
||||
* from `bbInside` in its condition to `bbOutside` outside the loop. Also,
|
||||
* `v` must be used outside the loop.
|
||||
*/
|
||||
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
|
||||
bbInside = sbbInside.getBasicBlock() and
|
||||
bbOutside = sbbOutside.getBasicBlock() and
|
||||
sbbInside.lastInBB() and
|
||||
sbbOutside.firstInBB() and
|
||||
loop.bbNotInLoop(sbbDef.getBasicBlock()) and
|
||||
exists(TBlockVar(sbbDef, v))
|
||||
sbbOutside.firstInBB()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -571,7 +587,7 @@ module FlowVar_internal {
|
||||
start = TBlockVar(sbbDef, v) and
|
||||
result = mid.getASuccessor() 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, _, _)
|
||||
)
|
||||
}
|
||||
@@ -593,12 +609,23 @@ module FlowVar_internal {
|
||||
private predicate variableLiveInSBB(SubBasicBlock sbb, Variable v) {
|
||||
variableAccessInSBB(v, sbb, _)
|
||||
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() |
|
||||
variableLiveInSBB(succ, v) and
|
||||
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`.
|
||||
*/
|
||||
@@ -679,10 +706,11 @@ module FlowVar_internal {
|
||||
predicate dominatedByOverwrite(UninitializedLocalVariable v, VariableAccess va) {
|
||||
exists(BasicBlock bb, int vaIndex |
|
||||
va = bb.getNode(vaIndex) and
|
||||
va.getTarget() = v
|
||||
|
|
||||
va.getTarget() = v and
|
||||
vaIndex > indexOfFirstOverwriteInBB(v, bb)
|
||||
or
|
||||
va = bb.getNode(vaIndex) and
|
||||
va.getTarget() = v and
|
||||
bbStrictlyDominates(getAnOverwritingBB(v), bb)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -122,11 +122,11 @@ private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
|
||||
exists(DataFlowFunction f, Call call, FunctionOutput outModel, int argOutIndex |
|
||||
call.getTarget() = f and
|
||||
argOut = call.getArgument(argOutIndex) and
|
||||
outModel.isOutParameterPointer(argOutIndex) and
|
||||
outModel.isParameterDeref(argOutIndex) and
|
||||
exists(int argInIndex, FunctionInput inModel | f.hasDataFlow(inModel, outModel) |
|
||||
// Taint flows from a pointer to a dereference, which DataFlow does not handle
|
||||
// memcpy(&dest_var, tainted_ptr, len)
|
||||
inModel.isInParameterPointer(argInIndex) and
|
||||
inModel.isParameterDeref(argInIndex) and
|
||||
exprIn = call.getArgument(argInIndex)
|
||||
)
|
||||
)
|
||||
@@ -134,15 +134,15 @@ private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
|
||||
exists(TaintFunction f, Call call, FunctionOutput outModel, int argOutIndex |
|
||||
call.getTarget() = f and
|
||||
argOut = call.getArgument(argOutIndex) and
|
||||
outModel.isOutParameterPointer(argOutIndex) and
|
||||
outModel.isParameterDeref(argOutIndex) and
|
||||
exists(int argInIndex, FunctionInput inModel | f.hasTaintFlow(inModel, outModel) |
|
||||
inModel.isInParameterPointer(argInIndex) and
|
||||
inModel.isParameterDeref(argInIndex) and
|
||||
exprIn = call.getArgument(argInIndex)
|
||||
or
|
||||
inModel.isInParameterPointer(argInIndex) and
|
||||
inModel.isParameterDeref(argInIndex) and
|
||||
call.passesByReference(argInIndex, exprIn)
|
||||
or
|
||||
inModel.isInParameter(argInIndex) and
|
||||
inModel.isParameter(argInIndex) and
|
||||
exprIn = call.getArgument(argInIndex)
|
||||
)
|
||||
)
|
||||
|
||||
83
cpp/ql/src/semmle/code/cpp/dispatch/VirtualDispatch.qll
Normal file
83
cpp/ql/src/semmle/code/cpp/dispatch/VirtualDispatch.qll
Normal file
@@ -0,0 +1,83 @@
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* A module for performing simple virtual dispatch analysis.
|
||||
*/
|
||||
module VirtualDispatch {
|
||||
/**
|
||||
* Gets a possible implementation target when the given function is the static target of a virtual call.
|
||||
*/
|
||||
private MemberFunction getAPossibleImplementation(MemberFunction staticTarget) {
|
||||
/*
|
||||
* `IUnknown` is a COM interface with many sub-types, and many overrides (tens of thousands on
|
||||
* some databases), so we ignore any member functions defined within that interface.
|
||||
*/
|
||||
|
||||
not staticTarget.getDeclaringType().hasName("IUnknown") and
|
||||
result = staticTarget.getAnOverridingFunction*()
|
||||
}
|
||||
|
||||
/** Gets the static type of the qualifier expression for the given call. */
|
||||
private Class getCallQualifierType(FunctionCall c) {
|
||||
result = c.getQualifier().getType().stripType() and
|
||||
/*
|
||||
* `IUnknown` is a COM interface with many sub-types (tens of thousands on some databases), so
|
||||
* we ignore any cases where the qualifier type is that interface.
|
||||
*/
|
||||
|
||||
not result.hasName("IUnknown")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable target for the given function call.
|
||||
*
|
||||
* If `c` is a virtual call, then we will perform a simple virtual dispatch analysis to return
|
||||
* the `Function` instances which might be a viable target, based on an analysis of the declared
|
||||
* type of the qualifier expression.
|
||||
*
|
||||
* (This analysis is imprecise: it looks for subtypes of the declared type of the qualifier expression
|
||||
* and the possible implementations of `c.getTarget()` that are declared or inherited by those subtypes.
|
||||
* This does not account for virtual inheritance and the ways this affects dispatch.)
|
||||
*
|
||||
* If `c` is not a virtual call, the result will be `c.getTarget()`.
|
||||
*/
|
||||
Function getAViableTarget(Call c) {
|
||||
exists(Function staticTarget | staticTarget = c.getTarget() |
|
||||
if c.(FunctionCall).isVirtual() and staticTarget instanceof MemberFunction
|
||||
then
|
||||
exists(Class qualifierType, Class qualifierSubType |
|
||||
result = getAPossibleImplementation(staticTarget) and
|
||||
qualifierType = getCallQualifierType(c) and
|
||||
qualifierType = qualifierSubType.getABaseClass*() and
|
||||
mayInherit(qualifierSubType, result) and
|
||||
not cannotInherit(qualifierSubType, result)
|
||||
)
|
||||
else result = staticTarget
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `f` is declared in `c` or a transitive base class of `c`. */
|
||||
private predicate mayInherit(Class c, MemberFunction f) {
|
||||
f.getDeclaringType() = c.getABaseClass*()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `c` cannot inherit the member function `f`,
|
||||
* i.e. `c` or one of its supertypes overrides `f`.
|
||||
*/
|
||||
private predicate cannotInherit(Class c, MemberFunction f) {
|
||||
exists(Class overridingType, MemberFunction override |
|
||||
cannotInheritHelper(c, f, overridingType, override) and
|
||||
override.overrides+(f)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate cannotInheritHelper(
|
||||
Class c, MemberFunction f, Class overridingType, MemberFunction override
|
||||
) {
|
||||
c.getABaseClass*() = overridingType and
|
||||
override.getDeclaringType() = overridingType and
|
||||
overridingType.getABaseClass+() = f.getDeclaringType()
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
override string getCanonicalQLClass() { result = "EnumConstantAccess" }
|
||||
@@ -27,15 +38,23 @@ class EnumConstantAccess extends Access, @varaccess {
|
||||
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)) }
|
||||
|
||||
/** 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() }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
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 {
|
||||
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,
|
||||
* so this is equivalent to `(*obj).field`.
|
||||
* A field access whose qualifier is a pointer to a class, struct or union.
|
||||
* 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 {
|
||||
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
|
||||
* 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 {
|
||||
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
|
||||
* 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 {
|
||||
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
|
||||
* form `T&`), which is converted to a value. For example:
|
||||
*
|
||||
* ```
|
||||
* int myfcn(MyStruct &x) {
|
||||
* return x.field;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In this example, the type of `x` is `MyStruct&`, but it gets implicitly
|
||||
* converted to `MyStruct` in the expression `x.field`.
|
||||
*/
|
||||
private predicate exprHasReferenceConversion(Expr e) { referenceConversion(e.getConversion+()) }
|
||||
|
||||
/**
|
||||
* A field access of a field of `this`. The access has no qualifier because
|
||||
* the use of `this` is implicit. For example, `field` is equivalent to
|
||||
* `this->field` if `field` is a member of `this`.
|
||||
* A field access of a field of `this` which has no qualifier because
|
||||
* the use of `this` is implicit. For example, in the following code the
|
||||
* 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
|
||||
* `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
|
||||
* `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 {
|
||||
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:
|
||||
* ```
|
||||
@@ -279,7 +369,7 @@ class FunctionAccess extends Access, @routineexpr {
|
||||
* }
|
||||
* ```
|
||||
* 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 {
|
||||
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
|
||||
* 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 {
|
||||
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 {
|
||||
override string getCanonicalQLClass() { result = "ArrayExpr" }
|
||||
@@ -306,14 +419,14 @@ class ArrayExpr extends Expr, @subscriptexpr {
|
||||
/**
|
||||
* 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) }
|
||||
|
||||
/**
|
||||
* 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) }
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ class UnaryPlusExpr extends UnaryArithmeticOperation, @unaryplusexpr {
|
||||
*/
|
||||
class ConjugationExpr extends UnaryArithmeticOperation, @conjugation {
|
||||
override string getOperator() { result = "~" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "ConjugationExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,6 +144,8 @@ class PostfixDecrExpr extends DecrementOperation, PostfixCrementOperation, @post
|
||||
*/
|
||||
class RealPartExpr extends UnaryArithmeticOperation, @realpartexpr {
|
||||
override string getOperator() { result = "__real" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "RealPartExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,6 +153,8 @@ class RealPartExpr extends UnaryArithmeticOperation, @realpartexpr {
|
||||
*/
|
||||
class ImaginaryPartExpr extends UnaryArithmeticOperation, @imagpartexpr {
|
||||
override string getOperator() { result = "__imag" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "ImaginaryPartExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -217,6 +223,8 @@ class RemExpr extends BinaryArithmeticOperation, @remexpr {
|
||||
class ImaginaryMulExpr extends BinaryArithmeticOperation, @jmulexpr {
|
||||
override string getOperator() { result = "*" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "ImaginaryMulExpr" }
|
||||
|
||||
override int getPrecedence() { result = 13 }
|
||||
}
|
||||
|
||||
@@ -226,6 +234,8 @@ class ImaginaryMulExpr extends BinaryArithmeticOperation, @jmulexpr {
|
||||
class ImaginaryDivExpr extends BinaryArithmeticOperation, @jdivexpr {
|
||||
override string getOperator() { result = "/" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "ImaginaryDivExpr" }
|
||||
|
||||
override int getPrecedence() { result = 13 }
|
||||
}
|
||||
|
||||
@@ -235,6 +245,8 @@ class ImaginaryDivExpr extends BinaryArithmeticOperation, @jdivexpr {
|
||||
class RealImaginaryAddExpr extends BinaryArithmeticOperation, @fjaddexpr {
|
||||
override string getOperator() { result = "+" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "RealImaginaryAddExpr" }
|
||||
|
||||
override int getPrecedence() { result = 12 }
|
||||
}
|
||||
|
||||
@@ -244,6 +256,8 @@ class RealImaginaryAddExpr extends BinaryArithmeticOperation, @fjaddexpr {
|
||||
class ImaginaryRealAddExpr extends BinaryArithmeticOperation, @jfaddexpr {
|
||||
override string getOperator() { result = "+" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "ImaginaryRealAddExpr" }
|
||||
|
||||
override int getPrecedence() { result = 12 }
|
||||
}
|
||||
|
||||
@@ -253,6 +267,8 @@ class ImaginaryRealAddExpr extends BinaryArithmeticOperation, @jfaddexpr {
|
||||
class RealImaginarySubExpr extends BinaryArithmeticOperation, @fjsubexpr {
|
||||
override string getOperator() { result = "-" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "RealImaginarySubExpr" }
|
||||
|
||||
override int getPrecedence() { result = 12 }
|
||||
}
|
||||
|
||||
@@ -262,6 +278,8 @@ class RealImaginarySubExpr extends BinaryArithmeticOperation, @fjsubexpr {
|
||||
class ImaginaryRealSubExpr extends BinaryArithmeticOperation, @jfsubexpr {
|
||||
override string getOperator() { result = "-" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "ImaginaryRealSubExpr" }
|
||||
|
||||
override int getPrecedence() { result = 12 }
|
||||
}
|
||||
|
||||
@@ -270,6 +288,8 @@ class ImaginaryRealSubExpr extends BinaryArithmeticOperation, @jfsubexpr {
|
||||
*/
|
||||
class MinExpr extends BinaryArithmeticOperation, @minexpr {
|
||||
override string getOperator() { result = "<?" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "MinExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -277,6 +297,8 @@ class MinExpr extends BinaryArithmeticOperation, @minexpr {
|
||||
*/
|
||||
class MaxExpr extends BinaryArithmeticOperation, @maxexpr {
|
||||
override string getOperator() { result = ">?" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "MaxExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -519,3 +519,18 @@ class BuiltInChooseExpr extends BuiltInOperation, @builtinchooseexpr {
|
||||
class VectorFillOperation extends UnaryOperation, @vec_fill {
|
||||
override string getOperator() { result = "(vector fill)" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The GNU `__builtin_complex` operation.
|
||||
*/
|
||||
class BuiltInComplexOperation extends BuiltInOperation, @builtincomplex {
|
||||
override string toString() { result = "__builtin_complex" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "BuiltInComplexOperation" }
|
||||
|
||||
/** Gets the operand corresponding to the real part of the complex number. */
|
||||
Expr getRealOperand() { this.hasChild(result, 0) }
|
||||
|
||||
/** Gets the operand corresponding to the imaginary part of the complex number. */
|
||||
Expr getImaginaryOperand() { this.hasChild(result, 1) }
|
||||
}
|
||||
|
||||
@@ -131,6 +131,8 @@ class Expr extends StmtParent, @expr {
|
||||
valuebind(_, underlyingElement(this))
|
||||
or
|
||||
addressConstantExpression(this)
|
||||
or
|
||||
constantTemplateLiteral(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -538,6 +540,13 @@ class ErrorExpr extends Expr, @errorexpr {
|
||||
*/
|
||||
class AssumeExpr extends Expr, @assume {
|
||||
override string toString() { result = "__assume(...)" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "AssumeExpr" }
|
||||
|
||||
/**
|
||||
* Gets the operand of the `__assume` expressions.
|
||||
*/
|
||||
Expr getOperand() { this.hasChild(result, 0) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1119,3 +1128,17 @@ private predicate isStandardPlacementNewAllocator(Function operatorNew) {
|
||||
|
||||
// Pulled out for performance. See QL-796.
|
||||
private predicate hasNoConversions(Expr e) { not e.hasConversion() }
|
||||
|
||||
/**
|
||||
* Holds if `e` is a literal of unknown value in a template, or a cast thereof.
|
||||
* We assume that such literals are constant.
|
||||
*/
|
||||
private predicate constantTemplateLiteral(Expr e) {
|
||||
// Unknown literals in uninstantiated templates could be enum constant
|
||||
// accesses or pointer-to-member literals.
|
||||
e instanceof Literal and
|
||||
e.isFromUninstantiatedTemplate(_) and
|
||||
not exists(e.getValue())
|
||||
or
|
||||
constantTemplateLiteral(e.(Cast).getExpr())
|
||||
}
|
||||
|
||||
@@ -132,7 +132,6 @@ class HexLiteral extends Literal {
|
||||
* A C/C++ aggregate literal.
|
||||
*/
|
||||
class AggregateLiteral extends Expr, @aggregateliteral {
|
||||
// if this is turned into a Literal we need to change mayBeImpure
|
||||
override string getCanonicalQLClass() { result = "AggregateLiteral" }
|
||||
|
||||
/**
|
||||
@@ -145,6 +144,10 @@ class AggregateLiteral extends Expr, @aggregateliteral {
|
||||
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. */
|
||||
override string toString() { result = "{...}" }
|
||||
}
|
||||
|
||||
@@ -32,6 +32,11 @@ private predicate constantAddressLValue(Expr lvalue) {
|
||||
// tells us how it's going to be used.
|
||||
lvalue.(FunctionAccess).getType() instanceof RoutineType
|
||||
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.
|
||||
lvalue instanceof StringLiteral
|
||||
or
|
||||
@@ -61,6 +66,10 @@ private predicate constantAddressPointer(Expr pointer) {
|
||||
// tells us how it's going to be used.
|
||||
pointer.(FunctionAccess).getType() instanceof FunctionPointerType
|
||||
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
|
||||
pointer.getType().getUnderlyingType() instanceof PointerType
|
||||
or
|
||||
|
||||
@@ -4,25 +4,17 @@ private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
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
|
||||
* 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) {
|
||||
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 {
|
||||
@@ -90,10 +82,10 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
|
||||
predictableInstruction(i2.getAnOperand().getDef()) and
|
||||
i1 = i2.getAnOperand().getDef()
|
||||
)
|
||||
// TODO: Check that we have flow from `a` to `a[i]`. It may work for constant
|
||||
// `i` because there is flow through `predictable` `BinaryInstruction` and
|
||||
// through `LoadInstruction`.
|
||||
//
|
||||
or
|
||||
// This is part of the translation of `a[i]`, where we want taint to flow
|
||||
// from `a`.
|
||||
i2.(PointerAddInstruction).getLeft() = i1
|
||||
// TODO: Flow from argument to return of known functions: Port missing parts
|
||||
// of `returnArgument` to the `interfaces.Taint` and `interfaces.DataFlow`
|
||||
// libraries.
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplCommon::Public
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
@@ -905,30 +905,35 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate localFlowStepPlus(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||
) {
|
||||
localFlowEntry(node1, config) and
|
||||
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
localFlowEntry(node1, config) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -936,11 +941,11 @@ private predicate localFlowStepPlus(
|
||||
* 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.
|
||||
*/
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
||||
(
|
||||
any(Configuration c).isSource(this)
|
||||
or
|
||||
localFlowBigStep(_, this, false, _)
|
||||
localFlowBigStep(_, this, false, _, _)
|
||||
or
|
||||
additionalJumpStep(_, this, _)
|
||||
)
|
||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
(
|
||||
exists(Node mid |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(mid, fromArg, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
apf = node.(AccessPathFrontNilNode).getApf()
|
||||
)
|
||||
or
|
||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
flowCandFwd(mid, fromArg, _, config) and
|
||||
store(mid, f, node) and
|
||||
nodeCand(node, unbind(config)) and
|
||||
readStoreCand(f, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
apf instanceof AccessPathFrontNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flowCand(mid, toReturn, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(node, _, apf, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flowCand(mid, toReturn, nil, config) and
|
||||
apf instanceof AccessPathFrontNil
|
||||
)
|
||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandStore(node, f, toReturn, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCand(f, apf, unbind(config))
|
||||
consCand(f, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, apf0, config) and
|
||||
consCandFwd(f, apf0, unbind(config)) and
|
||||
consCandFwd(f, apf0, config) and
|
||||
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] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* 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
|
||||
* 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.
|
||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
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() {
|
||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
override string toString() {
|
||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||
// 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() {
|
||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||
if len = 2
|
||||
then result = f1.toString() + ", " + f2.toString()
|
||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
||||
(
|
||||
exists(Node mid |
|
||||
flowFwd(mid, fromArg, apf, ap, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(mid, fromArg, _, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
ap = node.(AccessPathNilNode).getAp() and
|
||||
apf = ap.(AccessPathNil).getFront()
|
||||
)
|
||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
ap instanceof AccessPathNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flow(mid, toReturn, ap, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(node, _, _, ap, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flow(mid, toReturn, nil, config) and
|
||||
ap instanceof AccessPathNil
|
||||
)
|
||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
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`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
or
|
||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||
ap = ap0
|
||||
or
|
||||
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||
ap0 instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
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() {
|
||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
if len = 1
|
||||
then result = f.toString()
|
||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2204,16 +2216,19 @@ private module FlowExploration {
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, config) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplCommon::Public
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
@@ -905,30 +905,35 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate localFlowStepPlus(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||
) {
|
||||
localFlowEntry(node1, config) and
|
||||
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
localFlowEntry(node1, config) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -936,11 +941,11 @@ private predicate localFlowStepPlus(
|
||||
* 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.
|
||||
*/
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
||||
(
|
||||
any(Configuration c).isSource(this)
|
||||
or
|
||||
localFlowBigStep(_, this, false, _)
|
||||
localFlowBigStep(_, this, false, _, _)
|
||||
or
|
||||
additionalJumpStep(_, this, _)
|
||||
)
|
||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
(
|
||||
exists(Node mid |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(mid, fromArg, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
apf = node.(AccessPathFrontNilNode).getApf()
|
||||
)
|
||||
or
|
||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
flowCandFwd(mid, fromArg, _, config) and
|
||||
store(mid, f, node) and
|
||||
nodeCand(node, unbind(config)) and
|
||||
readStoreCand(f, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
apf instanceof AccessPathFrontNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flowCand(mid, toReturn, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(node, _, apf, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flowCand(mid, toReturn, nil, config) and
|
||||
apf instanceof AccessPathFrontNil
|
||||
)
|
||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandStore(node, f, toReturn, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCand(f, apf, unbind(config))
|
||||
consCand(f, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, apf0, config) and
|
||||
consCandFwd(f, apf0, unbind(config)) and
|
||||
consCandFwd(f, apf0, config) and
|
||||
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] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* 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
|
||||
* 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.
|
||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
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() {
|
||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
override string toString() {
|
||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||
// 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() {
|
||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||
if len = 2
|
||||
then result = f1.toString() + ", " + f2.toString()
|
||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
||||
(
|
||||
exists(Node mid |
|
||||
flowFwd(mid, fromArg, apf, ap, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(mid, fromArg, _, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
ap = node.(AccessPathNilNode).getAp() and
|
||||
apf = ap.(AccessPathNil).getFront()
|
||||
)
|
||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
ap instanceof AccessPathNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flow(mid, toReturn, ap, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(node, _, _, ap, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flow(mid, toReturn, nil, config) and
|
||||
ap instanceof AccessPathNil
|
||||
)
|
||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
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`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
or
|
||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||
ap = ap0
|
||||
or
|
||||
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||
ap0 instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
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() {
|
||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
if len = 1
|
||||
then result = f.toString()
|
||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2204,16 +2216,19 @@ private module FlowExploration {
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, config) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplCommon::Public
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
@@ -905,30 +905,35 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate localFlowStepPlus(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||
) {
|
||||
localFlowEntry(node1, config) and
|
||||
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
localFlowEntry(node1, config) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -936,11 +941,11 @@ private predicate localFlowStepPlus(
|
||||
* 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.
|
||||
*/
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
||||
(
|
||||
any(Configuration c).isSource(this)
|
||||
or
|
||||
localFlowBigStep(_, this, false, _)
|
||||
localFlowBigStep(_, this, false, _, _)
|
||||
or
|
||||
additionalJumpStep(_, this, _)
|
||||
)
|
||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
(
|
||||
exists(Node mid |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(mid, fromArg, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
apf = node.(AccessPathFrontNilNode).getApf()
|
||||
)
|
||||
or
|
||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
flowCandFwd(mid, fromArg, _, config) and
|
||||
store(mid, f, node) and
|
||||
nodeCand(node, unbind(config)) and
|
||||
readStoreCand(f, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
apf instanceof AccessPathFrontNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flowCand(mid, toReturn, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(node, _, apf, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flowCand(mid, toReturn, nil, config) and
|
||||
apf instanceof AccessPathFrontNil
|
||||
)
|
||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandStore(node, f, toReturn, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCand(f, apf, unbind(config))
|
||||
consCand(f, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, apf0, config) and
|
||||
consCandFwd(f, apf0, unbind(config)) and
|
||||
consCandFwd(f, apf0, config) and
|
||||
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] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* 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
|
||||
* 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.
|
||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
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() {
|
||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
override string toString() {
|
||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||
// 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() {
|
||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||
if len = 2
|
||||
then result = f1.toString() + ", " + f2.toString()
|
||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
||||
(
|
||||
exists(Node mid |
|
||||
flowFwd(mid, fromArg, apf, ap, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(mid, fromArg, _, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
ap = node.(AccessPathNilNode).getAp() and
|
||||
apf = ap.(AccessPathNil).getFront()
|
||||
)
|
||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
ap instanceof AccessPathNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flow(mid, toReturn, ap, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(node, _, _, ap, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flow(mid, toReturn, nil, config) and
|
||||
ap instanceof AccessPathNil
|
||||
)
|
||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
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`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
or
|
||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||
ap = ap0
|
||||
or
|
||||
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||
ap0 instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
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() {
|
||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
if len = 1
|
||||
then result = f.toString()
|
||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2204,16 +2216,19 @@ private module FlowExploration {
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, config) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* on each other without introducing mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplCommon::Public
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
|
||||
@@ -905,30 +905,35 @@ private predicate localFlowExit(Node node, Configuration config) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate localFlowStepPlus(
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config
|
||||
Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
|
||||
) {
|
||||
localFlowEntry(node1, config) and
|
||||
not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
localFlowEntry(node1, config) and
|
||||
(
|
||||
localFlowStep(node1, node2, config) and preservesValue = true
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
cc.relevantFor(node1.getEnclosingCallable()) and
|
||||
not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
additionalLocalFlowStep(node1, node2, config) and preservesValue = false
|
||||
) and
|
||||
node1 != node2 and
|
||||
nodeCand(node2, unbind(config))
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, preservesValue, config, cc) and
|
||||
localFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowStepPlus(node1, mid, _, config, cc) and
|
||||
additionalLocalFlowStep(mid, node2, config) and
|
||||
not mid instanceof CastNode and
|
||||
preservesValue = false and
|
||||
nodeCand(node2, unbind(config))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -936,11 +941,11 @@ private predicate localFlowStepPlus(
|
||||
* 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.
|
||||
*/
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1000,7 +1005,7 @@ private class AccessPathFrontNilNode extends Node {
|
||||
(
|
||||
any(Configuration c).isSource(this)
|
||||
or
|
||||
localFlowBigStep(_, this, false, _)
|
||||
localFlowBigStep(_, this, false, _, _)
|
||||
or
|
||||
additionalJumpStep(_, this, _)
|
||||
)
|
||||
@@ -1023,12 +1028,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
(
|
||||
exists(Node mid |
|
||||
flowCandFwd(mid, fromArg, apf, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(mid, fromArg, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
apf = node.(AccessPathFrontNilNode).getApf()
|
||||
)
|
||||
or
|
||||
@@ -1075,6 +1080,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
|
||||
flowCandFwd(mid, fromArg, _, config) and
|
||||
store(mid, f, node) and
|
||||
nodeCand(node, unbind(config)) and
|
||||
readStoreCand(f, unbind(config)) and
|
||||
apf.headUsesContent(f)
|
||||
)
|
||||
or
|
||||
@@ -1121,13 +1127,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
apf instanceof AccessPathFrontNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flowCand(mid, toReturn, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathFrontNil nil |
|
||||
flowCandFwd(node, _, apf, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flowCand(mid, toReturn, nil, config) and
|
||||
apf instanceof AccessPathFrontNil
|
||||
)
|
||||
@@ -1175,12 +1181,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandStore(node, f, toReturn, apf0, config) and
|
||||
apf0.headUsesContent(f) and
|
||||
consCand(f, apf, unbind(config))
|
||||
consCand(f, apf, config)
|
||||
)
|
||||
or
|
||||
exists(Content f, AccessPathFront apf0 |
|
||||
flowCandRead(node, f, toReturn, apf0, config) and
|
||||
consCandFwd(f, apf0, unbind(config)) and
|
||||
consCandFwd(f, apf0, config) and
|
||||
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] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first
|
||||
* element of the list and its length are tracked. If data flows from a source to
|
||||
* Conceptually a list of `Content`s followed by a `Type`, but only the first two
|
||||
* 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
|
||||
* 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.
|
||||
@@ -1260,7 +1266,7 @@ abstract private class AccessPath extends TAccessPath {
|
||||
|
||||
private class AccessPathNil extends AccessPath, TNil {
|
||||
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() {
|
||||
@@ -1276,7 +1282,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
|
||||
override string toString() {
|
||||
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
|
||||
// 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() {
|
||||
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
|
||||
if len = 2
|
||||
then result = f1.toString() + ", " + f2.toString()
|
||||
else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f1.toString() + ", " + f2.toString() + "]"
|
||||
else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1362,12 +1368,12 @@ private predicate flowFwd0(
|
||||
(
|
||||
exists(Node mid |
|
||||
flowFwd(mid, fromArg, apf, ap, config) and
|
||||
localFlowBigStep(mid, node, true, config)
|
||||
localFlowBigStep(mid, node, true, config, _)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(mid, fromArg, _, nil, config) and
|
||||
localFlowBigStep(mid, node, false, config) and
|
||||
localFlowBigStep(mid, node, false, config, _) and
|
||||
ap = node.(AccessPathNilNode).getAp() and
|
||||
apf = ap.(AccessPathNil).getFront()
|
||||
)
|
||||
@@ -1471,13 +1477,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
|
||||
ap instanceof AccessPathNil
|
||||
or
|
||||
exists(Node mid |
|
||||
localFlowBigStep(node, mid, true, config) and
|
||||
localFlowBigStep(node, mid, true, config, _) and
|
||||
flow(mid, toReturn, ap, config)
|
||||
)
|
||||
or
|
||||
exists(Node mid, AccessPathNil nil |
|
||||
flowFwd(node, _, _, ap, config) and
|
||||
localFlowBigStep(node, mid, false, config) and
|
||||
localFlowBigStep(node, mid, false, config, _) and
|
||||
flow(mid, toReturn, nil, config) and
|
||||
ap instanceof AccessPathNil
|
||||
)
|
||||
@@ -1625,7 +1631,7 @@ abstract class PathNode extends TPathNode {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
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`.
|
||||
*/
|
||||
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
|
||||
localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp()
|
||||
or
|
||||
localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
|
||||
midnode = mid.getNode() and
|
||||
conf = mid.getConfiguration() and
|
||||
cc = mid.getCallContext() and
|
||||
localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
|
||||
ap0 = mid.getAp()
|
||||
|
|
||||
localFlowBigStep(midnode, node, true, conf, localCC) and
|
||||
ap = ap0
|
||||
or
|
||||
localFlowBigStep(midnode, node, false, conf, localCC) and
|
||||
ap0 instanceof AccessPathNil and
|
||||
ap = node.(AccessPathNilNode).getAp()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -1879,7 +1891,7 @@ private predicate pathIntoCallable(
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
@@ -2070,7 +2082,7 @@ private module FlowExploration {
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
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() {
|
||||
@@ -2082,8 +2094,8 @@ private module FlowExploration {
|
||||
override string toString() {
|
||||
exists(Content f, int len | this = TPartialCons(f, len) |
|
||||
if len = 1
|
||||
then result = f.toString()
|
||||
else result = f.toString() + ", ... (" + len.toString() + ")"
|
||||
then result = "[" + f.toString() + "]"
|
||||
else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2160,7 +2172,7 @@ private module FlowExploration {
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " [" + s + "]"
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2204,16 +2216,19 @@ private module FlowExploration {
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
(
|
||||
localFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(mid.getNode(), node, config) and
|
||||
cc = mid.getCallContext() and
|
||||
mid.getAp() instanceof PartialAccessPathNil and
|
||||
ap = TPartialNil(getErasedRepr(node.getType())) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
jumpStep(mid.getNode(), node, config) and
|
||||
cc instanceof CallContextAny and
|
||||
@@ -2377,7 +2392,7 @@ private module FlowExploration {
|
||||
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
|
||||
p.isParameterOf(callable, i)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, emptyAp)
|
||||
else innercc = TSomeCall(p, emptyAp)
|
||||
)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -204,3 +204,5 @@ class DataFlowCall extends CallInstruction {
|
||||
|
||||
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
|
||||
}
|
||||
|
||||
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
|
||||
|
||||
247
cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll
Normal file
247
cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll
Normal file
@@ -0,0 +1,247 @@
|
||||
/**
|
||||
* Minimal, language-neutral type system for the IR.
|
||||
*/
|
||||
|
||||
private import internal.IRTypeInternal
|
||||
|
||||
private newtype TIRType =
|
||||
TIRVoidType() or
|
||||
TIRUnknownType() or
|
||||
TIRErrorType() { Language::hasErrorType() } or
|
||||
TIRBooleanType(int byteSize) { Language::hasBooleanType(byteSize) } or
|
||||
TIRSignedIntegerType(int byteSize) { Language::hasSignedIntegerType(byteSize) } or
|
||||
TIRUnsignedIntegerType(int byteSize) { Language::hasUnsignedIntegerType(byteSize) } or
|
||||
TIRFloatingPointType(int byteSize) { Language::hasFloatingPointType(byteSize) } or
|
||||
TIRAddressType(int byteSize) { Language::hasAddressType(byteSize) } or
|
||||
TIRFunctionAddressType(int byteSize) { Language::hasFunctionAddressType(byteSize) } or
|
||||
TIROpaqueType(Language::OpaqueTypeTag tag, int byteSize) {
|
||||
Language::hasOpaqueType(tag, byteSize)
|
||||
}
|
||||
|
||||
/**
|
||||
* The language-neutral type of an IR `Instruction`, `Operand`, or `IRVariable`.
|
||||
* The interface to `IRType` and its subclasses is the same across all languages for which the IR
|
||||
* is supported, so analyses that expect to be used for multiple languages should generally use
|
||||
* `IRType` rather than a language-specific type.
|
||||
*
|
||||
* Many types from the language-specific type system will map to a single canonical `IRType`. Two
|
||||
* types that map to the same `IRType` are considered equivalent by the IR. As an example, in C++,
|
||||
* all pointer types map to the same instance of `IRAddressType`.
|
||||
*/
|
||||
class IRType extends TIRType {
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets a string that uniquely identifies this `IRType`. This string is often the same as the
|
||||
* result of `IRType.toString()`, but for some types it may be more verbose to ensure uniqueness.
|
||||
*/
|
||||
string getIdentityString() { result = toString() }
|
||||
|
||||
/**
|
||||
* Gets the size of the type, in bytes, if known.
|
||||
*
|
||||
* This will hold for all `IRType` objects except `IRUnknownType`.
|
||||
*/
|
||||
int getByteSize() { none() }
|
||||
|
||||
/**
|
||||
* Gets a single instance of `LanguageType` that maps to this `IRType`.
|
||||
*/
|
||||
Language::LanguageType getCanonicalLanguageType() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An unknown type. Generally used to represent results and operands that access an unknown set of
|
||||
* memory locations, such as the side effects of a function call.
|
||||
*/
|
||||
class IRUnknownType extends IRType, TIRUnknownType {
|
||||
final override string toString() { result = "unknown" }
|
||||
|
||||
final override int getByteSize() { none() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalUnknownType()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A void type, which has no values. Used to represent the result type of an instruction that does
|
||||
* not produce a result.
|
||||
*/
|
||||
class IRVoidType extends IRType, TIRVoidType {
|
||||
final override string toString() { result = "void" }
|
||||
|
||||
final override int getByteSize() { result = 0 }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalVoidType()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An error type. Used when an error in the source code prevents the extractor from determining the
|
||||
* proper type.
|
||||
*/
|
||||
class IRErrorType extends IRType, TIRErrorType {
|
||||
final override string toString() { result = "error" }
|
||||
|
||||
final override int getByteSize() { result = 0 }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalErrorType()
|
||||
}
|
||||
}
|
||||
|
||||
private class IRSizedType extends IRType {
|
||||
int byteSize;
|
||||
|
||||
IRSizedType() {
|
||||
this = TIRBooleanType(byteSize) or
|
||||
this = TIRSignedIntegerType(byteSize) or
|
||||
this = TIRUnsignedIntegerType(byteSize) or
|
||||
this = TIRFloatingPointType(byteSize) or
|
||||
this = TIRAddressType(byteSize) or
|
||||
this = TIRFunctionAddressType(byteSize) or
|
||||
this = TIROpaqueType(_, byteSize)
|
||||
}
|
||||
|
||||
final override int getByteSize() { result = byteSize }
|
||||
}
|
||||
|
||||
/**
|
||||
* A Boolean type, which can hold the values `true` (non-zero) or `false` (zero).
|
||||
*/
|
||||
class IRBooleanType extends IRSizedType, TIRBooleanType {
|
||||
final override string toString() { result = "bool" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalBooleanType(byteSize)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A numberic type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and
|
||||
* `IRFloatingPointType`.
|
||||
*/
|
||||
class IRNumericType extends IRSizedType {
|
||||
IRNumericType() {
|
||||
this = TIRSignedIntegerType(byteSize) or
|
||||
this = TIRUnsignedIntegerType(byteSize) or
|
||||
this = TIRFloatingPointType(byteSize)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A signed two's-complement integer. Also used to represent enums whose underlying type is a signed
|
||||
* integer, as well as character types whose representation is signed.
|
||||
*/
|
||||
class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType {
|
||||
final override string toString() { result = "int" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalSignedIntegerType(byteSize)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An unsigned two's-complement integer. Also used to represent enums whose underlying type is an
|
||||
* unsigned integer, as well as character types whose representation is unsigned.
|
||||
*/
|
||||
class IRUnsignedIntegerType extends IRNumericType, TIRUnsignedIntegerType {
|
||||
final override string toString() { result = "uint" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalUnsignedIntegerType(byteSize)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A floating-point type.
|
||||
*/
|
||||
class IRFloatingPointType extends IRNumericType, TIRFloatingPointType {
|
||||
final override string toString() { result = "float" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalFloatingPointType(byteSize)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An address type, representing the memory address of data. Used to represent pointers, references,
|
||||
* and lvalues, include those that are garbage collected.
|
||||
*
|
||||
* The address of a function is represented by the separate `IRFunctionAddressType`.
|
||||
*/
|
||||
class IRAddressType extends IRSizedType, TIRAddressType {
|
||||
final override string toString() { result = "addr" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalAddressType(byteSize)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An address type, representing the memory address of code. Used to represent function pointers,
|
||||
* function references, and the target of a direct function call.
|
||||
*/
|
||||
class IRFunctionAddressType extends IRSizedType, TIRFunctionAddressType {
|
||||
final override string toString() { result = "func" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalFunctionAddressType(byteSize)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A type with known size that does not fit any of the other kinds of type. Used to represent
|
||||
* classes, structs, unions, fixed-size arrays, pointers-to-member, and more.
|
||||
*/
|
||||
class IROpaqueType extends IRSizedType, TIROpaqueType {
|
||||
Language::OpaqueTypeTag tag;
|
||||
|
||||
IROpaqueType() { this = TIROpaqueType(tag, byteSize) }
|
||||
|
||||
final override string toString() {
|
||||
result = "opaque" + byteSize.toString() + "{" + tag.toString() + "}"
|
||||
}
|
||||
|
||||
final override string getIdentityString() {
|
||||
result = "opaque" + byteSize.toString() + "{" + Language::getOpaqueTagIdentityString(tag) + "}"
|
||||
}
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalOpaqueType(tag, byteSize)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the "tag" that differentiates this type from other incompatible opaque types that have the
|
||||
* same size.
|
||||
*/
|
||||
final Language::OpaqueTypeTag getTag() { result = tag }
|
||||
}
|
||||
|
||||
module IRTypeSanity {
|
||||
query predicate missingCanonicalLanguageType(IRType type, string message) {
|
||||
not exists(type.getCanonicalLanguageType()) and
|
||||
message = "Type does not have a canonical `LanguageType`"
|
||||
}
|
||||
|
||||
query predicate multipleCanonicalLanguageTypes(IRType type, string message) {
|
||||
strictcount(type.getCanonicalLanguageType()) > 1 and
|
||||
message = "Type has multiple canonical `LanguageType`s: " +
|
||||
concat(type.getCanonicalLanguageType().toString(), ", ")
|
||||
}
|
||||
|
||||
query predicate missingIRType(Language::LanguageType type, string message) {
|
||||
not exists(type.getIRType()) and
|
||||
message = "`LanguageType` does not have a corresponding `IRType`."
|
||||
}
|
||||
|
||||
query predicate multipleIRTypes(Language::LanguageType type, string message) {
|
||||
strictcount(type.getIRType()) > 1 and
|
||||
message = "`LanguageType` " + type.getAQlClass() + " has multiple `IRType`s: " +
|
||||
concat(type.getIRType().toString(), ", ")
|
||||
}
|
||||
|
||||
import Language::LanguageTypeSanity
|
||||
}
|
||||
@@ -66,11 +66,14 @@ private newtype TOpcode =
|
||||
TCallSideEffect() or
|
||||
TCallReadSideEffect() or
|
||||
TIndirectReadSideEffect() or
|
||||
TIndirectWriteSideEffect() or
|
||||
TIndirectMustWriteSideEffect() or
|
||||
TIndirectMayWriteSideEffect() or
|
||||
TBufferReadSideEffect() or
|
||||
TBufferWriteSideEffect() or
|
||||
TBufferMustWriteSideEffect() or
|
||||
TBufferMayWriteSideEffect() or
|
||||
TSizedBufferReadSideEffect() or
|
||||
TSizedBufferMustWriteSideEffect() or
|
||||
TSizedBufferMayWriteSideEffect() or
|
||||
TChi() or
|
||||
TInlineAsm() or
|
||||
TUnreached() or
|
||||
@@ -135,17 +138,28 @@ abstract class ReadSideEffectOpcode 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
|
||||
* 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 { }
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
class NoOp extends Opcode, TNoOp {
|
||||
final override string toString() { result = "NoOp" }
|
||||
@@ -416,9 +430,9 @@ module Opcode {
|
||||
final override string toString() { result = "IndirectReadSideEffect" }
|
||||
}
|
||||
|
||||
class IndirectWriteSideEffect extends WriteSideEffectOpcode, MemoryAccessOpcode,
|
||||
TIndirectWriteSideEffect {
|
||||
final override string toString() { result = "IndirectWriteSideEffect" }
|
||||
class IndirectMustWriteSideEffect extends MustWriteSideEffectOpcode, MemoryAccessOpcode,
|
||||
TIndirectMustWriteSideEffect {
|
||||
final override string toString() { result = "IndirectMustWriteSideEffect" }
|
||||
}
|
||||
|
||||
class IndirectMayWriteSideEffect extends MayWriteSideEffectOpcode, MemoryAccessOpcode,
|
||||
@@ -430,9 +444,9 @@ module Opcode {
|
||||
final override string toString() { result = "BufferReadSideEffect" }
|
||||
}
|
||||
|
||||
class BufferWriteSideEffect extends WriteSideEffectOpcode, BufferAccessOpcode,
|
||||
TBufferWriteSideEffect {
|
||||
final override string toString() { result = "BufferWriteSideEffect" }
|
||||
class BufferMustWriteSideEffect extends MustWriteSideEffectOpcode, BufferAccessOpcode,
|
||||
TBufferMustWriteSideEffect {
|
||||
final override string toString() { result = "BufferMustWriteSideEffect" }
|
||||
}
|
||||
|
||||
class BufferMayWriteSideEffect extends MayWriteSideEffectOpcode, BufferAccessOpcode,
|
||||
@@ -440,6 +454,21 @@ module Opcode {
|
||||
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 {
|
||||
final override string toString() { result = "Chi" }
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import IRVariable
|
||||
import Operand
|
||||
private import internal.IRImports as Imports
|
||||
import Imports::EdgeKind
|
||||
import Imports::IRType
|
||||
import Imports::MemoryAccessKind
|
||||
|
||||
private newtype TIRPropertyProvider = MkIRPropertyProvider()
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
private import IR
|
||||
import InstructionSanity
|
||||
import IRTypeSanity
|
||||
|
||||
@@ -5,6 +5,7 @@ import Imports::TempVariableTag
|
||||
private import Imports::IRUtilities
|
||||
private import Imports::TTempVariableTag
|
||||
private import Imports::TIRVariable
|
||||
private import Imports::IRType
|
||||
|
||||
IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) {
|
||||
result.getVariable() = var and
|
||||
@@ -24,7 +25,17 @@ abstract class IRVariable extends TIRVariable {
|
||||
/**
|
||||
* Gets the type of the variable.
|
||||
*/
|
||||
abstract Language::Type getType();
|
||||
final Language::Type getType() { getLanguageType().hasType(result, false) }
|
||||
|
||||
/**
|
||||
* Gets the language-neutral type of the variable.
|
||||
*/
|
||||
final IRType getIRType() { result = getLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
* Gets the type of the variable.
|
||||
*/
|
||||
abstract Language::LanguageType getLanguageType();
|
||||
|
||||
/**
|
||||
* Gets the AST node that declared this variable, or that introduced this
|
||||
@@ -59,7 +70,7 @@ abstract class IRVariable extends TIRVariable {
|
||||
*/
|
||||
class IRUserVariable extends IRVariable, TIRUserVariable {
|
||||
Language::Variable var;
|
||||
Language::Type type;
|
||||
Language::LanguageType type;
|
||||
|
||||
IRUserVariable() { this = TIRUserVariable(var, type, func) }
|
||||
|
||||
@@ -71,7 +82,7 @@ class IRUserVariable extends IRVariable, TIRUserVariable {
|
||||
result = getVariable().toString() + " " + getVariable().getLocation().toString()
|
||||
}
|
||||
|
||||
final override Language::Type getType() { result = type }
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
|
||||
/**
|
||||
* Gets the original user-declared variable.
|
||||
@@ -110,11 +121,11 @@ IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) {
|
||||
class IRTempVariable extends IRVariable, IRAutomaticVariable, TIRTempVariable {
|
||||
Language::AST ast;
|
||||
TempVariableTag tag;
|
||||
Language::Type type;
|
||||
Language::LanguageType type;
|
||||
|
||||
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
|
||||
|
||||
final override Language::Type getType() { result = type }
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
|
||||
final override Language::AST getAST() { result = ast }
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import IRVariable
|
||||
import Operand
|
||||
private import internal.InstructionImports as Imports
|
||||
import Imports::EdgeKind
|
||||
import Imports::IRType
|
||||
import Imports::MemoryAccessKind
|
||||
import Imports::Opcode
|
||||
private import Imports::OperandTag
|
||||
@@ -30,7 +31,7 @@ module InstructionSanity {
|
||||
or
|
||||
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag
|
||||
or
|
||||
opcode instanceof BufferAccessOpcode and tag instanceof BufferSizeOperand
|
||||
opcode instanceof SizedBufferAccessOpcode and tag instanceof BufferSizeOperandTag
|
||||
or
|
||||
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag
|
||||
or
|
||||
@@ -48,8 +49,8 @@ module InstructionSanity {
|
||||
or
|
||||
(
|
||||
opcode instanceof ReadSideEffectOpcode or
|
||||
opcode instanceof MayWriteSideEffectOpcode or
|
||||
opcode instanceof Opcode::InlineAsm
|
||||
opcode instanceof Opcode::InlineAsm or
|
||||
opcode instanceof Opcode::CallSideEffect
|
||||
) and
|
||||
tag instanceof SideEffectOperandTag
|
||||
)
|
||||
@@ -113,13 +114,24 @@ module InstructionSanity {
|
||||
}
|
||||
|
||||
query predicate missingOperandType(Operand operand, string message) {
|
||||
exists(Language::Function func |
|
||||
exists(Language::Function func, Instruction use |
|
||||
not exists(operand.getType()) and
|
||||
func = operand.getUse().getEnclosingFunction() and
|
||||
message = "Operand missing type in function '" + Language::getIdentityString(func) + "'."
|
||||
use = operand.getUse() and
|
||||
func = use.getEnclosingFunction() and
|
||||
message = "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString()
|
||||
+ "' missing type in function '" + Language::getIdentityString(func) + "'."
|
||||
)
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
@@ -312,7 +324,7 @@ class Instruction extends Construction::TInstruction {
|
||||
}
|
||||
|
||||
private string getResultPrefix() {
|
||||
if getResultType() instanceof Language::VoidType
|
||||
if getResultIRType() instanceof IRVoidType
|
||||
then result = "v"
|
||||
else
|
||||
if hasMemoryResult()
|
||||
@@ -344,23 +356,6 @@ class Instruction extends Construction::TInstruction {
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[type]
|
||||
private string getValueCategoryString(string type) {
|
||||
if isGLValue() then result = "glval<" + type + ">" else result = type
|
||||
}
|
||||
|
||||
string getResultTypeString() {
|
||||
exists(string valcat |
|
||||
valcat = getValueCategoryString(getResultType().toString()) and
|
||||
if
|
||||
getResultType() instanceof Language::UnknownType and
|
||||
not isGLValue() and
|
||||
exists(getResultSize())
|
||||
then result = valcat + "[" + getResultSize().toString() + "]"
|
||||
else result = valcat
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a human-readable string that uniquely identifies this instruction
|
||||
* within the function. This string is used to refer to this instruction when
|
||||
@@ -380,7 +375,9 @@ class Instruction extends Construction::TInstruction {
|
||||
*
|
||||
* Example: `r1_1(int*)`
|
||||
*/
|
||||
final string getResultString() { result = getResultId() + "(" + getResultTypeString() + ")" }
|
||||
final string getResultString() {
|
||||
result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string describing the operands of this instruction, suitable for
|
||||
@@ -448,6 +445,16 @@ class Instruction extends Construction::TInstruction {
|
||||
result = Construction::getInstructionUnconvertedResultExpression(this)
|
||||
}
|
||||
|
||||
final Language::LanguageType getResultLanguageType() {
|
||||
result = Construction::getInstructionResultType(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the instruction does not produce
|
||||
* a result, its result type will be `IRVoidType`.
|
||||
*/
|
||||
final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the
|
||||
* instruction does not produce a result, its result type will be `VoidType`.
|
||||
@@ -455,7 +462,16 @@ class Instruction extends Construction::TInstruction {
|
||||
* If `isGLValue()` holds, then the result type of this instruction should be
|
||||
* thought of as "pointer to `getResultType()`".
|
||||
*/
|
||||
final Language::Type getResultType() { Construction::instructionHasType(this, result, _) }
|
||||
final Language::Type getResultType() {
|
||||
exists(Language::LanguageType resultType |
|
||||
resultType = getResultLanguageType() and
|
||||
(
|
||||
resultType.hasUnspecifiedType(result, _)
|
||||
or
|
||||
not resultType.hasUnspecifiedType(_, _) and result instanceof Language::UnknownType
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the result produced by this instruction is a glvalue. If this
|
||||
@@ -475,7 +491,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* result of the `Load` instruction is a prvalue of type `int`, representing
|
||||
* the integer value loaded from variable `x`.
|
||||
*/
|
||||
final predicate isGLValue() { Construction::instructionHasType(this, _, true) }
|
||||
final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
|
||||
|
||||
/**
|
||||
* Gets the size of the result produced by this instruction, in bytes. If the
|
||||
@@ -484,16 +500,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* If `this.isGLValue()` holds for this instruction, the value of
|
||||
* `getResultSize()` will always be the size of a pointer.
|
||||
*/
|
||||
final int getResultSize() {
|
||||
if isGLValue()
|
||||
then
|
||||
// a glvalue is always pointer-sized.
|
||||
result = Language::getPointerSize()
|
||||
else
|
||||
if getResultType() instanceof Language::UnknownType
|
||||
then result = Construction::getInstructionResultSize(this)
|
||||
else result = Language::getTypeSize(getResultType())
|
||||
}
|
||||
final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
|
||||
|
||||
/**
|
||||
* Gets the opcode that specifies the operation performed by this instruction.
|
||||
@@ -609,9 +616,14 @@ class VariableInstruction extends Instruction {
|
||||
|
||||
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 {
|
||||
@@ -644,6 +656,16 @@ class ConstantValueInstruction extends Instruction {
|
||||
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 {
|
||||
EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction }
|
||||
}
|
||||
@@ -1175,6 +1197,8 @@ class CallReadSideEffectInstruction extends SideEffectInstruction {
|
||||
*/
|
||||
class IndirectReadSideEffectInstruction extends SideEffectInstruction {
|
||||
IndirectReadSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectReadSideEffect }
|
||||
|
||||
Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1182,13 +1206,39 @@ class IndirectReadSideEffectInstruction extends SideEffectInstruction {
|
||||
*/
|
||||
class BufferReadSideEffectInstruction extends SideEffectInstruction {
|
||||
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.
|
||||
*/
|
||||
class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
IndirectWriteSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectWriteSideEffect }
|
||||
class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
IndirectMustWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::IndirectMustWriteSideEffect
|
||||
}
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
|
||||
}
|
||||
@@ -1197,18 +1247,34 @@ class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
* An instruction representing the write of an indirect buffer parameter within a function call. The
|
||||
* entire buffer is overwritten.
|
||||
*/
|
||||
class BufferWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
BufferWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferWriteSideEffect }
|
||||
class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
BufferMustWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::BufferMustWriteSideEffect
|
||||
}
|
||||
|
||||
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.
|
||||
* Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten.
|
||||
* written.
|
||||
*/
|
||||
class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
IndirectMayWriteSideEffectInstruction() {
|
||||
getOpcode() instanceof Opcode::IndirectMayWriteSideEffect
|
||||
}
|
||||
@@ -1222,7 +1288,7 @@ class IndirectMayWriteSideEffectInstruction 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 BufferMayWriteSideEffectInstruction extends SideEffectInstruction {
|
||||
class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
|
||||
BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect }
|
||||
|
||||
final override MemoryAccessKind getResultMemoryAccess() {
|
||||
@@ -1230,6 +1296,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.
|
||||
*/
|
||||
@@ -1300,7 +1382,7 @@ class CatchInstruction extends Instruction {
|
||||
* An instruction that catches an exception of a specific type.
|
||||
*/
|
||||
class CatchByTypeInstruction extends CatchInstruction {
|
||||
Language::Type exceptionType;
|
||||
Language::LanguageType exceptionType;
|
||||
|
||||
CatchByTypeInstruction() {
|
||||
getOpcode() instanceof Opcode::CatchByType and
|
||||
@@ -1312,7 +1394,7 @@ class CatchByTypeInstruction extends CatchInstruction {
|
||||
/**
|
||||
* Gets the type of exception to be caught.
|
||||
*/
|
||||
final Language::Type getExceptionType() { result = exceptionType }
|
||||
final Language::LanguageType getExceptionType() { result = exceptionType }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
import IRBlock
|
||||
private import Instruction
|
||||
private import IRBlock
|
||||
private import internal.OperandImports as Imports
|
||||
import Imports::MemoryAccessKind
|
||||
import Imports::Overlap
|
||||
private import Imports::MemoryAccessKind
|
||||
private import Imports::IRType
|
||||
private import Imports::Overlap
|
||||
private import Imports::OperandTag
|
||||
|
||||
cached
|
||||
@@ -143,22 +144,40 @@ class Operand extends TOperand {
|
||||
* the definition type, such as in the case of a partial read or a read from a pointer that
|
||||
* has been cast to a different type.
|
||||
*/
|
||||
Language::Type getType() { result = getAnyDef().getResultType() }
|
||||
Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() }
|
||||
|
||||
/**
|
||||
* Gets the language-neutral type of the value consumed by this operand. This is usually the same
|
||||
* as the result type of the definition instruction consumed by this operand. For register
|
||||
* operands, this is always the case. For some memory operands, the operand type may be different
|
||||
* from the definition type, such as in the case of a partial read or a read from a pointer that
|
||||
* has been cast to a different type.
|
||||
*/
|
||||
final IRType getIRType() { result = getLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
* Gets the type of the value consumed by this operand. This is usually the same as the
|
||||
* result type of the definition instruction consumed by this operand. For register operands,
|
||||
* this is always the case. For some memory operands, the operand type may be different from
|
||||
* the definition type, such as in the case of a partial read or a read from a pointer that
|
||||
* has been cast to a different type.
|
||||
*/
|
||||
final Language::Type getType() { getLanguageType().hasType(result, _) }
|
||||
|
||||
/**
|
||||
* Holds if the value consumed by this operand is a glvalue. If this
|
||||
* holds, the value of the operand represents the address of a location,
|
||||
* and the type of the location is given by `getType()`. If this does
|
||||
* not hold, the value of the operand represents a value whose type is
|
||||
* given by `getResultType()`.
|
||||
* given by `getType()`.
|
||||
*/
|
||||
predicate isGLValue() { getAnyDef().isGLValue() }
|
||||
final predicate isGLValue() { getLanguageType().hasType(_, true) }
|
||||
|
||||
/**
|
||||
* Gets the size of the value consumed by this operand, in bytes. If the operand does not have
|
||||
* a known constant size, this predicate does not hold.
|
||||
*/
|
||||
int getSize() { result = Language::getTypeSize(getType()) }
|
||||
final int getSize() { result = getLanguageType().getByteSize() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,11 +189,6 @@ class MemoryOperand extends Operand {
|
||||
this = TPhiOperand(_, _, _, _)
|
||||
}
|
||||
|
||||
override predicate isGLValue() {
|
||||
// A `MemoryOperand` can never be a glvalue
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the kind of memory access performed by the operand.
|
||||
*/
|
||||
@@ -239,7 +253,7 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOpe
|
||||
class TypedOperand extends NonPhiMemoryOperand {
|
||||
override TypedOperandTag tag;
|
||||
|
||||
final override Language::Type getType() {
|
||||
final override Language::LanguageType getLanguageType() {
|
||||
result = Construction::getInstructionOperandType(useInstr, tag)
|
||||
}
|
||||
}
|
||||
@@ -254,6 +268,16 @@ class AddressOperand extends RegisterOperand {
|
||||
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`,
|
||||
* `ReturnValue`, `ThrowValue`).
|
||||
@@ -371,12 +395,6 @@ class PositionalArgumentOperand extends ArgumentOperand {
|
||||
class SideEffectOperand extends TypedOperand {
|
||||
override SideEffectOperandTag tag;
|
||||
|
||||
final override int getSize() {
|
||||
if getType() instanceof Language::UnknownType
|
||||
then result = Construction::getInstructionOperandSize(useInstr, tag)
|
||||
else result = Language::getTypeSize(getType())
|
||||
}
|
||||
|
||||
override MemoryAccessKind getMemoryAccess() {
|
||||
useInstr instanceof CallSideEffectInstruction and
|
||||
result instanceof EscapedMayMemoryAccess
|
||||
@@ -390,10 +408,10 @@ class SideEffectOperand extends TypedOperand {
|
||||
useInstr instanceof BufferReadSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
useInstr instanceof IndirectWriteSideEffectInstruction and
|
||||
useInstr instanceof IndirectMustWriteSideEffectInstruction and
|
||||
result instanceof IndirectMemoryAccess
|
||||
or
|
||||
useInstr instanceof BufferWriteSideEffectInstruction and
|
||||
useInstr instanceof BufferMustWriteSideEffectInstruction and
|
||||
result instanceof BufferMemoryAccess
|
||||
or
|
||||
useInstr instanceof IndirectMayWriteSideEffectInstruction and
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
private import internal.ValueNumberingInternal
|
||||
private import cpp
|
||||
private import internal.ValueNumberingImports
|
||||
private import IR
|
||||
|
||||
/**
|
||||
@@ -23,31 +23,32 @@ newtype TValueNumber =
|
||||
initializeParameterValueNumber(_, irFunc, var)
|
||||
} or
|
||||
TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or
|
||||
TConstantValueNumber(IRFunction irFunc, Type type, string value) {
|
||||
TConstantValueNumber(IRFunction irFunc, IRType type, string value) {
|
||||
constantValueNumber(_, irFunc, type, value)
|
||||
} or
|
||||
TStringConstantValueNumber(IRFunction irFunc, Type type, string value) {
|
||||
TStringConstantValueNumber(IRFunction irFunc, IRType type, string value) {
|
||||
stringConstantValueNumber(_, irFunc, type, value)
|
||||
} or
|
||||
TFieldAddressValueNumber(IRFunction irFunc, Field field, ValueNumber objectAddress) {
|
||||
TFieldAddressValueNumber(IRFunction irFunc, Language::Field field, ValueNumber objectAddress) {
|
||||
fieldAddressValueNumber(_, irFunc, field, objectAddress)
|
||||
} or
|
||||
TBinaryValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand
|
||||
IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand
|
||||
) {
|
||||
binaryValueNumber(_, irFunc, opcode, type, leftOperand, rightOperand)
|
||||
} or
|
||||
TPointerArithmeticValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, Type type, int elementSize, ValueNumber leftOperand,
|
||||
IRFunction irFunc, Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand
|
||||
) {
|
||||
pointerArithmeticValueNumber(_, irFunc, opcode, type, elementSize, leftOperand, rightOperand)
|
||||
} or
|
||||
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand) {
|
||||
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand) {
|
||||
unaryValueNumber(_, irFunc, opcode, type, operand)
|
||||
} or
|
||||
TInheritanceConversionValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand
|
||||
IRFunction irFunc, Opcode opcode, Language::Class baseClass, Language::Class derivedClass,
|
||||
ValueNumber operand
|
||||
) {
|
||||
inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand)
|
||||
} or
|
||||
@@ -59,7 +60,7 @@ newtype TValueNumber =
|
||||
class ValueNumber extends TValueNumber {
|
||||
final string toString() { result = getExampleInstruction().getResultId() }
|
||||
|
||||
final Location getLocation() { result = getExampleInstruction().getLocation() }
|
||||
final Language::Location getLocation() { result = getExampleInstruction().getLocation() }
|
||||
|
||||
/**
|
||||
* Gets the instructions that have been assigned this value number. This will always produce at
|
||||
@@ -135,14 +136,14 @@ private predicate variableAddressValueNumber(
|
||||
VariableAddressInstruction instr, IRFunction irFunc, IRVariable var
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getVariable() = var
|
||||
instr.getIRVariable() = var
|
||||
}
|
||||
|
||||
private predicate initializeParameterValueNumber(
|
||||
InitializeParameterInstruction instr, IRFunction irFunc, IRVariable var
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getVariable() = var
|
||||
instr.getIRVariable() = var
|
||||
}
|
||||
|
||||
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
|
||||
@@ -150,23 +151,23 @@ private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRF
|
||||
}
|
||||
|
||||
private predicate constantValueNumber(
|
||||
ConstantInstruction instr, IRFunction irFunc, Type type, string value
|
||||
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getResultType() = type and
|
||||
instr.getResultIRType() = type and
|
||||
instr.getValue() = value
|
||||
}
|
||||
|
||||
private predicate stringConstantValueNumber(
|
||||
StringConstantInstruction instr, IRFunction irFunc, Type type, string value
|
||||
StringConstantInstruction instr, IRFunction irFunc, IRType type, string value
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getResultType() = type and
|
||||
instr.getResultIRType() = type and
|
||||
instr.getValue().getValue() = value
|
||||
}
|
||||
|
||||
private predicate fieldAddressValueNumber(
|
||||
FieldAddressInstruction instr, IRFunction irFunc, Field field, ValueNumber objectAddress
|
||||
FieldAddressInstruction instr, IRFunction irFunc, Language::Field field, ValueNumber objectAddress
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getField() = field and
|
||||
@@ -174,43 +175,43 @@ private predicate fieldAddressValueNumber(
|
||||
}
|
||||
|
||||
private predicate binaryValueNumber(
|
||||
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand,
|
||||
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr instanceof PointerArithmeticInstruction and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
instr.getResultIRType() = type and
|
||||
valueNumber(instr.getLeft()) = leftOperand and
|
||||
valueNumber(instr.getRight()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate pointerArithmeticValueNumber(
|
||||
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, Type type, int elementSize,
|
||||
ValueNumber leftOperand, ValueNumber rightOperand
|
||||
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, IRType type,
|
||||
int elementSize, ValueNumber leftOperand, ValueNumber rightOperand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
instr.getResultIRType() = type and
|
||||
instr.getElementSize() = elementSize and
|
||||
valueNumber(instr.getLeft()) = leftOperand and
|
||||
valueNumber(instr.getRight()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate unaryValueNumber(
|
||||
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand
|
||||
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr instanceof InheritanceConversionInstruction and
|
||||
not instr instanceof CopyInstruction and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
instr.getResultIRType() = type and
|
||||
valueNumber(instr.getUnary()) = operand
|
||||
}
|
||||
|
||||
private predicate inheritanceConversionValueNumber(
|
||||
InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode, Class baseClass,
|
||||
Class derivedClass, ValueNumber operand
|
||||
InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode,
|
||||
Language::Class baseClass, Language::Class derivedClass, ValueNumber operand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getOpcode() = opcode and
|
||||
@@ -225,7 +226,7 @@ private predicate inheritanceConversionValueNumber(
|
||||
*/
|
||||
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr.getResultType() instanceof VoidType and
|
||||
not instr.getResultIRType() instanceof IRVoidType and
|
||||
not numberableInstruction(instr)
|
||||
}
|
||||
|
||||
@@ -269,38 +270,41 @@ private ValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
initializeThisValueNumber(instr, irFunc) and
|
||||
result = TInitializeThisValueNumber(irFunc)
|
||||
or
|
||||
exists(Type type, string value |
|
||||
exists(IRType type, string value |
|
||||
constantValueNumber(instr, irFunc, type, value) and
|
||||
result = TConstantValueNumber(irFunc, type, value)
|
||||
)
|
||||
or
|
||||
exists(Type type, string value |
|
||||
exists(IRType type, string value |
|
||||
stringConstantValueNumber(instr, irFunc, type, value) and
|
||||
result = TStringConstantValueNumber(irFunc, type, value)
|
||||
)
|
||||
or
|
||||
exists(Field field, ValueNumber objectAddress |
|
||||
exists(Language::Field field, ValueNumber objectAddress |
|
||||
fieldAddressValueNumber(instr, irFunc, field, objectAddress) and
|
||||
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
|
||||
)
|
||||
or
|
||||
exists(Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand |
|
||||
exists(Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand |
|
||||
binaryValueNumber(instr, irFunc, opcode, type, leftOperand, rightOperand) and
|
||||
result = TBinaryValueNumber(irFunc, opcode, type, leftOperand, rightOperand)
|
||||
)
|
||||
or
|
||||
exists(Opcode opcode, Type type, ValueNumber operand |
|
||||
exists(Opcode opcode, IRType type, ValueNumber operand |
|
||||
unaryValueNumber(instr, irFunc, opcode, type, operand) and
|
||||
result = TUnaryValueNumber(irFunc, opcode, type, operand)
|
||||
)
|
||||
or
|
||||
exists(Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand |
|
||||
exists(
|
||||
Opcode opcode, Language::Class baseClass, Language::Class derivedClass, ValueNumber operand
|
||||
|
|
||||
inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and
|
||||
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, ValueNumber rightOperand
|
||||
Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand
|
||||
|
|
||||
pointerArithmeticValueNumber(instr, irFunc, opcode, type, elementSize, leftOperand,
|
||||
rightOperand) and
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
@@ -282,7 +282,7 @@ private predicate automaticVariableAddressEscapes(IRAutomaticVariable var) {
|
||||
// The variable's address escapes if the result of any
|
||||
// VariableAddressInstruction that computes the variable's address escapes.
|
||||
exists(VariableAddressInstruction instr |
|
||||
instr.getVariable() = var and
|
||||
instr.getIRVariable() = var and
|
||||
resultEscapesNonReturn(instr)
|
||||
)
|
||||
}
|
||||
@@ -305,7 +305,7 @@ predicate variableAddressEscapes(IRVariable var) {
|
||||
*/
|
||||
predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset) {
|
||||
// The address of a variable points to that variable, at offset 0.
|
||||
instr.(VariableAddressInstruction).getVariable() = var and
|
||||
instr.(VariableAddressInstruction).getIRVariable() = var and
|
||||
bitOffset = 0
|
||||
or
|
||||
exists(Operand operand, IntValue originalBitOffset, IntValue propagatedBitOffset |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
private import cpp
|
||||
import AliasAnalysis
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
private import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
private import semmle.code.cpp.Print
|
||||
private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR
|
||||
private import semmle.code.cpp.ir.internal.IntegerConstant as Ints
|
||||
@@ -10,29 +10,39 @@ private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
private class IntValue = Ints::IntValue;
|
||||
|
||||
private predicate hasResultMemoryAccess(
|
||||
Instruction instr, IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset
|
||||
Instruction instr, IRVariable var, IRType type, Language::LanguageType languageType,
|
||||
IntValue startBitOffset, IntValue endBitOffset
|
||||
) {
|
||||
resultPointsTo(instr.getResultAddress(), var, startBitOffset) and
|
||||
type = instr.getResultType() and
|
||||
if exists(instr.getResultSize())
|
||||
then endBitOffset = Ints::add(startBitOffset, Ints::mul(instr.getResultSize(), 8))
|
||||
languageType = instr.getResultLanguageType() and
|
||||
type = languageType.getIRType() and
|
||||
if exists(type.getByteSize())
|
||||
then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
|
||||
else endBitOffset = Ints::unknown()
|
||||
}
|
||||
|
||||
private predicate hasOperandMemoryAccess(
|
||||
MemoryOperand operand, IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset
|
||||
MemoryOperand operand, IRVariable var, IRType type, Language::LanguageType languageType,
|
||||
IntValue startBitOffset, IntValue endBitOffset
|
||||
) {
|
||||
resultPointsTo(operand.getAddressOperand().getAnyDef(), var, startBitOffset) and
|
||||
type = operand.getType() and
|
||||
if exists(operand.getSize())
|
||||
then endBitOffset = Ints::add(startBitOffset, Ints::mul(operand.getSize(), 8))
|
||||
languageType = operand.getLanguageType() and
|
||||
type = languageType.getIRType() and
|
||||
if exists(type.getByteSize())
|
||||
then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
|
||||
else endBitOffset = Ints::unknown()
|
||||
}
|
||||
|
||||
private newtype TMemoryLocation =
|
||||
TVariableMemoryLocation(IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset) {
|
||||
hasResultMemoryAccess(_, var, type, startBitOffset, endBitOffset) or
|
||||
hasOperandMemoryAccess(_, var, type, startBitOffset, endBitOffset)
|
||||
TVariableMemoryLocation(
|
||||
IRVariable var, IRType type, Language::LanguageType languageType, IntValue startBitOffset,
|
||||
IntValue endBitOffset
|
||||
) {
|
||||
(
|
||||
hasResultMemoryAccess(_, var, type, _, startBitOffset, endBitOffset) or
|
||||
hasOperandMemoryAccess(_, var, type, _, startBitOffset, endBitOffset)
|
||||
) and
|
||||
languageType = type.getCanonicalLanguageType()
|
||||
} or
|
||||
TUnknownMemoryLocation(IRFunction irFunc) or
|
||||
TUnknownVirtualVariable(IRFunction irFunc)
|
||||
@@ -49,9 +59,11 @@ abstract class MemoryLocation extends TMemoryLocation {
|
||||
|
||||
abstract VirtualVariable getVirtualVariable();
|
||||
|
||||
abstract Type getType();
|
||||
abstract Language::LanguageType getType();
|
||||
|
||||
abstract string getUniqueId();
|
||||
|
||||
final IRType getIRType() { result = getType().getIRType() }
|
||||
}
|
||||
|
||||
abstract class VirtualVariable extends MemoryLocation { }
|
||||
@@ -62,20 +74,34 @@ abstract class VirtualVariable extends MemoryLocation { }
|
||||
*/
|
||||
class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation {
|
||||
IRVariable var;
|
||||
Type type;
|
||||
IRType type;
|
||||
Language::LanguageType languageType;
|
||||
IntValue startBitOffset;
|
||||
IntValue endBitOffset;
|
||||
|
||||
VariableMemoryLocation() {
|
||||
this = TVariableMemoryLocation(var, type, startBitOffset, endBitOffset)
|
||||
this = TVariableMemoryLocation(var, type, languageType, startBitOffset, endBitOffset)
|
||||
}
|
||||
|
||||
final override string toString() {
|
||||
result = var.toString() + Interval::getIntervalString(startBitOffset, endBitOffset) + "<" +
|
||||
type.toString() + ">"
|
||||
type.toString() + ", " + languageType.toString() + ">"
|
||||
}
|
||||
|
||||
final override Type getType() { result = type }
|
||||
final override Language::LanguageType getType() {
|
||||
if
|
||||
strictcount(Language::LanguageType accessType |
|
||||
hasResultMemoryAccess(_, var, type, accessType, startBitOffset, endBitOffset) or
|
||||
hasOperandMemoryAccess(_, var, type, accessType, startBitOffset, endBitOffset)
|
||||
) = 1
|
||||
then
|
||||
// All of the accesses have the same `LanguageType`, so just use that.
|
||||
hasResultMemoryAccess(_, var, type, result, startBitOffset, endBitOffset) or
|
||||
hasOperandMemoryAccess(_, var, type, result, startBitOffset, endBitOffset)
|
||||
else
|
||||
// There is no single type for all accesses, so just use the canonical one for this `IRType`.
|
||||
result = type.getCanonicalLanguageType()
|
||||
}
|
||||
|
||||
final IntValue getStartBitOffset() { result = startBitOffset }
|
||||
|
||||
@@ -85,13 +111,14 @@ class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation {
|
||||
|
||||
final override string getUniqueId() {
|
||||
result = var.getUniqueId() + Interval::getIntervalString(startBitOffset, endBitOffset) + "<" +
|
||||
getTypeIdentityString(type) + ">"
|
||||
type.getIdentityString() + ">"
|
||||
}
|
||||
|
||||
final override VirtualVariable getVirtualVariable() {
|
||||
if variableAddressEscapes(var)
|
||||
then result = TUnknownVirtualVariable(var.getEnclosingIRFunction())
|
||||
else result = TVariableMemoryLocation(var, var.getType(), 0, var.getType().getSize() * 8)
|
||||
else
|
||||
result = TVariableMemoryLocation(var, var.getIRType(), _, 0, var.getIRType().getByteSize() * 8)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,7 +126,7 @@ class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation {
|
||||
*/
|
||||
final predicate coversEntireVariable() {
|
||||
startBitOffset = 0 and
|
||||
endBitOffset = var.getType().getSize() * 8
|
||||
endBitOffset = var.getIRType().getByteSize() * 8
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +138,7 @@ class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation {
|
||||
class VariableVirtualVariable extends VariableMemoryLocation, VirtualVariable {
|
||||
VariableVirtualVariable() {
|
||||
not variableAddressEscapes(var) and
|
||||
type = var.getType() and
|
||||
type = var.getIRType() and
|
||||
coversEntireVariable()
|
||||
}
|
||||
}
|
||||
@@ -128,7 +155,9 @@ class UnknownMemoryLocation extends TUnknownMemoryLocation, MemoryLocation {
|
||||
|
||||
final override VirtualVariable getVirtualVariable() { result = TUnknownVirtualVariable(irFunc) }
|
||||
|
||||
final override Type getType() { result instanceof UnknownType }
|
||||
final override Language::LanguageType getType() {
|
||||
result = any(IRUnknownType type).getCanonicalLanguageType()
|
||||
}
|
||||
|
||||
final override string getUniqueId() { result = "{Unknown}" }
|
||||
}
|
||||
@@ -143,7 +172,9 @@ class UnknownVirtualVariable extends TUnknownVirtualVariable, VirtualVariable {
|
||||
|
||||
final override string toString() { result = "{AllAliased}" }
|
||||
|
||||
final override Type getType() { result instanceof UnknownType }
|
||||
final override Language::LanguageType getType() {
|
||||
result = any(IRUnknownType type).getCanonicalLanguageType()
|
||||
}
|
||||
|
||||
final override string getUniqueId() { result = " " + toString() }
|
||||
|
||||
@@ -177,7 +208,7 @@ Overlap getOverlap(MemoryLocation def, MemoryLocation use) {
|
||||
intervalOverlap = getVariableMemoryLocationOverlap(def, use) and
|
||||
if intervalOverlap instanceof MustExactlyOverlap
|
||||
then
|
||||
if def.getType() = use.getType()
|
||||
if def.getIRType() = use.getIRType()
|
||||
then
|
||||
// The def and use types match, so it's an exact overlap.
|
||||
result instanceof MustExactlyOverlap
|
||||
@@ -282,11 +313,11 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
|
||||
(
|
||||
(
|
||||
kind.usesAddressOperand() and
|
||||
if hasResultMemoryAccess(instr, _, _, _, _)
|
||||
if hasResultMemoryAccess(instr, _, _, _, _, _)
|
||||
then
|
||||
exists(IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset |
|
||||
hasResultMemoryAccess(instr, var, type, startBitOffset, endBitOffset) and
|
||||
result = TVariableMemoryLocation(var, type, startBitOffset, endBitOffset)
|
||||
exists(IRVariable var, IRType type, IntValue startBitOffset, IntValue endBitOffset |
|
||||
hasResultMemoryAccess(instr, var, type, _, startBitOffset, endBitOffset) and
|
||||
result = TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset)
|
||||
)
|
||||
else result = TUnknownMemoryLocation(instr.getEnclosingIRFunction())
|
||||
)
|
||||
@@ -306,11 +337,11 @@ MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
|
||||
(
|
||||
(
|
||||
kind.usesAddressOperand() and
|
||||
if hasOperandMemoryAccess(operand, _, _, _, _)
|
||||
if hasOperandMemoryAccess(operand, _, _, _, _, _)
|
||||
then
|
||||
exists(IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset |
|
||||
hasOperandMemoryAccess(operand, var, type, startBitOffset, endBitOffset) and
|
||||
result = TVariableMemoryLocation(var, type, startBitOffset, endBitOffset)
|
||||
exists(IRVariable var, IRType type, IntValue startBitOffset, IntValue endBitOffset |
|
||||
hasOperandMemoryAccess(operand, var, type, _, startBitOffset, endBitOffset) and
|
||||
result = TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset)
|
||||
)
|
||||
else result = TUnknownMemoryLocation(operand.getEnclosingIRFunction())
|
||||
)
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind as EdgeKind
|
||||
import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.implementation.TempVariableTag as TempVariableTag
|
||||
import semmle.code.cpp.ir.internal.IRUtilities as IRUtilities
|
||||
import semmle.code.cpp.ir.internal.TempVariableTag as TTempVariableTag
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind as EdgeKind
|
||||
import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
import semmle.code.cpp.ir.implementation.Opcode as Opcode
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.internal.Overlap as Overlap
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
|
||||
|
||||
@@ -4,34 +4,52 @@ private import Alias
|
||||
private import SSAConstruction
|
||||
private import DebugSSA
|
||||
|
||||
bindingset[offset]
|
||||
private string getKeySuffixForOffset(int offset) {
|
||||
if offset % 2 = 0 then result = "" else result = "_Chi"
|
||||
}
|
||||
|
||||
bindingset[offset]
|
||||
private int getIndexForOffset(int offset) { result = offset / 2 }
|
||||
|
||||
/**
|
||||
* Property provide that dumps the memory access of each result. Useful for debugging SSA
|
||||
* construction.
|
||||
*/
|
||||
class PropertyProvider extends IRPropertyProvider {
|
||||
override string getInstructionProperty(Instruction instruction, string key) {
|
||||
exists(MemoryLocation location |
|
||||
location = getResultMemoryLocation(instruction) and
|
||||
(
|
||||
key = "ResultMemoryLocation" and result = location.toString()
|
||||
or
|
||||
key = "ResultVirtualVariable" and result = location.getVirtualVariable().toString()
|
||||
key = "ResultMemoryLocation" and
|
||||
result = strictconcat(MemoryLocation loc |
|
||||
loc = getResultMemoryLocation(instruction)
|
||||
|
|
||||
loc.toString(), ","
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(MemoryLocation location |
|
||||
location = getOperandMemoryLocation(instruction.getAnOperand()) and
|
||||
(
|
||||
key = "OperandMemoryAccess" and result = location.toString()
|
||||
or
|
||||
key = "OperandVirtualVariable" and result = location.getVirtualVariable().toString()
|
||||
key = "ResultVirtualVariable" and
|
||||
result = strictconcat(MemoryLocation loc |
|
||||
loc = getResultMemoryLocation(instruction)
|
||||
|
|
||||
loc.getVirtualVariable().toString(), ","
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defIndex |
|
||||
hasDefinitionAtRank(useLocation, _, defBlock, defRank, defIndex) and
|
||||
defBlock.getInstruction(defIndex) = instruction and
|
||||
key = "DefinitionRank[" + useLocation.toString() + "]" and
|
||||
key = "OperandMemoryLocation" and
|
||||
result = strictconcat(MemoryLocation loc |
|
||||
loc = getOperandMemoryLocation(instruction.getAnOperand())
|
||||
|
|
||||
loc.toString(), ","
|
||||
)
|
||||
or
|
||||
key = "OperandVirtualVariable" and
|
||||
result = strictconcat(MemoryLocation loc |
|
||||
loc = getOperandMemoryLocation(instruction.getAnOperand())
|
||||
|
|
||||
loc.getVirtualVariable().toString(), ","
|
||||
)
|
||||
or
|
||||
exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defOffset |
|
||||
hasDefinitionAtRank(useLocation, _, defBlock, defRank, defOffset) and
|
||||
defBlock.getInstruction(getIndexForOffset(defOffset)) = instruction and
|
||||
key = "DefinitionRank" + getKeySuffixForOffset(defOffset) + "[" + useLocation.toString() + "]" and
|
||||
result = defRank.toString()
|
||||
)
|
||||
or
|
||||
@@ -41,10 +59,11 @@ class PropertyProvider extends IRPropertyProvider {
|
||||
result = useRank.toString()
|
||||
)
|
||||
or
|
||||
exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defIndex |
|
||||
hasDefinitionAtRank(useLocation, _, defBlock, defRank, defIndex) and
|
||||
defBlock.getInstruction(defIndex) = instruction and
|
||||
key = "DefinitionReachesUse[" + useLocation.toString() + "]" and
|
||||
exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defOffset |
|
||||
hasDefinitionAtRank(useLocation, _, defBlock, defRank, defOffset) and
|
||||
defBlock.getInstruction(getIndexForOffset(defOffset)) = instruction and
|
||||
key = "DefinitionReachesUse" + getKeySuffixForOffset(defOffset) + "[" + useLocation.toString()
|
||||
+ "]" and
|
||||
result = strictconcat(IRBlock useBlock, int useRank, int useIndex |
|
||||
exists(Instruction useInstruction |
|
||||
hasUseAtRank(useLocation, useBlock, useRank, useInstruction) and
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import SSAConstructionInternal
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.Overlap
|
||||
private import SSAConstructionImports
|
||||
private import NewIR
|
||||
|
||||
private class OldBlock = Reachability::ReachableBlock;
|
||||
@@ -18,7 +15,7 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
predicate functionHasIR(Function func) {
|
||||
predicate functionHasIR(Language::Function func) {
|
||||
exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func)
|
||||
}
|
||||
|
||||
@@ -42,7 +39,7 @@ private module Cached {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction and
|
||||
hasChiNode(_, oldInstruction)
|
||||
} or
|
||||
Unreached(Function function) {
|
||||
Unreached(Language::Function function) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
function = oldInstruction.getEnclosingFunction() and
|
||||
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
|
||||
@@ -50,12 +47,14 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, Type type) {
|
||||
predicate hasTempVariable(
|
||||
Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type
|
||||
) {
|
||||
exists(OldIR::IRTempVariable var |
|
||||
var.getEnclosingFunction() = func and
|
||||
var.getAST() = ast and
|
||||
var.getTag() = tag and
|
||||
var.getType() = type
|
||||
var.getLanguageType() = type
|
||||
)
|
||||
}
|
||||
|
||||
@@ -135,24 +134,12 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
Type getInstructionOperandType(Instruction instr, TypedOperandTag tag) {
|
||||
Language::LanguageType getInstructionOperandType(Instruction instr, TypedOperandTag tag) {
|
||||
exists(OldInstruction oldInstruction, OldIR::TypedOperand oldOperand |
|
||||
oldInstruction = getOldInstruction(instr) and
|
||||
oldOperand = oldInstruction.getAnOperand() and
|
||||
tag = oldOperand.getOperandTag() and
|
||||
result = oldOperand.getType()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionOperandSize(Instruction instr, SideEffectOperandTag tag) {
|
||||
exists(OldInstruction oldInstruction, OldIR::SideEffectOperand oldOperand |
|
||||
oldInstruction = getOldInstruction(instr) and
|
||||
oldOperand = oldInstruction.getAnOperand() and
|
||||
tag = oldOperand.getOperandTag() and
|
||||
// Only return a result for operands that need an explicit result size.
|
||||
oldOperand.getType() instanceof UnknownType and
|
||||
result = oldOperand.getSize()
|
||||
result = oldOperand.getLanguageType()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -196,20 +183,21 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
Language::Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getConvertedResultExpression()
|
||||
}
|
||||
|
||||
cached
|
||||
Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
Language::Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node,
|
||||
* that node is its successor in the new successor relation, and the Chi node's successors are
|
||||
* the new instructions generated from the successors of the old instruction
|
||||
*/
|
||||
|
||||
cached
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
if hasChiNode(_, getOldInstruction(instruction))
|
||||
@@ -252,7 +240,7 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
Locatable getInstructionAST(Instruction instruction) {
|
||||
Language::AST getInstructionAST(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction)
|
||||
or
|
||||
@@ -270,29 +258,25 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
predicate instructionHasType(Instruction instruction, Type type, boolean isGLValue) {
|
||||
Language::LanguageType getInstructionResultType(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction) and
|
||||
type = oldInstruction.getResultType() and
|
||||
if oldInstruction.isGLValue() then isGLValue = true else isGLValue = false
|
||||
result = oldInstruction.getResultLanguageType()
|
||||
)
|
||||
or
|
||||
exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar |
|
||||
instruction = Chi(oldInstruction) and
|
||||
hasChiNode(vvar, oldInstruction) and
|
||||
type = vvar.getType() and
|
||||
isGLValue = false
|
||||
result = vvar.getType()
|
||||
)
|
||||
or
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = Phi(_, location) and
|
||||
type = location.getType() and
|
||||
isGLValue = false
|
||||
result = location.getType()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(_) and
|
||||
type instanceof VoidType and
|
||||
isGLValue = false
|
||||
result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -334,16 +318,21 @@ private module Cached {
|
||||
IRVariable getInstructionVariable(Instruction instruction) {
|
||||
result = getNewIRVariable(getOldInstruction(instruction)
|
||||
.(OldIR::VariableInstruction)
|
||||
.getVariable())
|
||||
.getIRVariable())
|
||||
}
|
||||
|
||||
cached
|
||||
Field getInstructionField(Instruction instruction) {
|
||||
Language::Field getInstructionField(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField()
|
||||
}
|
||||
|
||||
cached
|
||||
Function getInstructionFunction(Instruction instruction) {
|
||||
int getInstructionIndex(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::Function getInstructionFunction(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
|
||||
}
|
||||
|
||||
@@ -353,19 +342,19 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
StringLiteral getInstructionStringLiteral(Instruction instruction) {
|
||||
Language::StringLiteral getInstructionStringLiteral(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::StringConstantInstruction).getValue()
|
||||
}
|
||||
|
||||
cached
|
||||
BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
|
||||
Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
|
||||
result = getOldInstruction(instruction)
|
||||
.(OldIR::BuiltInOperationInstruction)
|
||||
.getBuiltInOperation()
|
||||
}
|
||||
|
||||
cached
|
||||
Type getInstructionExceptionType(Instruction instruction) {
|
||||
Language::LanguageType getInstructionExceptionType(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType()
|
||||
}
|
||||
|
||||
@@ -375,14 +364,9 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionResultSize(Instruction instruction) {
|
||||
// Only return a result for instructions that needed an explicit result size.
|
||||
instruction.getResultType() instanceof UnknownType and
|
||||
result = getOldInstruction(instruction).getResultSize()
|
||||
}
|
||||
|
||||
cached
|
||||
predicate getInstructionInheritance(Instruction instruction, Class baseClass, Class derivedClass) {
|
||||
predicate getInstructionInheritance(
|
||||
Instruction instruction, Language::Class baseClass, Language::Class derivedClass
|
||||
) {
|
||||
exists(OldIR::InheritanceConversionInstruction oldInstr |
|
||||
oldInstr = getOldInstruction(instruction) and
|
||||
baseClass = oldInstr.getBaseClass() and
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import semmle.code.cpp.ir.implementation.Opcode
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
@@ -2,4 +2,5 @@ import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as OldIR
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.ReachableBlock as Reachability
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.Dominance as Dominance
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as NewIR
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import AliasedSSA as Alias
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
@@ -66,12 +66,14 @@ AddressOperandTag addressOperand() { result = TAddressOperand() }
|
||||
* The buffer size operand of an instruction that represents a read or write of
|
||||
* a buffer.
|
||||
*/
|
||||
class BufferSizeOperand extends RegisterOperandTag, TBufferSizeOperand {
|
||||
class BufferSizeOperandTag extends RegisterOperandTag, TBufferSizeOperand {
|
||||
final override string toString() { result = "BufferSize" }
|
||||
|
||||
final override int getSortOrder() { result = 1 }
|
||||
}
|
||||
|
||||
BufferSizeOperandTag bufferSizeOperand() { result = TBufferSizeOperand() }
|
||||
|
||||
/**
|
||||
* The operand representing the read side effect of a `SideEffectInstruction`.
|
||||
*/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user