Merge branch 'master' into rdmarsh/cpp/hasGlobalOrStdName

This commit is contained in:
Robert Marsh
2019-11-08 11:33:35 -08:00
1110 changed files with 55404 additions and 17972 deletions

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

@@ -0,0 +1,14 @@
---
name: General issue
about: Tell us if you think something is wrong or if you have a question
title: General issue
labels: question
assignees: ''
---
**Description of the issue**
<!-- Please explain briefly what is the problem.
If it is about an LGTM project, please include its URL.-->

3
.gitignore vendored
View File

@@ -12,3 +12,6 @@
# Visual studio temporaries, except a file used by QL4VS
.vs/*
!.vs/VSWorkspaceSettings.json
# It's useful (though not required) to be able to unpack codeql in the ql checkout itself
/codeql/

View File

@@ -2,9 +2,9 @@
/java/ @Semmle/java
/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

View File

@@ -36,6 +36,7 @@
| Shift out of range (`js/shift-out-of-range`| Fewer false positive results | This rule now correctly handles BigInt shift operands. |
| 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -21,6 +21,7 @@ from Variable v
where
v.isStatic() and
v.hasDefinition() and
not v.isConstexpr() and
not exists(VariableAccess a | a.getTarget() = v) and
not v instanceof MemberVariable and
not declarationHasSideEffects(v) and

View File

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

View File

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

View File

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

View File

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

View File

@@ -30,7 +30,7 @@ private predicate additionalLogicalCheck(Expr e, string operation, int valueToCh
/**
* An `Operation` that seems to be checking for leap year.
*/
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" }
}
/**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,15 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Using TLS or SSLv23 protool from the boost::asio library, but not disabling deprecated protocols or disabling minimum-recommended protocols.</p>
</overview>
<references>
<li>
<a href="https://www.boost.org/doc/libs/1_71_0/doc/html/boost_asio.html">Boost.Asio documentation</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,119 @@
/**
* @name Boost_asio TLS Settings Misconfiguration
* @description Using TLS or SSLv23 protool from the boost::asio library, but not disabling deprecated protocols or disabling minimum-recommended protocols
* @kind problem
* @problem.severity error
* @id cpp/boost/tls_settings_misconfiguration
* @tags security
*/
import cpp
import semmle.code.cpp.security.boostorg.asio.protocols
class ExistsAnyFlowConfig extends DataFlow::Configuration {
ExistsAnyFlowConfig() { this = "ExistsAnyFlowConfig" }
override predicate isSource(DataFlow::Node source) { any() }
override predicate isSink(DataFlow::Node sink) { any() }
}
bindingset[flag]
predicate isOptionSet(ConstructorCall cc, int flag, FunctionCall fcSetOptions) {
exists(
BoostorgAsio::SslContextFlowsToSetOptionConfig config, ExistsAnyFlowConfig testConfig,
Expr optionsSink
|
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
exists(VariableAccess contextSetOptions |
testConfig.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(contextSetOptions)) and
exists(BoostorgAsio::SslSetOptionsFunction f | f.getACallToThisFunction() = fcSetOptions |
contextSetOptions = fcSetOptions.getQualifier() and
forall(
Expr optionArgument, BoostorgAsio::SslOptionConfig optionArgConfig,
Expr optionArgumentSource
|
optionArgument = fcSetOptions.getArgument(0) and
optionArgConfig
.hasFlow(DataFlow::exprNode(optionArgumentSource), DataFlow::exprNode(optionArgument))
|
optionArgument.getValue().toInt().bitShiftRight(16).bitAnd(flag) = flag
)
)
)
)
}
bindingset[flag]
predicate isOptionNotSet(ConstructorCall cc, int flag) {
not exists(
BoostorgAsio::SslContextFlowsToSetOptionConfig config, ExistsAnyFlowConfig testConfig,
Expr optionsSink
|
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
exists(VariableAccess contextSetOptions |
testConfig.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(contextSetOptions)) and
exists(FunctionCall fcSetOptions, BoostorgAsio::SslSetOptionsFunction f |
f.getACallToThisFunction() = fcSetOptions
|
contextSetOptions = fcSetOptions.getQualifier() and
forall(
Expr optionArgument, BoostorgAsio::SslOptionConfig optionArgConfig,
Expr optionArgumentSource
|
optionArgument = fcSetOptions.getArgument(0) and
optionArgConfig
.hasFlow(DataFlow::exprNode(optionArgumentSource), DataFlow::exprNode(optionArgument))
|
optionArgument.getValue().toInt().bitShiftRight(16).bitAnd(flag) = flag
)
)
)
)
}
from
BoostorgAsio::SslContextCallTlsProtocolConfig configConstructor,
BoostorgAsio::SslContextFlowsToSetOptionConfig config, Expr protocolSource, Expr protocolSink,
ConstructorCall cc, Expr e, string msg
where
configConstructor.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink)) and
cc.getArgument(0) = protocolSink and
(
BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
not exists(Expr optionsSink |
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoSsl3(), _) and
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1(), _) and
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1(), _) and
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2())
)
or
BoostorgAsio::isExprTlsBoostProtocol(protocolSource) and
not BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
not exists(Expr optionsSink |
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1(), _) and
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1(), _) and
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2())
)
) and
(
BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoSsl3()) and
e = cc and
msg = "no_sslv3 has not been set"
or
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1()) and
e = cc and
msg = "no_tlsv1 has not been set"
or
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1()) and
e = cc and
msg = "no_tlsv1_1 has not been set"
or
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2(), e) and
msg = "no_tlsv1_2 was set"
)
select cc, "Usage of $@ with protocol $@ is not configured correctly: The option $@.", cc,
"boost::asio::ssl::context::context", protocolSource, protocolSource.toString(), e, msg

View File

@@ -0,0 +1,16 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Using boost::asio library but specifying a deprecated hardcoded protocol.</p>
<p>Using a deprecated hardcoded protocol instead of negotiting would lock your application to a protocol that has known vulnerabilities or weaknesses.</p>
</overview>
<references>
<li>
<a href="https://www.boost.org/doc/libs/1_71_0/doc/html/boost_asio.html">Boost.Asio documentation</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,27 @@
/**
* @name boost::asio Use of deprecated hardcoded Protocol
* @description Using a deprecated hard-coded protocol using the boost::asio library.
* @kind problem
* @problem.severity error
* @id cpp/boost/use-of-deprecated-hardcoded-security-protocol
* @tags security
*/
import cpp
import semmle.code.cpp.security.boostorg.asio.protocols
from
BoostorgAsio::SslContextCallConfig config, Expr protocolSource, Expr protocolSink,
ConstructorCall cc
where
config.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink)) and
not exists(BoostorgAsio::SslContextCallTlsProtocolConfig tlsConfig |
tlsConfig.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink))
) and
cc.getArgument(0) = protocolSink and
exists(BoostorgAsio::SslContextCallBannedProtocolConfig bannedConfig |
bannedConfig.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink))
)
select protocolSink, "Usage of $@ specifying a deprecated hardcoded protocol $@ in function $@.",
cc, "boost::asio::ssl::context::context", protocolSource, protocolSource.toString(),
cc.getEnclosingFunction(), cc.getEnclosingFunction().toString()

View File

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

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

View File

@@ -5,7 +5,7 @@
* @id cpp/comparison-with-wider-type
* @kind problem
* @problem.severity warning
* @precision medium
* @precision high
* @tags reliability
* security
* external/cwe/cwe-190

View File

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

View File

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

View File

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

View File

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

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

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

View File

@@ -0,0 +1,14 @@
- description: Standard LGTM queries for C/C++, including ones not displayed by default
- qlpack: codeql-cpp
- apply: lgtm-selectors.yml
from: codeql-suite-helpers
# These queries are infeasible to compute on large projects:
- exclude:
query path:
- Security/CWE/CWE-497/ExposedSystemData.ql
- Critical/DescriptorMayNotBeClosed.ql
- Critical/DescriptorNeverClosed.ql
- Critical/FileMayNotBeClosed.ql
- Critical/FileNeverClosed.ql
- Critical/MemoryMayNotBeFreed.ql
- Critical/MemoryNeverFreed.ql

View File

@@ -0,0 +1,4 @@
- description: Standard LGTM queries for C/C++
- apply: codeql-suites/cpp-lgtm-full.qls
- apply: lgtm-displayed-only.yml
from: codeql-suite-helpers

View File

@@ -51,7 +51,7 @@ class ReferenceCopyAssignmentOperator extends MemberFunction {
/**
* A call to a function called swap. Note: could be a member,
* 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() {

View File

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

View File

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

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

View File

@@ -605,15 +605,6 @@ class Class extends UserType {
class_instantiation(underlyingElement(this), unresolveElement(c))
}
/**
* Gets the `i`th template argument used to instantiate this class from a
* class template. When called on a class template, this will return the
* `i`th template parameter.
*/
override Type getTemplateArgument(int i) {
class_template_argument(underlyingElement(this), i, unresolveElement(result))
}
/**
* Holds if this class/struct is polymorphic (has a virtual function, or
* inherits one).
@@ -623,7 +614,7 @@ class Class extends UserType {
}
override predicate involvesTemplateParameter() {
getATemplateArgument().involvesTemplateParameter()
getATemplateArgument().(Type).involvesTemplateParameter()
}
/** Holds if this class, struct or union was declared 'final'. */

View File

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

View File

@@ -343,15 +343,6 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
function_instantiation(underlyingElement(this), unresolveElement(f))
}
/**
* Gets the `i`th template argument used to instantiate this function from a
* function template. When called on a function template, this will return the
* `i`th template parameter.
*/
override Type getTemplateArgument(int index) {
function_template_argument(underlyingElement(this), index, unresolveElement(result))
}
/**
* Holds if this function is defined in several files. This is illegal in
* C (though possible in some C++ compilers), and likely indicates that

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

View File

@@ -35,6 +35,14 @@ private string getParameterTypeString(Type parameterType) {
else result = parameterType.(DumpType).getTypeIdentityString()
}
private string getTemplateArgumentString(Declaration d, int i) {
if exists(d.getTemplateArgumentKind(i))
then
result = d.getTemplateArgumentKind(i).(DumpType).getTypeIdentityString() + " " +
d.getTemplateArgument(i)
else result = d.getTemplateArgument(i).(DumpType).getTypeIdentityString()
}
/**
* A `Declaration` extended to add methods for generating strings useful only for dumps and debugging.
*/
@@ -56,7 +64,7 @@ abstract private class DumpDeclaration extends Declaration {
strictconcat(int i |
exists(this.getTemplateArgument(i))
|
this.getTemplateArgument(i).(DumpType).getTypeIdentityString(), ", " order by i
getTemplateArgumentString(this, i), ", " order by i
) + ">"
else result = ""
}

View File

@@ -7,3 +7,15 @@
import cpp
import PrintAST
/**
* Temporarily tweak this class or make a copy to control which functions are
* printed.
*/
class Cfg extends PrintASTConfiguration {
/**
* TWEAK THIS PREDICATE AS NEEDED.
* Holds if the AST for `func` should be printed.
*/
override predicate shouldPrintFunction(Function func) { any() }
}

View File

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

View File

@@ -155,15 +155,6 @@ class Variable extends Declaration, @variable {
variable_instantiation(underlyingElement(this), unresolveElement(v))
}
/**
* Gets the `i`th template argument used to instantiate this variable from a
* variable template. When called on a variable template, this will return the
* `i`th template parameter.
*/
override Type getTemplateArgument(int index) {
variable_template_argument(underlyingElement(this), index, unresolveElement(result))
}
/**
* Holds if this is a compiler-generated variable. For example, a
* [range-based for loop](http://en.cppreference.com/w/cpp/language/range-for)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,6 +5,8 @@
private import cpp
private import semmle.code.cpp.dataflow.internal.FlowVar
private import semmle.code.cpp.models.interfaces.DataFlow
private import semmle.code.cpp.controlflow.Guards
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
cached
private newtype TNode =
@@ -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)
)
}
}

View File

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

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

View File

@@ -18,7 +18,18 @@ abstract class Access extends Expr, NameQualifiableElement {
}
/**
* A C/C++ enum constant access expression.
* A C/C++ `enum` constant access expression. For example the access to
* `MYENUMCONST1` in `myFunction` in the following code:
* ```
* enum MyEnum {
* MYENUMCONST1,
* MYENUMCONST2
* };
*
* void myFunction() {
* MyEnum v = MYENUMCONST1;
* };
* ```
*/
class EnumConstantAccess extends Access, @varaccess {
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) }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -204,3 +204,5 @@ class DataFlowCall extends CallInstruction {
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
}
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

@@ -1,2 +1,3 @@
private import IR
import InstructionSanity
import IRTypeSanity

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,2 @@
import semmle.code.cpp.ir.internal.Overlap
import semmle.code.cpp.ir.internal.IRCppLanguage as Language

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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