mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge branch 'master' into rdmarsh/cpp/hasGlobalOrStdName
This commit is contained in:
5
.codeqlmanifest.json
Normal file
5
.codeqlmanifest.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{ "provide": [ "*/ql/src/qlpack.yml",
|
||||
"*/upgrades/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,8 +18,11 @@ 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. |
|
||||
| Comparison of narrow type with wide type in loop condition (`cpp/comparison-with-wider-type`) | Higher precision | The precision of this query has been increased to "high" as the alerts from this query have proved to be valuable on real-world projects. With this precision, results are now displayed by default in LGTM. |
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
@@ -30,11 +33,16 @@ 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
|
||||
changes are in line with the user expectations we've observed.
|
||||
* The data-flow library now makes it easier to specify barriers/sanitizers
|
||||
arising from guards by overriding the predicate
|
||||
`isBarrierGuard`/`isSanitizerGuard` on data-flow and taint-tracking
|
||||
configurations respectively.
|
||||
* There is now a `DataFlow::localExprFlow` predicate and a
|
||||
`TaintTracking::localExprTaint` predicate to make it easy to use the most
|
||||
common case of local data flow and taint: from one `Expr` to another.
|
||||
@@ -44,3 +52,12 @@ The following changes in version 1.23 affect C/C++ analysis in all applications.
|
||||
predicates should be updated to use the corresponding new member predicate.
|
||||
* The predicates `Declaration.hasStdName()` and `Declaration.hasGlobalOrStdName`
|
||||
have been added, simplifying handling of C++ standard library functions.
|
||||
* The control-flow graph is now computed in QL, not in the extractor. This can
|
||||
lead to regressions (or improvements) in how queries are optimized because
|
||||
optimization in QL relies on static size estimates, and the control-flow edge
|
||||
relations will now have different size estimates than before.
|
||||
* Support has been added for non-type template arguments. This means that the
|
||||
return type of `Declaration::getTemplateArgument()` and
|
||||
`Declaration::getATemplateArgument` have changed to `Locatable`. See the
|
||||
documentation for `Declaration::getTemplateArgument()` and
|
||||
`Declaration::getTemplateArgumentKind()` for details.
|
||||
|
||||
@@ -8,6 +8,7 @@ The following changes in version 1.23 affect C# analysis in all applications.
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
| Deserialized delegate (`cs/deserialized-delegate`) | security, external/cwe/cwe-502 | Finds unsafe deserialization of delegate types. |
|
||||
| Unsafe year argument for 'DateTime' constructor (`cs/unsafe-year-construction`) | reliability, date-time | Finds incorrect manipulation of `DateTime` values, which could lead to invalid dates. |
|
||||
| Mishandling the Japanese era start date (`cs/mishandling-japanese-era`) | reliability, date-time | Finds hard-coded Japanese era start dates that could be invalid. |
|
||||
|
||||
@@ -16,6 +17,7 @@ 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
|
||||
|
||||
@@ -38,5 +40,10 @@ 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.
|
||||
* Data is now tracked through null-coalescing expressions (`??`).
|
||||
|
||||
## Changes to autobuilder
|
||||
|
||||
@@ -2,14 +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,6 +2,8 @@
|
||||
|
||||
## General improvements
|
||||
|
||||
* Support 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)
|
||||
@@ -10,17 +12,28 @@
|
||||
|
||||
* The call graph has been improved to resolve method calls in more cases. This may produce more security alerts.
|
||||
|
||||
* TypeScript 3.6 and 3.7 features are now supported.
|
||||
|
||||
* Automatic classification of generated files has been improved, in particular files generated by Doxygen are now recognized.
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|---------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Unused index variable (`js/unused-index-variable`) | correctness | Highlights loops that iterate over an array, but do not use the index variable to access array elements, indicating a possible typo or logic error. Results are shown on LGTM by default. |
|
||||
| Loop bound injection (`js/loop-bound-injection`) | security, external/cwe/cwe-834 | Highlights loops where a user-controlled object with an arbitrary .length value can trick the server to loop indefinitely. Results are not shown on LGTM by default. |
|
||||
| Loop bound injection (`js/loop-bound-injection`) | security, external/cwe/cwe-834 | Highlights loops where a user-controlled object with an arbitrary .length value can trick the server to loop indefinitely. Results are 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. |
|
||||
| Ignoring result from pure array method (`js/ignore-array-result`) | maintainability, correctness | Highlights calls to array methods without side effects where the return value is ignored. Results are shown on LGTM by default. |
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
|
||||
| Double escaping or unescaping (`js/double-escaping`) | More results | This rule now detects additional escaping and unescaping functions. |
|
||||
| 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, 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. |
|
||||
@@ -33,7 +46,27 @@
|
||||
| Reflected cross-site scripting (`js/reflected-xss`) | Fewer false-positive results | The query now recognizes more sanitizers. |
|
||||
| Stored cross-site scripting (`js/stored-xss`) | Fewer false-positive results | The query now recognizes more sanitizers. |
|
||||
| Uncontrolled command line (`js/command-line-injection`) | More results | This query now treats responses from servers as untrusted. |
|
||||
| Uncontrolled data used in path expression (`js/path-injection`) | Fewer false-positive results | This query now recognizes calls to Express `sendFile` as safe in some cases. |
|
||||
| Unknown directive (`js/unknown-directive`) | Fewer false positive results | This query no longer flags uses of ":", which is sometimes used like a directive. |
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
* `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,17 @@
|
||||
|-----------|----------|-------------|
|
||||
| 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. |
|
||||
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
* Django library now recognizes positional arguments from a `django.conf.urls.url` regex (Django version 1.x)
|
||||
|
||||
@@ -5,6 +5,19 @@
|
||||
## Changes to code extraction
|
||||
|
||||
* Asynchronous generator methods are now parsed correctly and no longer cause a spurious syntax error.
|
||||
* Files in `node_modules` and `bower_components` folders are no longer extracted by default. If you still want to extract files from these folders, you can add the following filters to your `lgtm.yml` file (or add them to existing filters):
|
||||
|
||||
```yaml
|
||||
extraction:
|
||||
javascript:
|
||||
index:
|
||||
filters:
|
||||
- include: "**/node_modules"
|
||||
- include: "**/bower_components"
|
||||
```
|
||||
|
||||
* Recognition of CommonJS modules has improved. As a result, some files that were previously extracted as
|
||||
global scripts are now extracted as modules.
|
||||
* Top-level `await` is now supported.
|
||||
* 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``.
|
||||
|
||||
@@ -76,7 +76,11 @@
|
||||
"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/unaliased_ssa/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",
|
||||
@@ -169,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",
|
||||
@@ -235,5 +256,9 @@
|
||||
"C# IR PrintIRImports": [
|
||||
"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"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ from Variable v
|
||||
where
|
||||
v.isStatic() and
|
||||
v.hasDefinition() and
|
||||
not v.isConstexpr() and
|
||||
not exists(VariableAccess a | a.getTarget() = v) and
|
||||
not v instanceof MemberVariable and
|
||||
not declarationHasSideEffects(v) and
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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, as this could 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()
|
||||
@@ -5,7 +5,7 @@
|
||||
* @id cpp/comparison-with-wider-type
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @precision high
|
||||
* @tags reliability
|
||||
* security
|
||||
* external/cwe/cwe-190
|
||||
|
||||
@@ -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 will 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 behavior.
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
}
|
||||
705
cpp/ql/src/Security/CWE/CWE-457/InitializationFunctions.qll
Normal file
705
cpp/ql/src/Security/CWE/CWE-457/InitializationFunctions.qll
Normal file
@@ -0,0 +1,705 @@
|
||||
/**
|
||||
* 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()
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper predicate for `getTarget`, that computes possible targets of a `Call`.
|
||||
*
|
||||
* If there is at least one defined target after performing some simple virtual dispatch
|
||||
* resolution, then the result is all the defined targets.
|
||||
*/
|
||||
private Function getTarget1(Call c) {
|
||||
result = VirtualDispatch::getAViableTarget(c) and
|
||||
result.isDefined()
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper predicate for `getTarget`, that computes possible targets of a `Call`.
|
||||
*
|
||||
* If we can use the heuristic matching of functions to find definitions for some of the viable
|
||||
* targets, return those.
|
||||
*/
|
||||
private Function getTarget2(Call c) {
|
||||
not exists(getTarget1(c)) and
|
||||
result = getAPossibleDefinition(VirtualDispatch::getAViableTarget(c))
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper predicate for `getTarget`, that computes possible targets of a `Call`.
|
||||
*
|
||||
* Otherwise, the result is the undefined `Function` instances.
|
||||
*/
|
||||
private Function getTarget3(Call c) {
|
||||
not exists(getTarget1(c)) and
|
||||
not exists(getTarget2(c)) and
|
||||
result = VirtualDispatch::getAViableTarget(c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible target for the `Call`, using the name and parameter matching if we did not associate
|
||||
* this call with a specific definition at link or compile time, and performing simple virtual
|
||||
* dispatch resolution.
|
||||
*/
|
||||
Function getTarget(Call c) {
|
||||
result = getTarget1(c) or
|
||||
result = getTarget2(c) or
|
||||
result = getTarget3(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
|
||||
@@ -605,15 +605,6 @@ class Class extends UserType {
|
||||
class_instantiation(underlyingElement(this), unresolveElement(c))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument used to instantiate this class from a
|
||||
* class template. When called on a class template, this will return the
|
||||
* `i`th template parameter.
|
||||
*/
|
||||
override Type getTemplateArgument(int i) {
|
||||
class_template_argument(underlyingElement(this), i, unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this class/struct is polymorphic (has a virtual function, or
|
||||
* inherits one).
|
||||
@@ -623,7 +614,7 @@ class Class extends UserType {
|
||||
}
|
||||
|
||||
override predicate involvesTemplateParameter() {
|
||||
getATemplateArgument().involvesTemplateParameter()
|
||||
getATemplateArgument().(Type).involvesTemplateParameter()
|
||||
}
|
||||
|
||||
/** Holds if this class, struct or union was declared 'final'. */
|
||||
|
||||
@@ -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.
|
||||
@@ -196,26 +200,99 @@ abstract class Declaration extends Locatable, @declaration {
|
||||
|
||||
/**
|
||||
* Gets a template argument used to instantiate this declaration from a template.
|
||||
* When called on a template, this will return a template parameter.
|
||||
* When called on a template, this will return a template parameter type for
|
||||
* both typed and non-typed parameters.
|
||||
*/
|
||||
final Type getATemplateArgument() { result = getTemplateArgument(_) }
|
||||
final Locatable getATemplateArgument() { result = getTemplateArgument(_) }
|
||||
|
||||
/**
|
||||
* Gets a template argument used to instantiate this declaration from a template.
|
||||
* When called on a template, this will return a non-typed template
|
||||
* parameter value.
|
||||
*/
|
||||
final Locatable getATemplateArgumentKind() { result = getTemplateArgumentKind(_) }
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument used to instantiate this declaration from a
|
||||
* template. When called on a template, this will return the `i`th template parameter.
|
||||
* template.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* `template<typename T, T X> class Foo;`
|
||||
*
|
||||
* Will have `getTemplateArgument(0)` return `T`, and
|
||||
* `getTemplateArgument(1)` return `X`.
|
||||
*
|
||||
* `Foo<int, 1> bar;
|
||||
*
|
||||
* Will have `getTemplateArgument())` return `int`, and
|
||||
* `getTemplateArgument(1)` return `1`.
|
||||
*/
|
||||
Type getTemplateArgument(int index) { none() }
|
||||
final Locatable getTemplateArgument(int index) {
|
||||
if exists(getTemplateArgumentValue(index))
|
||||
then result = getTemplateArgumentValue(index)
|
||||
else result = getTemplateArgumentType(index)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument value used to instantiate this declaration
|
||||
* from a template. When called on a template, this will return the `i`th template
|
||||
* parameter value if it exists.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* `template<typename T, T X> class Foo;`
|
||||
*
|
||||
* Will have `getTemplateArgumentKind(1)` return `T`, and no result for
|
||||
* `getTemplateArgumentKind(0)`.
|
||||
*
|
||||
* `Foo<int, 10> bar;
|
||||
*
|
||||
* Will have `getTemplateArgumentKind(1)` return `int`, and no result for
|
||||
* `getTemplateArgumentKind(0)`.
|
||||
*/
|
||||
final Locatable getTemplateArgumentKind(int index) {
|
||||
if exists(getTemplateArgumentValue(index))
|
||||
then result = getTemplateArgumentType(index)
|
||||
else none()
|
||||
}
|
||||
|
||||
/** Gets the number of template arguments for this declaration. */
|
||||
final int getNumberOfTemplateArguments() {
|
||||
result = count(int i | exists(getTemplateArgument(i)))
|
||||
}
|
||||
|
||||
private Type getTemplateArgumentType(int index) {
|
||||
class_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
or
|
||||
function_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
or
|
||||
variable_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
}
|
||||
|
||||
private Expr getTemplateArgumentValue(int index) {
|
||||
class_template_argument_value(underlyingElement(this), index, unresolveElement(result))
|
||||
or
|
||||
function_template_argument_value(underlyingElement(this), index, unresolveElement(result))
|
||||
or
|
||||
variable_template_argument_value(underlyingElement(this), index, unresolveElement(result))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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. */
|
||||
@@ -288,8 +365,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_.
|
||||
@@ -423,8 +511,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`,
|
||||
|
||||
@@ -343,15 +343,6 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
function_instantiation(underlyingElement(this), unresolveElement(f))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument used to instantiate this function from a
|
||||
* function template. When called on a function template, this will return the
|
||||
* `i`th template parameter.
|
||||
*/
|
||||
override Type getTemplateArgument(int index) {
|
||||
function_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this function is defined in several files. This is illegal in
|
||||
* C (though possible in some C++ compilers), and likely indicates that
|
||||
|
||||
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 }
|
||||
}
|
||||
@@ -35,6 +35,14 @@ private string getParameterTypeString(Type parameterType) {
|
||||
else result = parameterType.(DumpType).getTypeIdentityString()
|
||||
}
|
||||
|
||||
private string getTemplateArgumentString(Declaration d, int i) {
|
||||
if exists(d.getTemplateArgumentKind(i))
|
||||
then
|
||||
result = d.getTemplateArgumentKind(i).(DumpType).getTypeIdentityString() + " " +
|
||||
d.getTemplateArgument(i)
|
||||
else result = d.getTemplateArgument(i).(DumpType).getTypeIdentityString()
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Declaration` extended to add methods for generating strings useful only for dumps and debugging.
|
||||
*/
|
||||
@@ -56,7 +64,7 @@ abstract private class DumpDeclaration extends Declaration {
|
||||
strictconcat(int i |
|
||||
exists(this.getTemplateArgument(i))
|
||||
|
|
||||
this.getTemplateArgument(i).(DumpType).getTypeIdentityString(), ", " order by i
|
||||
getTemplateArgumentString(this, i), ", " order by i
|
||||
) + ">"
|
||||
else result = ""
|
||||
}
|
||||
|
||||
@@ -7,3 +7,15 @@
|
||||
|
||||
import cpp
|
||||
import PrintAST
|
||||
|
||||
/**
|
||||
* Temporarily tweak this class or make a copy to control which functions are
|
||||
* printed.
|
||||
*/
|
||||
class Cfg extends PrintASTConfiguration {
|
||||
/**
|
||||
* TWEAK THIS PREDICATE AS NEEDED.
|
||||
* Holds if the AST for `func` should be printed.
|
||||
*/
|
||||
override predicate shouldPrintFunction(Function func) { any() }
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ class Type extends Locatable, @type {
|
||||
// A function call that provides an explicit template argument that refers to T uses T.
|
||||
// We exclude calls within instantiations, since they do not appear directly in the source.
|
||||
exists(FunctionCall c |
|
||||
c.getAnExplicitTemplateArgument().refersTo(this) and
|
||||
c.getAnExplicitTemplateArgument().(Type).refersTo(this) and
|
||||
result = c and
|
||||
not c.getEnclosingFunction().isConstructedFrom(_)
|
||||
)
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -155,15 +155,6 @@ class Variable extends Declaration, @variable {
|
||||
variable_instantiation(underlyingElement(this), unresolveElement(v))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument used to instantiate this variable from a
|
||||
* variable template. When called on a variable template, this will return the
|
||||
* `i`th template parameter.
|
||||
*/
|
||||
override Type getTemplateArgument(int index) {
|
||||
variable_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this is a compiler-generated variable. For example, a
|
||||
* [range-based for loop](http://en.cppreference.com/w/cpp/language/range-for)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -1122,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
|
||||
)
|
||||
@@ -1261,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() {
|
||||
@@ -1277,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))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1294,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() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1363,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()
|
||||
)
|
||||
@@ -1472,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
|
||||
)
|
||||
@@ -1626,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1729,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
|
||||
@@ -1880,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)
|
||||
)
|
||||
@@ -2071,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() {
|
||||
@@ -2083,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() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2161,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2205,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
|
||||
@@ -2378,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)
|
||||
)
|
||||
|
||||
@@ -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
|
||||
@@ -1122,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
|
||||
)
|
||||
@@ -1261,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() {
|
||||
@@ -1277,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))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1294,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() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1363,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()
|
||||
)
|
||||
@@ -1472,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
|
||||
)
|
||||
@@ -1626,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1729,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
|
||||
@@ -1880,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)
|
||||
)
|
||||
@@ -2071,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() {
|
||||
@@ -2083,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() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2161,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2205,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
|
||||
@@ -2378,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)
|
||||
)
|
||||
|
||||
@@ -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
|
||||
@@ -1122,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
|
||||
)
|
||||
@@ -1261,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() {
|
||||
@@ -1277,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))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1294,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() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1363,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()
|
||||
)
|
||||
@@ -1472,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
|
||||
)
|
||||
@@ -1626,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1729,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
|
||||
@@ -1880,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)
|
||||
)
|
||||
@@ -2071,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() {
|
||||
@@ -2083,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() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2161,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2205,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
|
||||
@@ -2378,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)
|
||||
)
|
||||
|
||||
@@ -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
|
||||
@@ -1122,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
|
||||
)
|
||||
@@ -1261,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() {
|
||||
@@ -1277,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))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1294,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() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1363,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()
|
||||
)
|
||||
@@ -1472,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
|
||||
)
|
||||
@@ -1626,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1729,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
|
||||
@@ -1880,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)
|
||||
)
|
||||
@@ -2071,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() {
|
||||
@@ -2083,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() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2161,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2205,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
|
||||
@@ -2378,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)
|
||||
)
|
||||
|
||||
@@ -120,13 +120,16 @@ private module ImplCommon {
|
||||
int i, ArgumentNode arg, CallContext outercc, DataFlowCall call
|
||||
) {
|
||||
exists(DataFlowCallable c | argumentOf(call, i, arg, c) |
|
||||
outercc = TAnyCallContext()
|
||||
or
|
||||
outercc = TSomeCall(getAParameter(c), _)
|
||||
or
|
||||
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
|
||||
reducedViableImplInCallContext(_, c, other)
|
||||
)
|
||||
(
|
||||
outercc = TAnyCallContext()
|
||||
or
|
||||
outercc = TSomeCall(getAParameter(c), _)
|
||||
or
|
||||
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
|
||||
recordDataFlowCallSite(other, c)
|
||||
)
|
||||
) and
|
||||
not isUnreachableInCall(arg, outercc.(CallContextSpecificCall).getCall())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -152,7 +155,7 @@ private module ImplCommon {
|
||||
exists(int i, DataFlowCallable callable |
|
||||
viableParamArg1(p, callable, i, arg, outercc, call)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, true)
|
||||
else innercc = TSomeCall(p, true)
|
||||
)
|
||||
@@ -164,7 +167,7 @@ private module ImplCommon {
|
||||
exists(DataFlowCall call, int i, DataFlowCallable callable |
|
||||
result = TSpecificCall(call, i, _) and
|
||||
p.isParameterOf(callable, i) and
|
||||
reducedViableImplInCallContext(_, callable, call)
|
||||
recordDataFlowCallSite(call, callable)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -180,14 +183,16 @@ private module ImplCommon {
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid, cc) and
|
||||
step(mid, node) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
compatibleTypes(p.getType(), node.getType()) and
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(Node arg |
|
||||
parameterValueFlow(p, arg, cc) and
|
||||
argumentValueFlowsThrough(arg, node, cc) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
compatibleTypes(p.getType(), node.getType()) and
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -220,6 +225,7 @@ private module ImplCommon {
|
||||
argumentValueFlowsThrough0(call, arg, kind, cc)
|
||||
|
|
||||
out = getAnOutNode(call, kind) and
|
||||
not isUnreachableInCall(out, cc.(CallContextSpecificCall).getCall()) and
|
||||
compatibleTypes(arg.getType(), out.getType())
|
||||
)
|
||||
}
|
||||
@@ -462,13 +468,16 @@ private module ImplCommon {
|
||||
int i, ArgumentNode arg, CallContext outercc, DataFlowCall call
|
||||
) {
|
||||
exists(DataFlowCallable c | argumentOf(call, i, arg, c) |
|
||||
outercc = TAnyCallContext()
|
||||
or
|
||||
outercc = TSomeCall(getAParameter(c), _)
|
||||
or
|
||||
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
|
||||
reducedViableImplInCallContext(_, c, other)
|
||||
)
|
||||
(
|
||||
outercc = TAnyCallContext()
|
||||
or
|
||||
outercc = TSomeCall(getAParameter(c), _)
|
||||
or
|
||||
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
|
||||
recordDataFlowCallSite(other, c)
|
||||
)
|
||||
) and
|
||||
not isUnreachableInCall(arg, outercc.(CallContextSpecificCall).getCall())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -494,7 +503,7 @@ private module ImplCommon {
|
||||
exists(int i, DataFlowCallable callable |
|
||||
viableParamArg1(p, callable, i, arg, outercc, call)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, true)
|
||||
else innercc = TSomeCall(p, true)
|
||||
)
|
||||
@@ -506,7 +515,7 @@ private module ImplCommon {
|
||||
exists(DataFlowCall call, int i, DataFlowCallable callable |
|
||||
result = TSpecificCall(call, i, _) and
|
||||
p.isParameterOf(callable, i) and
|
||||
reducedViableImplInCallContext(_, callable, call)
|
||||
recordDataFlowCallSite(call, callable)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -522,14 +531,16 @@ private module ImplCommon {
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid, cc) and
|
||||
step(mid, node) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
compatibleTypes(p.getType(), node.getType()) and
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(Node arg |
|
||||
parameterValueFlow(p, arg, cc) and
|
||||
argumentValueFlowsThrough(arg, node, cc) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
compatibleTypes(p.getType(), node.getType()) and
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -562,6 +573,7 @@ private module ImplCommon {
|
||||
argumentValueFlowsThrough0(call, arg, kind, cc)
|
||||
|
|
||||
out = getAnOutNode(call, kind) and
|
||||
not isUnreachableInCall(out, cc.(CallContextSpecificCall).getCall()) and
|
||||
compatibleTypes(arg.getType(), out.getType())
|
||||
)
|
||||
}
|
||||
@@ -575,11 +587,22 @@ private module ImplCommon {
|
||||
exists(ArgumentNode arg | arg.argumentOf(call, -1))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` either improves virtual dispatch in
|
||||
* `callable` or if it allows us to prune unreachable nodes in `callable`.
|
||||
*/
|
||||
cached
|
||||
predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
|
||||
reducedViableImplInCallContext(_, callable, call)
|
||||
or
|
||||
exists(Node n | n.getEnclosingCallable() = callable | isUnreachableInCall(n, call))
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TCallContext =
|
||||
TAnyCallContext() or
|
||||
TSpecificCall(DataFlowCall call, int i, boolean emptyAp) {
|
||||
reducedViableImplInCallContext(_, _, call) and
|
||||
recordDataFlowCallSite(call, _) and
|
||||
(emptyAp = true or emptyAp = false) and
|
||||
(
|
||||
exists(call.getArgument(i))
|
||||
@@ -593,6 +616,11 @@ private module ImplCommon {
|
||||
cached
|
||||
newtype TReturnPosition =
|
||||
TReturnPosition0(DataFlowCallable c, ReturnKind kind) { returnPosition(_, c, kind) }
|
||||
|
||||
cached
|
||||
newtype TLocalFlowCallContext =
|
||||
TAnyLocalCall() or
|
||||
TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) }
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -602,14 +630,15 @@ private module ImplCommon {
|
||||
}
|
||||
|
||||
/**
|
||||
* A call context to restrict the targets of virtual dispatch and match the
|
||||
* call sites of flow into a method with flow out of a method.
|
||||
* A call context to restrict the targets of virtual dispatch, prune local flow,
|
||||
* and match the call sites of flow into a method with flow out of a method.
|
||||
*
|
||||
* There are four cases:
|
||||
* - `TAnyCallContext()` : No restrictions on method flow.
|
||||
* - `TSpecificCall(DataFlowCall call, int i)` : Flow entered through the `i`th
|
||||
* parameter at the given `call`. This call improves the set of viable
|
||||
* dispatch targets for at least one method call in the current callable.
|
||||
* dispatch targets for at least one method call in the current callable
|
||||
* or helps prune unreachable nodes in the current callable.
|
||||
* - `TSomeCall(ParameterNode p)` : Flow entered through parameter `p`. The
|
||||
* originating call does not improve the set of dispatch targets for any
|
||||
* method call in the current callable and was therefore not recorded.
|
||||
@@ -619,10 +648,15 @@ private module ImplCommon {
|
||||
*/
|
||||
abstract class CallContext extends TCallContext {
|
||||
abstract string toString();
|
||||
|
||||
/** Holds if this call context is relevant for `callable`. */
|
||||
abstract predicate relevantFor(DataFlowCallable callable);
|
||||
}
|
||||
|
||||
class CallContextAny extends CallContext, TAnyCallContext {
|
||||
override string toString() { result = "CcAny" }
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) { any() }
|
||||
}
|
||||
|
||||
abstract class CallContextCall extends CallContext { }
|
||||
@@ -633,16 +667,73 @@ private module ImplCommon {
|
||||
result = "CcCall(" + call + ", " + i + ")"
|
||||
)
|
||||
}
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) {
|
||||
recordDataFlowCallSite(getCall(), callable)
|
||||
}
|
||||
|
||||
DataFlowCall getCall() { this = TSpecificCall(result, _, _) }
|
||||
}
|
||||
|
||||
class CallContextSomeCall extends CallContextCall, TSomeCall {
|
||||
override string toString() { result = "CcSomeCall" }
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) {
|
||||
exists(ParameterNode p | this = TSomeCall(p, _) and p.getEnclosingCallable() = callable)
|
||||
}
|
||||
}
|
||||
|
||||
class CallContextReturn extends CallContext, TReturn {
|
||||
override string toString() {
|
||||
exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
|
||||
}
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) {
|
||||
exists(DataFlowCall call | this = TReturn(_, call) and call.getEnclosingCallable() = callable)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call context that is relevant for pruning local flow.
|
||||
*/
|
||||
abstract class LocalCallContext extends TLocalFlowCallContext {
|
||||
abstract string toString();
|
||||
|
||||
/** Holds if this call context is relevant for `callable`. */
|
||||
abstract predicate relevantFor(DataFlowCallable callable);
|
||||
}
|
||||
|
||||
class LocalCallContextAny extends LocalCallContext, TAnyLocalCall {
|
||||
override string toString() { result = "LocalCcAny" }
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) { any() }
|
||||
}
|
||||
|
||||
class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall {
|
||||
LocalCallContextSpecificCall() { this = TSpecificLocalCall(call) }
|
||||
|
||||
DataFlowCall call;
|
||||
|
||||
DataFlowCall getCall() { result = call }
|
||||
|
||||
override string toString() { result = "LocalCcCall(" + call + ")" }
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) { relevantLocalCCtx(call, callable) }
|
||||
}
|
||||
|
||||
private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) {
|
||||
exists(Node n | n.getEnclosingCallable() = callable and isUnreachableInCall(n, call))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local call context given the call context and the callable that
|
||||
* the contexts apply to.
|
||||
*/
|
||||
LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) {
|
||||
ctx.relevantFor(callable) and
|
||||
if relevantLocalCCtx(ctx.(CallContextSpecificCall).getCall(), callable)
|
||||
then result.(LocalCallContextSpecificCall).getCall() = ctx.(CallContextSpecificCall).getCall()
|
||||
else result instanceof LocalCallContextAny
|
||||
}
|
||||
|
||||
/** A callable tagged with a relevant return kind. */
|
||||
|
||||
@@ -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
|
||||
@@ -1122,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
|
||||
)
|
||||
@@ -1261,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() {
|
||||
@@ -1277,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))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1294,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() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1363,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()
|
||||
)
|
||||
@@ -1472,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
|
||||
)
|
||||
@@ -1626,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1729,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
|
||||
@@ -1880,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)
|
||||
)
|
||||
@@ -2071,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() {
|
||||
@@ -2083,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() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2161,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2205,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
|
||||
@@ -2378,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
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.dataflow.internal.FlowVar
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.controlflow.Guards
|
||||
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
cached
|
||||
private newtype TNode =
|
||||
@@ -25,7 +27,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 +251,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 +510,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 +553,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
|
||||
@@ -658,12 +682,16 @@ VariableAccess getAnAccessToAssignedVariable(Expr assign) {
|
||||
*
|
||||
* It is important that all extending classes in scope are disjoint.
|
||||
*/
|
||||
class BarrierGuard extends Expr {
|
||||
/** NOT YET SUPPORTED. Holds if this guard validates `e` upon evaluating to `branch`. */
|
||||
abstract deprecated predicate checks(Expr e, boolean branch);
|
||||
class BarrierGuard extends GuardCondition {
|
||||
/** Override this predicate to hold if this guard validates `e` upon evaluating to `b`. */
|
||||
abstract predicate checks(Expr e, boolean b);
|
||||
|
||||
/** Gets a node guarded by this guard. */
|
||||
final Node getAGuardedNode() {
|
||||
none() // stub
|
||||
final ExprNode getAGuardedNode() {
|
||||
exists(GVN value, boolean branch |
|
||||
result.getExpr() = value.getAnExpr() and
|
||||
this.checks(value.getAnExpr(), branch) and
|
||||
this.controls(result.getExpr().getBasicBlock(), branch)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
87
cpp/ql/src/semmle/code/cpp/dispatch/VirtualDispatch.qll
Normal file
87
cpp/ql/src/semmle/code/cpp/dispatch/VirtualDispatch.qll
Normal file
@@ -0,0 +1,87 @@
|
||||
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")
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper predicate for `getAViableTarget`, which computes the viable targets for
|
||||
* virtual calls based on the qualifier type.
|
||||
*/
|
||||
private Function getAViableVirtualCallTarget(Class qualifierType, MemberFunction staticTarget) {
|
||||
exists(Class qualifierSubType |
|
||||
result = getAPossibleImplementation(staticTarget) and
|
||||
qualifierType = qualifierSubType.getABaseClass*() and
|
||||
mayInherit(qualifierSubType, result) and
|
||||
not cannotInherit(qualifierSubType, result)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable target for the given function call.
|
||||
*
|
||||
* 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) {
|
||||
if c.(FunctionCall).isVirtual() and c.getTarget() instanceof MemberFunction
|
||||
then result = getAViableVirtualCallTarget(getCallQualifierType(c), c.getTarget())
|
||||
else result = c.getTarget()
|
||||
}
|
||||
|
||||
/** Holds if `f` is declared in `c` or a transitive base class of `c`. */
|
||||
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) }
|
||||
}
|
||||
|
||||
@@ -139,17 +139,29 @@ class FunctionCall extends Call, @funbindexpr {
|
||||
override string getCanonicalQLClass() { result = "FunctionCall" }
|
||||
|
||||
/** Gets an explicit template argument for this call. */
|
||||
Type getAnExplicitTemplateArgument() { result = getExplicitTemplateArgument(_) }
|
||||
Locatable getAnExplicitTemplateArgument() { result = getExplicitTemplateArgument(_) }
|
||||
|
||||
/** Gets an explicit template argument value for this call. */
|
||||
Locatable getAnExplicitTemplateArgumentKind() { result = getExplicitTemplateArgumentKind(_) }
|
||||
|
||||
/** Gets a template argument for this call. */
|
||||
Type getATemplateArgument() { result = getTarget().getATemplateArgument() }
|
||||
Locatable getATemplateArgument() { result = getTarget().getATemplateArgument() }
|
||||
|
||||
/** Gets a template argument value for this call. */
|
||||
Locatable getATemplateArgumentKind() { result = getTarget().getATemplateArgumentKind() }
|
||||
|
||||
/** Gets the nth explicit template argument for this call. */
|
||||
Type getExplicitTemplateArgument(int n) {
|
||||
Locatable getExplicitTemplateArgument(int n) {
|
||||
n < getNumberOfExplicitTemplateArguments() and
|
||||
result = getTemplateArgument(n)
|
||||
}
|
||||
|
||||
/** Gets the nth explicit template argument value for this call. */
|
||||
Locatable getExplicitTemplateArgumentKind(int n) {
|
||||
n < getNumberOfExplicitTemplateArguments() and
|
||||
result = getTemplateArgumentKind(n)
|
||||
}
|
||||
|
||||
/** Gets the number of explicit template arguments for this call. */
|
||||
int getNumberOfExplicitTemplateArguments() {
|
||||
if numtemplatearguments(underlyingElement(this), _)
|
||||
@@ -161,7 +173,10 @@ class FunctionCall extends Call, @funbindexpr {
|
||||
int getNumberOfTemplateArguments() { result = count(int i | exists(getTemplateArgument(i))) }
|
||||
|
||||
/** Gets the nth template argument for this call (indexed from 0). */
|
||||
Type getTemplateArgument(int n) { result = getTarget().getTemplateArgument(n) }
|
||||
Locatable getTemplateArgument(int n) { result = getTarget().getTemplateArgument(n) }
|
||||
|
||||
/** Gets the nth template argument value for this call (indexed from 0). */
|
||||
Locatable getTemplateArgumentKind(int n) { result = getTarget().getTemplateArgumentKind(n) }
|
||||
|
||||
/** Holds if any template arguments for this call are implicit / deduced. */
|
||||
predicate hasImplicitTemplateArguments() {
|
||||
|
||||
@@ -540,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) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 {
|
||||
@@ -45,7 +37,7 @@ private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
|
||||
}
|
||||
|
||||
private predicate accessesVariable(CopyInstruction copy, Variable var) {
|
||||
exists(VariableAddressInstruction va | va.getVariable().getAST() = var |
|
||||
exists(VariableAddressInstruction va | va.getASTVariable() = var |
|
||||
copy.(StoreInstruction).getDestinationAddress() = va
|
||||
or
|
||||
copy.(LoadInstruction).getSourceAddress() = va
|
||||
|
||||
@@ -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
|
||||
@@ -1122,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
|
||||
)
|
||||
@@ -1261,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() {
|
||||
@@ -1277,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))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1294,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() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1363,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()
|
||||
)
|
||||
@@ -1472,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
|
||||
)
|
||||
@@ -1626,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1729,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
|
||||
@@ -1880,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)
|
||||
)
|
||||
@@ -2071,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() {
|
||||
@@ -2083,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() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2161,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2205,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
|
||||
@@ -2378,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)
|
||||
)
|
||||
|
||||
@@ -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
|
||||
@@ -1122,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
|
||||
)
|
||||
@@ -1261,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() {
|
||||
@@ -1277,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))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1294,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() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1363,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()
|
||||
)
|
||||
@@ -1472,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
|
||||
)
|
||||
@@ -1626,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1729,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
|
||||
@@ -1880,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)
|
||||
)
|
||||
@@ -2071,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() {
|
||||
@@ -2083,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() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2161,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2205,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
|
||||
@@ -2378,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)
|
||||
)
|
||||
|
||||
@@ -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
|
||||
@@ -1122,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
|
||||
)
|
||||
@@ -1261,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() {
|
||||
@@ -1277,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))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1294,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() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1363,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()
|
||||
)
|
||||
@@ -1472,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
|
||||
)
|
||||
@@ -1626,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1729,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
|
||||
@@ -1880,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)
|
||||
)
|
||||
@@ -2071,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() {
|
||||
@@ -2083,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() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2161,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2205,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
|
||||
@@ -2378,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)
|
||||
)
|
||||
|
||||
@@ -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
|
||||
@@ -1122,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
|
||||
)
|
||||
@@ -1261,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() {
|
||||
@@ -1277,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))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1294,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() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1363,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()
|
||||
)
|
||||
@@ -1472,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
|
||||
)
|
||||
@@ -1626,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1729,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
|
||||
@@ -1880,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)
|
||||
)
|
||||
@@ -2071,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() {
|
||||
@@ -2083,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() + ")]"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2161,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2205,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
|
||||
@@ -2378,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)
|
||||
)
|
||||
|
||||
@@ -120,13 +120,16 @@ private module ImplCommon {
|
||||
int i, ArgumentNode arg, CallContext outercc, DataFlowCall call
|
||||
) {
|
||||
exists(DataFlowCallable c | argumentOf(call, i, arg, c) |
|
||||
outercc = TAnyCallContext()
|
||||
or
|
||||
outercc = TSomeCall(getAParameter(c), _)
|
||||
or
|
||||
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
|
||||
reducedViableImplInCallContext(_, c, other)
|
||||
)
|
||||
(
|
||||
outercc = TAnyCallContext()
|
||||
or
|
||||
outercc = TSomeCall(getAParameter(c), _)
|
||||
or
|
||||
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
|
||||
recordDataFlowCallSite(other, c)
|
||||
)
|
||||
) and
|
||||
not isUnreachableInCall(arg, outercc.(CallContextSpecificCall).getCall())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -152,7 +155,7 @@ private module ImplCommon {
|
||||
exists(int i, DataFlowCallable callable |
|
||||
viableParamArg1(p, callable, i, arg, outercc, call)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, true)
|
||||
else innercc = TSomeCall(p, true)
|
||||
)
|
||||
@@ -164,7 +167,7 @@ private module ImplCommon {
|
||||
exists(DataFlowCall call, int i, DataFlowCallable callable |
|
||||
result = TSpecificCall(call, i, _) and
|
||||
p.isParameterOf(callable, i) and
|
||||
reducedViableImplInCallContext(_, callable, call)
|
||||
recordDataFlowCallSite(call, callable)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -180,14 +183,16 @@ private module ImplCommon {
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid, cc) and
|
||||
step(mid, node) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
compatibleTypes(p.getType(), node.getType()) and
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(Node arg |
|
||||
parameterValueFlow(p, arg, cc) and
|
||||
argumentValueFlowsThrough(arg, node, cc) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
compatibleTypes(p.getType(), node.getType()) and
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -220,6 +225,7 @@ private module ImplCommon {
|
||||
argumentValueFlowsThrough0(call, arg, kind, cc)
|
||||
|
|
||||
out = getAnOutNode(call, kind) and
|
||||
not isUnreachableInCall(out, cc.(CallContextSpecificCall).getCall()) and
|
||||
compatibleTypes(arg.getType(), out.getType())
|
||||
)
|
||||
}
|
||||
@@ -462,13 +468,16 @@ private module ImplCommon {
|
||||
int i, ArgumentNode arg, CallContext outercc, DataFlowCall call
|
||||
) {
|
||||
exists(DataFlowCallable c | argumentOf(call, i, arg, c) |
|
||||
outercc = TAnyCallContext()
|
||||
or
|
||||
outercc = TSomeCall(getAParameter(c), _)
|
||||
or
|
||||
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
|
||||
reducedViableImplInCallContext(_, c, other)
|
||||
)
|
||||
(
|
||||
outercc = TAnyCallContext()
|
||||
or
|
||||
outercc = TSomeCall(getAParameter(c), _)
|
||||
or
|
||||
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
|
||||
recordDataFlowCallSite(other, c)
|
||||
)
|
||||
) and
|
||||
not isUnreachableInCall(arg, outercc.(CallContextSpecificCall).getCall())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -494,7 +503,7 @@ private module ImplCommon {
|
||||
exists(int i, DataFlowCallable callable |
|
||||
viableParamArg1(p, callable, i, arg, outercc, call)
|
||||
|
|
||||
if reducedViableImplInCallContext(_, callable, call)
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
then innercc = TSpecificCall(call, i, true)
|
||||
else innercc = TSomeCall(p, true)
|
||||
)
|
||||
@@ -506,7 +515,7 @@ private module ImplCommon {
|
||||
exists(DataFlowCall call, int i, DataFlowCallable callable |
|
||||
result = TSpecificCall(call, i, _) and
|
||||
p.isParameterOf(callable, i) and
|
||||
reducedViableImplInCallContext(_, callable, call)
|
||||
recordDataFlowCallSite(call, callable)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -522,14 +531,16 @@ private module ImplCommon {
|
||||
exists(Node mid |
|
||||
parameterValueFlow(p, mid, cc) and
|
||||
step(mid, node) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
compatibleTypes(p.getType(), node.getType()) and
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(Node arg |
|
||||
parameterValueFlow(p, arg, cc) and
|
||||
argumentValueFlowsThrough(arg, node, cc) and
|
||||
compatibleTypes(p.getType(), node.getType())
|
||||
compatibleTypes(p.getType(), node.getType()) and
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -562,6 +573,7 @@ private module ImplCommon {
|
||||
argumentValueFlowsThrough0(call, arg, kind, cc)
|
||||
|
|
||||
out = getAnOutNode(call, kind) and
|
||||
not isUnreachableInCall(out, cc.(CallContextSpecificCall).getCall()) and
|
||||
compatibleTypes(arg.getType(), out.getType())
|
||||
)
|
||||
}
|
||||
@@ -575,11 +587,22 @@ private module ImplCommon {
|
||||
exists(ArgumentNode arg | arg.argumentOf(call, -1))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` either improves virtual dispatch in
|
||||
* `callable` or if it allows us to prune unreachable nodes in `callable`.
|
||||
*/
|
||||
cached
|
||||
predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
|
||||
reducedViableImplInCallContext(_, callable, call)
|
||||
or
|
||||
exists(Node n | n.getEnclosingCallable() = callable | isUnreachableInCall(n, call))
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TCallContext =
|
||||
TAnyCallContext() or
|
||||
TSpecificCall(DataFlowCall call, int i, boolean emptyAp) {
|
||||
reducedViableImplInCallContext(_, _, call) and
|
||||
recordDataFlowCallSite(call, _) and
|
||||
(emptyAp = true or emptyAp = false) and
|
||||
(
|
||||
exists(call.getArgument(i))
|
||||
@@ -593,6 +616,11 @@ private module ImplCommon {
|
||||
cached
|
||||
newtype TReturnPosition =
|
||||
TReturnPosition0(DataFlowCallable c, ReturnKind kind) { returnPosition(_, c, kind) }
|
||||
|
||||
cached
|
||||
newtype TLocalFlowCallContext =
|
||||
TAnyLocalCall() or
|
||||
TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) }
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -602,14 +630,15 @@ private module ImplCommon {
|
||||
}
|
||||
|
||||
/**
|
||||
* A call context to restrict the targets of virtual dispatch and match the
|
||||
* call sites of flow into a method with flow out of a method.
|
||||
* A call context to restrict the targets of virtual dispatch, prune local flow,
|
||||
* and match the call sites of flow into a method with flow out of a method.
|
||||
*
|
||||
* There are four cases:
|
||||
* - `TAnyCallContext()` : No restrictions on method flow.
|
||||
* - `TSpecificCall(DataFlowCall call, int i)` : Flow entered through the `i`th
|
||||
* parameter at the given `call`. This call improves the set of viable
|
||||
* dispatch targets for at least one method call in the current callable.
|
||||
* dispatch targets for at least one method call in the current callable
|
||||
* or helps prune unreachable nodes in the current callable.
|
||||
* - `TSomeCall(ParameterNode p)` : Flow entered through parameter `p`. The
|
||||
* originating call does not improve the set of dispatch targets for any
|
||||
* method call in the current callable and was therefore not recorded.
|
||||
@@ -619,10 +648,15 @@ private module ImplCommon {
|
||||
*/
|
||||
abstract class CallContext extends TCallContext {
|
||||
abstract string toString();
|
||||
|
||||
/** Holds if this call context is relevant for `callable`. */
|
||||
abstract predicate relevantFor(DataFlowCallable callable);
|
||||
}
|
||||
|
||||
class CallContextAny extends CallContext, TAnyCallContext {
|
||||
override string toString() { result = "CcAny" }
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) { any() }
|
||||
}
|
||||
|
||||
abstract class CallContextCall extends CallContext { }
|
||||
@@ -633,16 +667,73 @@ private module ImplCommon {
|
||||
result = "CcCall(" + call + ", " + i + ")"
|
||||
)
|
||||
}
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) {
|
||||
recordDataFlowCallSite(getCall(), callable)
|
||||
}
|
||||
|
||||
DataFlowCall getCall() { this = TSpecificCall(result, _, _) }
|
||||
}
|
||||
|
||||
class CallContextSomeCall extends CallContextCall, TSomeCall {
|
||||
override string toString() { result = "CcSomeCall" }
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) {
|
||||
exists(ParameterNode p | this = TSomeCall(p, _) and p.getEnclosingCallable() = callable)
|
||||
}
|
||||
}
|
||||
|
||||
class CallContextReturn extends CallContext, TReturn {
|
||||
override string toString() {
|
||||
exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
|
||||
}
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) {
|
||||
exists(DataFlowCall call | this = TReturn(_, call) and call.getEnclosingCallable() = callable)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call context that is relevant for pruning local flow.
|
||||
*/
|
||||
abstract class LocalCallContext extends TLocalFlowCallContext {
|
||||
abstract string toString();
|
||||
|
||||
/** Holds if this call context is relevant for `callable`. */
|
||||
abstract predicate relevantFor(DataFlowCallable callable);
|
||||
}
|
||||
|
||||
class LocalCallContextAny extends LocalCallContext, TAnyLocalCall {
|
||||
override string toString() { result = "LocalCcAny" }
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) { any() }
|
||||
}
|
||||
|
||||
class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall {
|
||||
LocalCallContextSpecificCall() { this = TSpecificLocalCall(call) }
|
||||
|
||||
DataFlowCall call;
|
||||
|
||||
DataFlowCall getCall() { result = call }
|
||||
|
||||
override string toString() { result = "LocalCcCall(" + call + ")" }
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) { relevantLocalCCtx(call, callable) }
|
||||
}
|
||||
|
||||
private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) {
|
||||
exists(Node n | n.getEnclosingCallable() = callable and isUnreachableInCall(n, call))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local call context given the call context and the callable that
|
||||
* the contexts apply to.
|
||||
*/
|
||||
LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) {
|
||||
ctx.relevantFor(callable) and
|
||||
if relevantLocalCCtx(ctx.(CallContextSpecificCall).getCall(), callable)
|
||||
then result.(LocalCallContextSpecificCall).getCall() = ctx.(CallContextSpecificCall).getCall()
|
||||
else result instanceof LocalCallContextAny
|
||||
}
|
||||
|
||||
/** A callable tagged with a relevant return kind. */
|
||||
|
||||
@@ -204,3 +204,5 @@ class DataFlowCall extends CallInstruction {
|
||||
|
||||
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
|
||||
}
|
||||
|
||||
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.controlflow.IRGuards
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
|
||||
/**
|
||||
* A newtype wrapper to prevent accidental casts between `Node` and
|
||||
@@ -213,6 +214,14 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
|
||||
*/
|
||||
predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `i1` to `i2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
predicate localInstructionFlow(Instruction e1, Instruction e2) {
|
||||
localFlow(instructionNode(e1), instructionNode(e2))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `e1` to `e2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
@@ -220,7 +229,7 @@ predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
|
||||
predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)) }
|
||||
|
||||
/**
|
||||
* A guard that validates some expression.
|
||||
* A guard that validates some instruction.
|
||||
*
|
||||
* To use this in a configuration, extend the class and provide a
|
||||
* characteristic predicate precisely specifying the guard, and override
|
||||
@@ -229,11 +238,15 @@ predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)
|
||||
* It is important that all extending classes in scope are disjoint.
|
||||
*/
|
||||
class BarrierGuard extends IRGuardCondition {
|
||||
/** NOT YET SUPPORTED. Holds if this guard validates `e` upon evaluating to `b`. */
|
||||
abstract deprecated predicate checks(Instruction e, boolean b);
|
||||
/** Override this predicate to hold if this guard validates `instr` upon evaluating to `b`. */
|
||||
abstract predicate checks(Instruction instr, boolean b);
|
||||
|
||||
/** Gets a node guarded by this guard. */
|
||||
final Node getAGuardedNode() {
|
||||
none() // stub
|
||||
exists(ValueNumber value, boolean edge |
|
||||
result.asInstruction() = value.getAnInstruction() and
|
||||
this.checks(value.getAnInstruction(), edge) and
|
||||
this.controls(result.asInstruction().getBlock(), edge)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,14 @@ private predicate localInstructionTaintStep(Instruction nodeFrom, Instruction no
|
||||
*/
|
||||
predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) }
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `i1` to `i2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
predicate localInstructionTaint(Instruction i1, Instruction i2) {
|
||||
localTaint(DataFlow::instructionNode(i1), DataFlow::instructionNode(i2))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `e1` to `e2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
|
||||
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
|
||||
}
|
||||
@@ -5,6 +5,7 @@ private newtype TMemoryAccessKind =
|
||||
TBufferMayMemoryAccess() or
|
||||
TEscapedMemoryAccess() or
|
||||
TEscapedMayMemoryAccess() or
|
||||
TNonLocalMayMemoryAccess() or
|
||||
TPhiMemoryAccess() or
|
||||
TUnmodeledMemoryAccess() or
|
||||
TChiTotalMemoryAccess() or
|
||||
@@ -80,6 +81,14 @@ class EscapedMayMemoryAccess extends MemoryAccessKind, TEscapedMayMemoryAccess {
|
||||
override string toString() { result = "escaped(may)" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand or result may access all memory whose address has escaped, other than data on the
|
||||
* stack frame of the current function.
|
||||
*/
|
||||
class NonLocalMayMemoryAccess extends MemoryAccessKind, TNonLocalMayMemoryAccess {
|
||||
override string toString() { result = "nonlocal(may)" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand is a Phi operand, which accesses the same memory as its
|
||||
* definition.
|
||||
|
||||
@@ -57,6 +57,7 @@ private newtype TOpcode =
|
||||
TUnmodeledDefinition() or
|
||||
TUnmodeledUse() or
|
||||
TAliasedDefinition() or
|
||||
TAliasedUse() or
|
||||
TPhi() or
|
||||
TBuiltIn() or
|
||||
TVarArgsStart() or
|
||||
@@ -393,6 +394,10 @@ module Opcode {
|
||||
final override string toString() { result = "AliasedDefinition" }
|
||||
}
|
||||
|
||||
class AliasedUse extends Opcode, TAliasedUse {
|
||||
final override string toString() { result = "AliasedUse" }
|
||||
}
|
||||
|
||||
class Phi extends Opcode, TPhi {
|
||||
final override string toString() { result = "Phi" }
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -49,7 +50,8 @@ module InstructionSanity {
|
||||
(
|
||||
opcode instanceof ReadSideEffectOpcode or
|
||||
opcode instanceof Opcode::InlineAsm or
|
||||
opcode instanceof Opcode::CallSideEffect
|
||||
opcode instanceof Opcode::CallSideEffect or
|
||||
opcode instanceof Opcode::AliasedUse
|
||||
) and
|
||||
tag instanceof SideEffectOperandTag
|
||||
)
|
||||
@@ -113,13 +115,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.
|
||||
*/
|
||||
@@ -251,6 +264,7 @@ module InstructionSanity {
|
||||
) {
|
||||
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
|
||||
not useOperand.getUse() instanceof UnmodeledUseInstruction and
|
||||
not defInstr instanceof UnmodeledDefinitionInstruction and
|
||||
pointOfEvaluation(useOperand, useBlock, useIndex) and
|
||||
defInstr = useOperand.getAnyDef() and
|
||||
(
|
||||
@@ -312,7 +326,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 +358,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 +377,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 +447,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 +464,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 +493,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 +502,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.
|
||||
@@ -611,7 +620,12 @@ class VariableInstruction extends Instruction {
|
||||
|
||||
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 {
|
||||
@@ -1370,7 +1384,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
|
||||
@@ -1382,7 +1396,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 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1409,6 +1423,13 @@ class AliasedDefinitionInstruction extends Instruction {
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that consumes all escaped memory on exit from the function.
|
||||
*/
|
||||
class AliasedUseInstruction extends Instruction {
|
||||
AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse }
|
||||
}
|
||||
|
||||
class UnmodeledUseInstruction extends Instruction {
|
||||
UnmodeledUseInstruction() { getOpcode() instanceof Opcode::UnmodeledUse }
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -381,13 +395,10 @@ 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 AliasedUseInstruction and
|
||||
result instanceof NonLocalMayMemoryAccess
|
||||
or
|
||||
useInstr instanceof CallSideEffectInstruction and
|
||||
result instanceof EscapedMayMemoryAccess
|
||||
or
|
||||
|
||||
@@ -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,31 +10,42 @@ 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
|
||||
TUnknownNonLocalMemoryLocation(IRFunction irFunc) or
|
||||
TUnknownVirtualVariable(IRFunction irFunc)
|
||||
|
||||
/**
|
||||
@@ -49,9 +60,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 +75,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 +112,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 +127,7 @@ class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation {
|
||||
*/
|
||||
final predicate coversEntireVariable() {
|
||||
startBitOffset = 0 and
|
||||
endBitOffset = var.getType().getSize() * 8
|
||||
endBitOffset = var.getIRType().getByteSize() * 8
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +139,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,11 +156,33 @@ 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}" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An access to memory that is not known to be confined to a specific `IRVariable`, but is known to
|
||||
* not access memory on the current function's stack frame.
|
||||
*/
|
||||
class UnknownNonLocalMemoryLocation extends TUnknownNonLocalMemoryLocation, MemoryLocation {
|
||||
IRFunction irFunc;
|
||||
|
||||
UnknownNonLocalMemoryLocation() { this = TUnknownNonLocalMemoryLocation(irFunc) }
|
||||
|
||||
final override string toString() { result = "{UnknownNonLocal}" }
|
||||
|
||||
final override VirtualVariable getVirtualVariable() { result = TUnknownVirtualVariable(irFunc) }
|
||||
|
||||
final override Language::LanguageType getType() {
|
||||
result = any(IRUnknownType type).getCanonicalLanguageType()
|
||||
}
|
||||
|
||||
final override string getUniqueId() { result = "{UnknownNonLocal}" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An access to all aliased memory.
|
||||
*/
|
||||
@@ -143,7 +193,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() }
|
||||
|
||||
@@ -163,6 +215,13 @@ Overlap getOverlap(MemoryLocation def, MemoryLocation use) {
|
||||
def instanceof UnknownMemoryLocation and
|
||||
result instanceof MayPartiallyOverlap
|
||||
or
|
||||
// An UnknownNonLocalMemoryLocation may partially overlap any location within the same virtual
|
||||
// variable, except a local variable.
|
||||
def.getVirtualVariable() = use.getVirtualVariable() and
|
||||
def instanceof UnknownNonLocalMemoryLocation and
|
||||
result instanceof MayPartiallyOverlap and
|
||||
not use.(VariableMemoryLocation).getVariable() instanceof IRAutomaticVariable
|
||||
or
|
||||
exists(VariableMemoryLocation defVariableLocation |
|
||||
defVariableLocation = def and
|
||||
(
|
||||
@@ -171,13 +230,20 @@ Overlap getOverlap(MemoryLocation def, MemoryLocation use) {
|
||||
(use instanceof UnknownMemoryLocation or use instanceof UnknownVirtualVariable) and
|
||||
result instanceof MayPartiallyOverlap
|
||||
or
|
||||
// A VariableMemoryLocation that is not a local variable may partially overlap an unknown
|
||||
// non-local location within the same virtual variable.
|
||||
def.getVirtualVariable() = use.getVirtualVariable() and
|
||||
use instanceof UnknownNonLocalMemoryLocation and
|
||||
result instanceof MayPartiallyOverlap and
|
||||
not defVariableLocation.getVariable() instanceof IRAutomaticVariable
|
||||
or
|
||||
// A VariableMemoryLocation overlaps another location within the same variable based on the relationship
|
||||
// of the two offset intervals.
|
||||
exists(Overlap intervalOverlap |
|
||||
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 +348,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())
|
||||
)
|
||||
@@ -296,6 +362,9 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
|
||||
or
|
||||
kind instanceof EscapedMayMemoryAccess and
|
||||
result = TUnknownMemoryLocation(instr.getEnclosingIRFunction())
|
||||
or
|
||||
kind instanceof NonLocalMayMemoryAccess and
|
||||
result = TUnknownNonLocalMemoryLocation(instr.getEnclosingIRFunction())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -306,11 +375,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())
|
||||
)
|
||||
@@ -320,6 +389,9 @@ MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
|
||||
or
|
||||
kind instanceof EscapedMayMemoryAccess and
|
||||
result = TUnknownMemoryLocation(operand.getEnclosingIRFunction())
|
||||
or
|
||||
kind instanceof NonLocalMayMemoryAccess and
|
||||
result = TUnknownNonLocalMemoryLocation(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,11 +318,11 @@ 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()
|
||||
}
|
||||
|
||||
@@ -348,7 +332,7 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
Function getInstructionFunction(Instruction instruction) {
|
||||
Language::Function getInstructionFunction(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
|
||||
}
|
||||
|
||||
@@ -358,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()
|
||||
}
|
||||
|
||||
@@ -380,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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user