Merge from master and resolve conflicts

This commit is contained in:
Dave Bartolomeo
2019-12-04 10:14:52 -07:00
240 changed files with 12094 additions and 9744 deletions

View File

@@ -2,66 +2,65 @@
The following changes in version 1.23 affect C/C++ analysis in all applications. The following changes in version 1.23 affect C/C++ analysis in all applications.
## General improvements
## New queries ## New queries
| **Query** | **Tags** | **Purpose** | | **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------| |-----------------------------|-----------|--------------------------------------------------------------------|
| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | reliability, japanese-era | This query is a combination of two old queries that were identical in purpose but separate as an implementation detail. This new query replaces Hard-coded Japanese era start date in call (`cpp/japanese-era/constructor-or-method-with-exact-era-date`) and Hard-coded Japanese era start date in struct (`cpp/japanese-era/struct-with-exact-era-date`). | | Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | reliability, japanese-era | This query is a combination of two old queries that were identical in purpose but separate as an implementation detail. This new query replaces Hard-coded Japanese era start date in call (`cpp/japanese-era/constructor-or-method-with-exact-era-date`) and Hard-coded Japanese era start date in struct (`cpp/japanese-era/struct-with-exact-era-date`). Results are not shown on LGTM by default. |
| Signed overflow check (`cpp/signed-overflow-check`) | correctness, security | Finds overflow checks that rely on signed integer addition to overflow, which has undefined behavior. Example: `a + b < a`. | | Pointer overflow check (`cpp/pointer-overflow-check`) | correctness, security | Finds overflow checks that rely on pointer addition to overflow, which has undefined behavior. Example: `ptr + a < ptr`. Results are shown on LGTM by default. |
| Pointer overflow check (`cpp/pointer-overflow-check`) | correctness, security | Finds overflow checks that rely on pointer addition to overflow, which has undefined behavior. Example: `ptr + a < ptr`. | | Signed overflow check (`cpp/signed-overflow-check`) | correctness, security | Finds overflow checks that rely on signed integer addition to overflow, which has undefined behavior. Example: `a + b < a`. Results are shown on LGTM by default. |
## Changes to existing queries ## Changes to existing queries
| **Query** | **Expected impact** | **Change** | | **Query** | **Expected impact** | **Change** |
|----------------------------|------------------------|------------------------------------------------------------------| |----------------------------|------------------------|------------------------------------------------------------------|
| Query name (`query id`) | Expected impact | Message. | | 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. |
| Hard-coded Japanese era start date in call (`cpp/japanese-era/constructor-or-method-with-exact-era-date`) | Deprecated | This query has been deprecated. Use the new combined query Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) instead. | | Hard-coded Japanese era start date in call (`cpp/japanese-era/constructor-or-method-with-exact-era-date`) | Deprecated | This query has been deprecated. Use the new combined query Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) instead. |
| Hard-coded Japanese era start date in struct (`cpp/japanese-era/struct-with-exact-era-date`) | Deprecated | This query has been deprecated. Use the new combined query Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) instead. | | Hard-coded Japanese era start date in struct (`cpp/japanese-era/struct-with-exact-era-date`) | Deprecated | This query has been deprecated. Use the new combined query Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) instead. |
| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | More correct results | This query now checks for the beginning date of the Reiwa era (1st May 2019). | | Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | More correct results | This query now checks for the beginning date of the Reiwa era (1st May 2019). |
| Non-constant format string (`cpp/non-constant-format`) | Fewer false positive results | Fixed false positive results triggrered by mismatching declarations of a formatting function. |
| Sign check of bitwise operation (`cpp/bitwise-sign-check`) | Fewer false positive results | Results involving `>=` or `<=` are no longer reported. | | 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 few arguments to formatting function (`cpp/wrong-number-format-arguments`) | Fewer false positive results | Fixed false positive results triggered by mismatching 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. | | Too many arguments to formatting function (`cpp/too-many-format-arguments`) | Fewer false positive results | Fixed false positive results triggered by mismatching 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. | | Unclear comparison precedence (`cpp/comparison-precedence`) | Fewer false positive results | False positive results 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. | | Wrong type of arguments to formatting function (`cpp/wrong-type-format-argument`) | More correct results and fewer false positive results | This query now understands explicitly-specified argument numbers in format strings, such as the `1$` in `%1$s`. |
| Non-constant format string (`cpp/non-constant-format`) | Fewer false positive results | Fixed false positives resulting from mistmatching declarations of a formatting function. |
| Wrong type of arguments to formatting function (`cpp/wrong-type-format-argument`) | More correct results and fewer false positive results | This query now understands explicitly specified argument numbers in format strings, such as the `1$` in `%1$s`. |
## Changes to libraries ## Changes to libraries
* The data-flow library has been extended with a new feature to aid debugging. * The data-flow library in `semmle.code.cpp.dataflow.DataFlow` and
Instead of specifying `isSink(Node n) { any() }` on a configuration to `semmle.code.cpp.dataflow.TaintTracking` have had extensive changes:
explore the possible flow from a source, it is recommended to use the new * Data flow through fields is now more complete and reliable.
`Configuration::hasPartialFlow` predicate, as this gives a more complete * The data-flow library has been extended with a new feature to aid debugging.
picture of the partial flow paths from a given source. The feature is Previously, to explore the possible flow from all sources you could specify `isSink(Node n) { any() }` on a configuration.
disabled by default and can be enabled for individual configurations by Now you can use the new `Configuration::hasPartialFlow` predicate,
overriding `int explorationLimit()`. which gives a more complete picture of the partial flow paths from a given source, including flow that doesn't reach any sink.
* The data-flow library now supports flow out of C++ reference parameters. The feature is disabled by default and can be enabled for individual configurations by overriding `int explorationLimit()`.
* The data-flow library now allows flow through the address-of operator (`&`). * There is now flow out of C++ reference parameters.
* The `DataFlow::DefinitionByReferenceNode` class now considers `f(x)` to be a * There is now flow through the address-of operator (`&`).
definition of `x` when `x` is a variable of pointer type. It no longer * The `DataFlow::DefinitionByReferenceNode` class now considers `f(x)` to be a
considers deep paths such as `f(&x.myField)` to be definitions of `x`. These definition of `x` when `x` is a variable of pointer type. It no longer
changes are in line with the user expectations we've observed. considers deep paths such as `f(&x.myField)` to be definitions of `x`. These
* The data-flow library now makes it easier to specify barriers/sanitizers changes are in line with the user expectations we've observed.
arising from guards by overriding the predicate * It's now easier to specify barriers/sanitizers
`isBarrierGuard`/`isSanitizerGuard` on data-flow and taint-tracking arising from guards by overriding the predicate
configurations respectively. `isBarrierGuard`/`isSanitizerGuard` on data-flow and taint-tracking
* There is now a `DataFlow::localExprFlow` predicate and a configurations respectively.
`TaintTracking::localExprTaint` predicate to make it easy to use the most * There is now a `DataFlow::localExprFlow` predicate and a
common case of local data flow and taint: from one `Expr` to another. `TaintTracking::localExprTaint` predicate to make it easy to use the most
common case of local data flow and taint: from one `Expr` to another.
* The member predicates of the `FunctionInput` and `FunctionOutput` classes have been renamed for * The member predicates of the `FunctionInput` and `FunctionOutput` classes have been renamed for
clarity (e.g. `isOutReturnPointer()` to `isReturnValueDeref()`). The existing member predicates clarity (for example, `isOutReturnPointer()` to `isReturnValueDeref()`). The existing member predicates
have been deprecated, and will be removed in a future release. Code that uses the old member have been deprecated, and will be removed in a future release. Code that uses the old member
predicates should be updated to use the corresponding new member predicate. predicates should be updated to use the corresponding new member predicate.
* The predicates `Declaration.hasStdName()` and `Declaration.hasGlobalOrStdName` * The predicate `Declaration.hasGlobalOrStdName` has been added, making it
have been added, simplifying handling of C++ standard library functions. easier to recognize C library functions called from C++.
* The control-flow graph is now computed in QL, not in the extractor. This can * 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 lead to changes in how queries are optimized because
optimization in QL relies on static size estimates, and the control-flow edge optimization in QL relies on static size estimates, and the control-flow edge
relations will now have different size estimates than before. relations will now have different size estimates than before.
* Support has been added for non-type template arguments. This means that the * Support has been added for non-type template arguments. This means that the
return type of `Declaration::getTemplateArgument()` and return type of `Declaration::getTemplateArgument()` and
`Declaration::getATemplateArgument` have changed to `Locatable`. See the `Declaration::getATemplateArgument` have changed to `Locatable`. For details, see the
documentation for `Declaration::getTemplateArgument()` and CodeQL library documentation for `Declaration::getTemplateArgument()` and
`Declaration::getTemplateArgumentKind()` for details. `Declaration::getTemplateArgumentKind()`.

View File

@@ -6,25 +6,23 @@ The following changes in version 1.23 affect Java analysis in all applications.
| **Query** | **Tags** | **Purpose** | | **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------| |-----------------------------|-----------|--------------------------------------------------------------------|
| Continue statement that does not continue (`java/continue-in-false-loop`) | correctness | Finds `continue` statements in `do { ... } while (false)` loops. | | Continue statement that does not continue (`java/continue-in-false-loop`) | correctness | Finds `continue` statements in `do { ... } while (false)` loops. Results are shown on LGTM by default. |
## Changes to existing queries ## Changes to existing queries
| **Query** | **Expected impact** | **Change** | | **Query** | **Expected impact** | **Change** |
|------------------------------|------------------------|-----------------------------------| |------------------------------|------------------------|-----------------------------------|
| Dereferenced variable may be null (`java/dereferenced-value-may-be-null`) | Fewer false positives | Certain indirect null guards involving two auxiliary variables known to be equal can now be detected. | | Dereferenced variable may be null (`java/dereferenced-value-may-be-null`) | Fewer false positive results | Additional indirect null guards are detected, where two auxiliary variables are known to be equal. |
| 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. | | Non-synchronized override of synchronized method (`java/non-sync-override`) | Fewer false positive results | 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 sinks for SQL expressions. |
| Query built from local-user-controlled sources (`java/sql-injection-local`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as SQL expressions sinks. | | Query built from user-controlled sources (`java/sql-injection`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as sinks for SQL expressions. |
| Query built without neutralizing special characters (`java/concatenated-sql-query`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as SQL expressions sinks. | | Query built without neutralizing special characters (`java/concatenated-sql-query`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as sinks for SQL expressions. |
| Useless comparison test (`java/constant-comparison`) | Fewer false positives | Additional overflow check patterns are now recognized and no longer reported. | | Useless comparison test (`java/constant-comparison`) | Fewer false positive results | Additional overflow check patterns are now recognized and no longer reported. Also, a few bug fixes in the range analysis for floating-point variables gives a further reduction in false positive results. |
## Changes to libraries ## Changes to libraries
* The data-flow library has been extended with a new feature to aid debugging. The data-flow library has been extended with a new feature to aid debugging.
Instead of specifying `isSink(Node n) { any() }` on a configuration to Previously, to explore the possible flow from all sources you could specify `isSink(Node n) { any() }` on a configuration.
explore the possible flow from a source, it is recommended to use the new Now you can use the new `Configuration::hasPartialFlow` predicate,
`Configuration::hasPartialFlow` predicate, as this gives a more complete which gives a more complete picture of the partial flow paths from a given source, including flow that doesn't reach any sink.
picture of the partial flow paths from a given source. The feature is The feature is disabled by default and can be enabled for individual configurations by overriding `int explorationLimit()`.
disabled by default and can be enabled for individual configurations by
overriding `int explorationLimit()`.

View File

@@ -27,7 +27,7 @@
|---------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |---------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 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. | | 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. |
| Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | security, correctness, external/cwe/cwe-020 | Highlights checks for `javascript:` URLs that do not take `data:` or `vbscript:` URLs into account. Results are shown on LGTM by default. | | Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | security, correctness, external/cwe/cwe-020 | Highlights checks for `javascript:` URLs that do not take `data:` or `vbscript:` URLs into account. Results are shown on LGTM by default. |
| Loop bound injection (`js/loop-bound-injection`) | security, external/cwe/cwe-834 | Highlights loops where a user-controlled object with an arbitrary .length value can trick the server to loop indefinitely. Results are shown on LGTM by default. | | 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 into looping indefinitely. 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.| | 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.|
| 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. | | 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. |
| 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. | | 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. |
@@ -39,19 +39,19 @@
| **Query** | **Expected impact** | **Change** | | **Query** | **Expected impact** | **Change** |
|--------------------------------|------------------------------|---------------------------------------------------------------------------| |--------------------------------|------------------------------|---------------------------------------------------------------------------|
| Incomplete string escaping or encoding (`js/incomplete-sanitization`) | Fewer false-positive results | This rule now recognizes additional ways delimiters can be stripped away. | | 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. |
| Client-side cross-site scripting (`js/xss`) | More results, fewer false-positive results | More potential vulnerabilities involving functions that manipulate DOM attributes are now recognized, and more sanitizers are detected. |
| Code injection (`js/code-injection`) | More results | More potential vulnerabilities involving functions that manipulate DOM event handler attributes are now recognized. | | Code injection (`js/code-injection`) | More results | More potential vulnerabilities involving functions that manipulate DOM event handler attributes are now recognized. |
| Hard-coded credentials (`js/hardcoded-credentials`) | Fewer false-positive results | This rule now flags fewer password examples. | | Hard-coded credentials (`js/hardcoded-credentials`) | Fewer false positive results | This rule now flags fewer password examples. |
| Illegal invocation (`js/illegal-invocation`) | Fewer false-positive results | This rule now correctly handles methods named `call` and `apply`. | | Illegal invocation (`js/illegal-invocation`) | Fewer false positive results | This rule now correctly handles methods named `call` and `apply`. |
| Incorrect suffix check (`js/incorrect-suffix-check`) | Fewer false-positive results | The query recognizes valid checks in more cases. | | Incomplete string escaping or encoding (`js/incomplete-sanitization`) | Fewer false positive results | This rule now recognizes additional ways delimiters can be stripped away. |
| Network data written to file (`js/http-to-file-access`) | Fewer false-positive results | This query has been renamed to better match its intended purpose, and now only considers network data untrusted. | | Incorrect suffix check (`js/incorrect-suffix-check`) | Fewer false positive results | The query recognizes valid checks in more cases. |
| Password in configuration file (`js/password-in-configuration-file`) | Fewer false-positive results | This rule now flags fewer password examples. | | Network data written to file (`js/http-to-file-access`) | Fewer false positive results | This query has been renamed to better match its intended purpose, and now only considers network data untrusted. |
| Password in configuration file (`js/password-in-configuration-file`) | Fewer false positive results | This rule now flags fewer password examples. |
| Prototype pollution (`js/prototype-pollution`) | More results | The query now highlights vulnerable uses of jQuery and Angular, and the results are shown on LGTM by default. | | Prototype pollution (`js/prototype-pollution`) | More results | The query now highlights vulnerable uses of jQuery and Angular, and the results are shown on LGTM by default. |
| Reflected cross-site scripting (`js/reflected-xss`) | Fewer false-positive results | The query now recognizes more sanitizers. | | 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. | | Stored cross-site scripting (`js/stored-xss`) | Fewer false positive results | The query now recognizes more sanitizers. |
| Uncontrolled command line (`js/command-line-injection`) | More results | This query now treats responses from servers as untrusted. | | Uncontrolled command line (`js/command-line-injection`) | More results | This query now treats responses from servers as untrusted. |
| 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. | | 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. | | 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 libraries ## Changes to libraries
@@ -67,16 +67,16 @@
The following queries (deprecated since 1.17) are no longer available in the distribution: 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) * Bad parity check (js/incomplete-parity-check)
* Potentially misspelled property or variable name (js/wrong-capitalization) * Builtin redefined (js/builtin-redefinition)
* Unknown JSDoc tag (js/jsdoc/unknown-tag-type) * Call to parseInt without radix (js/parseint-without-radix)
* Inefficient method definition (js/method-definition-in-constructor)
* Invalid JSLint directive (js/jslint/invalid-directive) * Invalid JSLint directive (js/jslint/invalid-directive)
* Malformed JSLint directive (js/jslint/malformed-directive) * Malformed JSLint directive (js/jslint/malformed-directive)
* Use of HTML comments (js/html-comment)
* Multi-line string literal (js/multi-line-string) * Multi-line string literal (js/multi-line-string)
* Octal literal (js/octal-literal) * Octal literal (js/octal-literal)
* Potentially misspelled property or variable name (js/wrong-capitalization)
* Reserved word used as variable name (js/use-of-reserved-word) * 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) * Trailing comma in array or object expressions (js/trailing-comma-in-array-or-object)
* Call to parseInt without radix (js/parseint-without-radix) * Unknown JSDoc tag (js/jsdoc/unknown-tag-type)
* Use of HTML comments (js/html-comment)

View File

@@ -3,7 +3,19 @@
## General improvements ## General improvements
### Python 3.8 support
Python 3.8 syntax is now supported. In particular, the following constructs are parsed correctly:
- Assignment expressions using the "walrus" operator, such as `while chunk := file.read(1024): ...`.
- The positional argument separator `/`, such as in `def foo(a, /, b, *, c): ...`.
- Self-documenting expressions in f-strings, such as `f"{var=}"`.
### General query improvements
Following the replacement of the `Object` API (for example, `ClassObject`) in favor of the
`Value` API (for example, `ClassValue`) in the 1.21 release, many of the standard queries have been updated
to use the `Value` API. This should result in more precise results.
## New queries ## New queries
@@ -18,10 +30,23 @@
| **Query** | **Expected impact** | **Change** | | **Query** | **Expected impact** | **Change** |
|----------------------------|------------------------|------------| |----------------------------|------------------------|------------|
| Unreachable code | Fewer false positives | Analysis now accounts for uses of `contextlib.suppress` to suppress exceptions. | | Explicit export is undefined (`py/undefined-export`) | Fewer false positive results | Instances where an exported value may be defined in a module that lacks points-to information are no longer flagged. |
| `__iter__` method returns a non-iterator | Better alert message | Alert now highlights which class is expected to be an iterator. | | Module-level cyclic import (`py/unsafe-cyclic-import`) | Fewer false positive results | Instances where one of the links in an import cycle is never actually executed are no longer flagged. |
| Non-iterable used in for loop (`py/non-iterable-in-for-loop`) | Fewer false positive results | `__aiter__` is now recognized as an iterator method. |
| Unreachable code (`py/unreachable-statement`) | Fewer false positive results | Analysis now accounts for uses of `contextlib.suppress` to suppress exceptions. |
| Unreachable code (`py/unreachable-statement`) | Fewer false positive results | Unreachable `else` branches that do nothing but `assert` their non-reachability are no longer flagged. |
| Unused import (`py/unused-import`) | Fewer false positive results | Instances where a module is used in a forward-referenced type annotation, or only during type checking are no longer flagged. |
| `__iter__` method returns a non-iterator (`py/iter-returns-non-iterator`) | Better alert message | Alert now highlights which class is expected to be an iterator. |
| `__init__` method returns a value (`py/explicit-return-in-init`) | Fewer false positive results | Instances where the `__init__` method returns the value of a call to a procedure are no longer flagged. |
## Changes to QL libraries ## Changes to QL libraries
* Django library now recognizes positional arguments from a `django.conf.urls.url` regex (Django version 1.x) * Django library now recognizes positional arguments from a `django.conf.urls.url` regex (Django version 1.x)
* Instances of the `Value` class now support the `isAbsent` method, indicating
whether that `Value` lacks points-to information, but inference
suggests that it exists. For instance, if a file contains `import
django`, but `django` was not extracted properly, there will be a
`ModuleValue` corresponding to this "unknown" module, and the `isAbsent`
method will hold for this `ModuleValue`.
* The `Expr` class now has a nullary method `pointsTo` that returns the possible
instances of `Value` that this expression may have.

View File

@@ -7,17 +7,17 @@
* Asynchronous generator methods are now parsed correctly and no longer cause a spurious syntax error. * 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): * 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 ```yaml
extraction: extraction:
javascript: javascript:
index: index:
filters: filters:
- include: "**/node_modules" - include: "**/node_modules"
- include: "**/bower_components" - include: "**/bower_components"
``` ```
* Additional [Flow](https://flow.org/) syntax is now supported.
* Recognition of CommonJS modules has improved. As a result, some files that were previously extracted as * Recognition of CommonJS modules has improved. As a result, some files that were previously extracted as
global scripts are now extracted as modules. global scripts are now extracted as modules.
* Top-level `await` is now supported. * Top-level `await` is now supported.
* A bug was fixed in how the TypeScript extractor handles default-exported anonymous classes. * Bugs were fixed in how the TypeScript extractor handles default-exported anonymous classes and computed-instance field names.
* A bug was fixed in how the TypeScript extractor handles computed instance field names.

View File

@@ -17,4 +17,11 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
## Changes to libraries ## Changes to libraries
* * The new class `StackVariable` should be used in place of `LocalScopeVariable`
in most cases. The difference is that `StackVariable` does not include
variables declared with `static` or `thread_local`.
* As a rule of thumb, custom queries about the _values_ of variables should
be changed from `LocalScopeVariable` to `StackVariable`, while queries
about the _name or scope_ of variables should remain unchanged.
* The `LocalScopeVariableReachability` library is deprecated in favor of
`StackVariableReachability`. The functionality is the same.

View File

@@ -0,0 +1,25 @@
# Improvements to C# analysis
The following changes in version 1.24 affect C# analysis in all applications.
## New queries
| **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------|
| Insecure configuration for ASP.NET requestValidationMode (`cs/insecure-request-validation-mode`) | security, external/cwe/cwe-016 | Finds where this attribute has been set to a value less than 4.5, which turns off some validation features and makes the application less secure. |
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
|------------------------------|------------------------|-----------------------------------|
## Removal of old queries
## Changes to code extraction
## Changes to libraries
* The taint tracking library now tracks flow through (implicit or explicit) conversion operator calls.
## Changes to autobuilder

View File

@@ -4,13 +4,17 @@
* Support for the following frameworks and libraries has been improved: * Support for the following frameworks and libraries has been improved:
- [react](https://www.npmjs.com/package/react) - [react](https://www.npmjs.com/package/react)
- [typeahead.js](https://www.npmjs.com/package/typeahead.js)
- [Handlebars](https://www.npmjs.com/package/handlebars) - [Handlebars](https://www.npmjs.com/package/handlebars)
- Imports with the `.js` extension can now be resolved to a TypeScript file,
when the import refers to a file generated by TypeScript.
## New queries ## New queries
| **Query** | **Tags** | **Purpose** | | **Query** | **Tags** | **Purpose** |
|---------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |---------------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Cross-site scripting through exception (`js/xss-through-exception`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities where an exception is written to the DOM. Results are not shown on LGTM by default. |
## Changes to existing queries ## Changes to existing queries
@@ -22,3 +26,5 @@
## Changes to libraries ## Changes to libraries
* The predicates `RegExpTerm.getSuccessor` and `RegExpTerm.getPredecessor` have been changed to reflect textual, not operational, matching order. This only makes a difference in lookbehind assertions, which are operationally matched backwards. Previously, `getSuccessor` would mimick this, so in an assertion `(?<=ab)` the term `b` would be considered the predecessor, not the successor, of `a`. Textually, however, `a` is still matched before `b`, and this is the order we now follow.

View File

@@ -22,7 +22,7 @@ predicate testAndBranch(Expr e, Stmt branch) {
) )
} }
predicate choice(LocalScopeVariable v, Stmt branch, string value) { predicate choice(StackVariable v, Stmt branch, string value) {
exists(AnalysedExpr e | exists(AnalysedExpr e |
testAndBranch(e, branch) and testAndBranch(e, branch) and
( (
@@ -33,7 +33,7 @@ predicate choice(LocalScopeVariable v, Stmt branch, string value) {
) )
} }
predicate guarded(LocalScopeVariable v, Stmt loopstart, AnalysedExpr child) { predicate guarded(StackVariable v, Stmt loopstart, AnalysedExpr child) {
choice(v, loopstart, _) and choice(v, loopstart, _) and
loopstart.getChildStmt*() = child.getEnclosingStmt() and loopstart.getChildStmt*() = child.getEnclosingStmt() and
(definition(v, child) or exists(child.getNullSuccessor(v))) (definition(v, child) or exists(child.getNullSuccessor(v)))
@@ -47,9 +47,7 @@ predicate addressLeak(Variable v, Stmt leak) {
) )
} }
from from StackVariable v, Stmt branch, AnalysedExpr cond, string context, string test, string testresult
LocalScopeVariable v, Stmt branch, AnalysedExpr cond, string context, string test,
string testresult
where where
choice(v, branch, context) and choice(v, branch, context) and
forall(ControlFlowNode def | definition(v, def) and definitionReaches(def, cond) | forall(ControlFlowNode def | definition(v, def) and definitionReaches(def, cond) |

View File

@@ -23,14 +23,14 @@ predicate closeCall(FunctionCall fc, Variable v) {
) )
} }
predicate openDefinition(LocalScopeVariable v, ControlFlowNode def) { predicate openDefinition(StackVariable v, ControlFlowNode def) {
exists(Expr expr | exprDefinition(v, def, expr) and allocateDescriptorCall(expr)) exists(Expr expr | exprDefinition(v, def, expr) and allocateDescriptorCall(expr))
} }
predicate openReaches(ControlFlowNode def, ControlFlowNode node) { predicate openReaches(ControlFlowNode def, ControlFlowNode node) {
exists(LocalScopeVariable v | openDefinition(v, def) and node = def.getASuccessor()) exists(StackVariable v | openDefinition(v, def) and node = def.getASuccessor())
or or
exists(LocalScopeVariable v, ControlFlowNode mid | exists(StackVariable v, ControlFlowNode mid |
openDefinition(v, def) and openDefinition(v, def) and
openReaches(def, mid) and openReaches(def, mid) and
not errorSuccessor(v, mid) and not errorSuccessor(v, mid) and
@@ -40,7 +40,7 @@ predicate openReaches(ControlFlowNode def, ControlFlowNode node) {
) )
} }
predicate assignedToFieldOrGlobal(LocalScopeVariable v, Assignment assign) { predicate assignedToFieldOrGlobal(StackVariable v, Assignment assign) {
exists(Variable external | exists(Variable external |
assign.getRValue() = v.getAnAccess() and assign.getRValue() = v.getAnAccess() and
assign.getLValue().(VariableAccess).getTarget() = external and assign.getLValue().(VariableAccess).getTarget() = external and
@@ -48,7 +48,7 @@ predicate assignedToFieldOrGlobal(LocalScopeVariable v, Assignment assign) {
) )
} }
from LocalScopeVariable v, ControlFlowNode def, ReturnStmt ret from StackVariable v, ControlFlowNode def, ReturnStmt ret
where where
openDefinition(v, def) and openDefinition(v, def) and
openReaches(def, ret) and openReaches(def, ret) and

View File

@@ -10,7 +10,7 @@
*/ */
import FileClosed import FileClosed
import semmle.code.cpp.controlflow.LocalScopeVariableReachability import semmle.code.cpp.controlflow.StackVariableReachability
/** /**
* Extend the NullValue class used by Nullness.qll to include simple -1 as a 'null' value * Extend the NullValue class used by Nullness.qll to include simple -1 as a 'null' value
@@ -68,18 +68,18 @@ predicate fcloseCallOrIndirect(FunctionCall fc, Variable v) {
) )
} }
predicate fopenDefinition(LocalScopeVariable v, ControlFlowNode def) { predicate fopenDefinition(StackVariable v, ControlFlowNode def) {
exists(Expr expr | exprDefinition(v, def, expr) and fopenCallOrIndirect(expr)) exists(Expr expr | exprDefinition(v, def, expr) and fopenCallOrIndirect(expr))
} }
class FOpenVariableReachability extends LocalScopeVariableReachabilityWithReassignment { class FOpenVariableReachability extends StackVariableReachabilityWithReassignment {
FOpenVariableReachability() { this = "FOpenVariableReachability" } FOpenVariableReachability() { this = "FOpenVariableReachability" }
override predicate isSourceActual(ControlFlowNode node, LocalScopeVariable v) { override predicate isSourceActual(ControlFlowNode node, StackVariable v) {
fopenDefinition(v, node) fopenDefinition(v, node)
} }
override predicate isSinkActual(ControlFlowNode node, LocalScopeVariable v) { override predicate isSinkActual(ControlFlowNode node, StackVariable v) {
// node may be used in fopenReaches // node may be used in fopenReaches
exists(node.(AnalysedExpr).getNullSuccessor(v)) or exists(node.(AnalysedExpr).getNullSuccessor(v)) or
fcloseCallOrIndirect(node, v) or fcloseCallOrIndirect(node, v) or
@@ -88,15 +88,13 @@ class FOpenVariableReachability extends LocalScopeVariableReachabilityWithReassi
v.getFunction() = node.(ReturnStmt).getEnclosingFunction() v.getFunction() = node.(ReturnStmt).getEnclosingFunction()
} }
override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) { override predicate isBarrier(ControlFlowNode node, StackVariable v) { definitionBarrier(v, node) }
definitionBarrier(v, node)
}
} }
/** /**
* The value from fopen at `def` is still held in Variable `v` upon entering `node`. * The value from fopen at `def` is still held in Variable `v` upon entering `node`.
*/ */
predicate fopenVariableReaches(LocalScopeVariable v, ControlFlowNode def, ControlFlowNode node) { predicate fopenVariableReaches(StackVariable v, ControlFlowNode def, ControlFlowNode node) {
exists(FOpenVariableReachability r | exists(FOpenVariableReachability r |
// reachability // reachability
r.reachesTo(def, _, node, v) r.reachesTo(def, _, node, v)
@@ -107,25 +105,23 @@ predicate fopenVariableReaches(LocalScopeVariable v, ControlFlowNode def, Contro
) )
} }
class FOpenReachability extends LocalScopeVariableReachabilityExt { class FOpenReachability extends StackVariableReachabilityExt {
FOpenReachability() { this = "FOpenReachability" } FOpenReachability() { this = "FOpenReachability" }
override predicate isSource(ControlFlowNode node, LocalScopeVariable v) { override predicate isSource(ControlFlowNode node, StackVariable v) { fopenDefinition(v, node) }
fopenDefinition(v, node)
}
override predicate isSink(ControlFlowNode node, LocalScopeVariable v) { override predicate isSink(ControlFlowNode node, StackVariable v) {
v.getFunction() = node.(ReturnStmt).getEnclosingFunction() v.getFunction() = node.(ReturnStmt).getEnclosingFunction()
} }
override predicate isBarrier( override predicate isBarrier(
ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, LocalScopeVariable v ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, StackVariable v
) { ) {
isSource(source, v) and isSource(source, v) and
next = node.getASuccessor() and next = node.getASuccessor() and
// the file (stored in any variable `v0`) opened at `source` is closed or // the file (stored in any variable `v0`) opened at `source` is closed or
// assigned to a global at node, or NULL checked on the edge node -> next. // assigned to a global at node, or NULL checked on the edge node -> next.
exists(LocalScopeVariable v0 | fopenVariableReaches(v0, source, node) | exists(StackVariable v0 | fopenVariableReaches(v0, source, node) |
node.(AnalysedExpr).getNullSuccessor(v0) = next or node.(AnalysedExpr).getNullSuccessor(v0) = next or
fcloseCallOrIndirect(node, v0) or fcloseCallOrIndirect(node, v0) or
assignedToFieldOrGlobal(v0, node) assignedToFieldOrGlobal(v0, node)
@@ -142,11 +138,11 @@ predicate fopenReaches(ControlFlowNode def, ControlFlowNode node) {
exists(FOpenReachability r | r.reaches(def, _, node)) exists(FOpenReachability r | r.reaches(def, _, node))
} }
predicate assignedToFieldOrGlobal(LocalScopeVariable v, Expr e) { predicate assignedToFieldOrGlobal(StackVariable v, Expr e) {
// assigned to anything except a LocalScopeVariable // assigned to anything except a StackVariable
// (typically a field or global, but for example also *ptr = v) // (typically a field or global, but for example also *ptr = v)
e.(Assignment).getRValue() = v.getAnAccess() and e.(Assignment).getRValue() = v.getAnAccess() and
not e.(Assignment).getLValue().(VariableAccess).getTarget() instanceof LocalScopeVariable not e.(Assignment).getLValue().(VariableAccess).getTarget() instanceof StackVariable
or or
exists(Expr midExpr, Function mid, int arg | exists(Expr midExpr, Function mid, int arg |
// indirect assignment // indirect assignment
@@ -163,7 +159,7 @@ predicate assignedToFieldOrGlobal(LocalScopeVariable v, Expr e) {
from ControlFlowNode def, ReturnStmt ret from ControlFlowNode def, ReturnStmt ret
where where
fopenReaches(def, ret) and fopenReaches(def, ret) and
not exists(LocalScopeVariable v | not exists(StackVariable v |
fopenVariableReaches(v, def, ret) and fopenVariableReaches(v, def, ret) and
ret.getAChild*() = v.getAnAccess() ret.getAChild*() = v.getAnAccess()
) )

View File

@@ -11,7 +11,7 @@
import cpp import cpp
from LocalScopeVariable v, ControlFlowNode def, VariableAccess checked, VariableAccess unchecked from StackVariable v, ControlFlowNode def, VariableAccess checked, VariableAccess unchecked
where where
checked = v.getAnAccess() and checked = v.getAnAccess() and
dereferenced(checked) and dereferenced(checked) and

View File

@@ -13,7 +13,7 @@
import cpp import cpp
predicate negativeCheck(LocalScopeVariable v, ComparisonOperation op) { predicate negativeCheck(StackVariable v, ComparisonOperation op) {
exists(int varindex, string constant, Literal lit | exists(int varindex, string constant, Literal lit |
op.getChild(varindex) = v.getAnAccess() and op.getChild(varindex) = v.getAnAccess() and
op.getChild(1 - varindex) = lit and op.getChild(1 - varindex) = lit and
@@ -38,7 +38,7 @@ predicate negativeCheck(LocalScopeVariable v, ComparisonOperation op) {
) )
} }
from LocalScopeVariable v, ArrayExpr dangerous, Expr check from StackVariable v, ArrayExpr dangerous, Expr check
where where
useUsePair(v, dangerous.getArrayOffset(), check.getAChild()) and useUsePair(v, dangerous.getArrayOffset(), check.getAChild()) and
negativeCheck(v, check) and negativeCheck(v, check) and

View File

@@ -10,7 +10,7 @@
*/ */
import MemoryFreed import MemoryFreed
import semmle.code.cpp.controlflow.LocalScopeVariableReachability import semmle.code.cpp.controlflow.StackVariableReachability
/** /**
* 'call' is either a direct call to f, or a possible call to f * 'call' is either a direct call to f, or a possible call to f
@@ -97,18 +97,18 @@ predicate freeCallOrIndirect(ControlFlowNode n, Variable v) {
) )
} }
predicate allocationDefinition(LocalScopeVariable v, ControlFlowNode def) { predicate allocationDefinition(StackVariable v, ControlFlowNode def) {
exists(Expr expr | exprDefinition(v, def, expr) and allocCallOrIndirect(expr)) exists(Expr expr | exprDefinition(v, def, expr) and allocCallOrIndirect(expr))
} }
class AllocVariableReachability extends LocalScopeVariableReachabilityWithReassignment { class AllocVariableReachability extends StackVariableReachabilityWithReassignment {
AllocVariableReachability() { this = "AllocVariableReachability" } AllocVariableReachability() { this = "AllocVariableReachability" }
override predicate isSourceActual(ControlFlowNode node, LocalScopeVariable v) { override predicate isSourceActual(ControlFlowNode node, StackVariable v) {
allocationDefinition(v, node) allocationDefinition(v, node)
} }
override predicate isSinkActual(ControlFlowNode node, LocalScopeVariable v) { override predicate isSinkActual(ControlFlowNode node, StackVariable v) {
// node may be used in allocationReaches // node may be used in allocationReaches
exists(node.(AnalysedExpr).getNullSuccessor(v)) or exists(node.(AnalysedExpr).getNullSuccessor(v)) or
freeCallOrIndirect(node, v) or freeCallOrIndirect(node, v) or
@@ -117,15 +117,13 @@ class AllocVariableReachability extends LocalScopeVariableReachabilityWithReassi
v.getFunction() = node.(ReturnStmt).getEnclosingFunction() v.getFunction() = node.(ReturnStmt).getEnclosingFunction()
} }
override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) { override predicate isBarrier(ControlFlowNode node, StackVariable v) { definitionBarrier(v, node) }
definitionBarrier(v, node)
}
} }
/** /**
* The value from allocation `def` is still held in Variable `v` upon entering `node`. * The value from allocation `def` is still held in Variable `v` upon entering `node`.
*/ */
predicate allocatedVariableReaches(LocalScopeVariable v, ControlFlowNode def, ControlFlowNode node) { predicate allocatedVariableReaches(StackVariable v, ControlFlowNode def, ControlFlowNode node) {
exists(AllocVariableReachability r | exists(AllocVariableReachability r |
// reachability // reachability
r.reachesTo(def, _, node, v) r.reachesTo(def, _, node, v)
@@ -136,25 +134,25 @@ predicate allocatedVariableReaches(LocalScopeVariable v, ControlFlowNode def, Co
) )
} }
class AllocReachability extends LocalScopeVariableReachabilityExt { class AllocReachability extends StackVariableReachabilityExt {
AllocReachability() { this = "AllocReachability" } AllocReachability() { this = "AllocReachability" }
override predicate isSource(ControlFlowNode node, LocalScopeVariable v) { override predicate isSource(ControlFlowNode node, StackVariable v) {
allocationDefinition(v, node) allocationDefinition(v, node)
} }
override predicate isSink(ControlFlowNode node, LocalScopeVariable v) { override predicate isSink(ControlFlowNode node, StackVariable v) {
v.getFunction() = node.(ReturnStmt).getEnclosingFunction() v.getFunction() = node.(ReturnStmt).getEnclosingFunction()
} }
override predicate isBarrier( override predicate isBarrier(
ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, LocalScopeVariable v ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, StackVariable v
) { ) {
isSource(source, v) and isSource(source, v) and
next = node.getASuccessor() and next = node.getASuccessor() and
// the memory (stored in any variable `v0`) allocated at `source` is freed or // the memory (stored in any variable `v0`) allocated at `source` is freed or
// assigned to a global at node, or NULL checked on the edge node -> next. // assigned to a global at node, or NULL checked on the edge node -> next.
exists(LocalScopeVariable v0 | allocatedVariableReaches(v0, source, node) | exists(StackVariable v0 | allocatedVariableReaches(v0, source, node) |
node.(AnalysedExpr).getNullSuccessor(v0) = next or node.(AnalysedExpr).getNullSuccessor(v0) = next or
freeCallOrIndirect(node, v0) or freeCallOrIndirect(node, v0) or
assignedToFieldOrGlobal(v0, node) assignedToFieldOrGlobal(v0, node)
@@ -171,11 +169,11 @@ predicate allocationReaches(ControlFlowNode def, ControlFlowNode node) {
exists(AllocReachability r | r.reaches(def, _, node)) exists(AllocReachability r | r.reaches(def, _, node))
} }
predicate assignedToFieldOrGlobal(LocalScopeVariable v, Expr e) { predicate assignedToFieldOrGlobal(StackVariable v, Expr e) {
// assigned to anything except a LocalScopeVariable // assigned to anything except a StackVariable
// (typically a field or global, but for example also *ptr = v) // (typically a field or global, but for example also *ptr = v)
e.(Assignment).getRValue() = v.getAnAccess() and e.(Assignment).getRValue() = v.getAnAccess() and
not e.(Assignment).getLValue().(VariableAccess).getTarget() instanceof LocalScopeVariable not e.(Assignment).getLValue().(VariableAccess).getTarget() instanceof StackVariable
or or
exists(Expr midExpr, Function mid, int arg | exists(Expr midExpr, Function mid, int arg |
// indirect assignment // indirect assignment
@@ -192,7 +190,7 @@ predicate assignedToFieldOrGlobal(LocalScopeVariable v, Expr e) {
from ControlFlowNode def, ReturnStmt ret from ControlFlowNode def, ReturnStmt ret
where where
allocationReaches(def, ret) and allocationReaches(def, ret) and
not exists(LocalScopeVariable v | not exists(StackVariable v |
allocatedVariableReaches(v, def, ret) and allocatedVariableReaches(v, def, ret) and
ret.getAChild*() = v.getAnAccess() ret.getAChild*() = v.getAnAccess()
) )

View File

@@ -43,7 +43,7 @@ class FunctionWithNegativeReturn extends Function {
predicate dangerousUse(IntegralReturnValue val, Expr use) { predicate dangerousUse(IntegralReturnValue val, Expr use) {
exists(ArrayExpr ae | ae.getArrayOffset() = val and use = val) exists(ArrayExpr ae | ae.getArrayOffset() = val and use = val)
or or
exists(LocalScopeVariable v, ControlFlowNode def, ArrayExpr ae | exists(StackVariable v, ControlFlowNode def, ArrayExpr ae |
exprDefinition(v, def, val) and exprDefinition(v, def, val) and
use = ae.getArrayOffset() and use = ae.getArrayOffset() and
not boundsChecked(v, use) and not boundsChecked(v, use) and
@@ -54,7 +54,7 @@ predicate dangerousUse(IntegralReturnValue val, Expr use) {
val = use and val = use and
use.getType().getUnderlyingType() instanceof PointerType use.getType().getUnderlyingType() instanceof PointerType
or or
exists(LocalScopeVariable v, ControlFlowNode def, AddExpr add | exists(StackVariable v, ControlFlowNode def, AddExpr add |
exprDefinition(v, def, val) and exprDefinition(v, def, val) and
definitionUsePair(v, def, use) and definitionUsePair(v, def, use) and
add.getAnOperand() = use and add.getAnOperand() = use and

View File

@@ -60,7 +60,7 @@ predicate allocExprOrIndirect(Expr alloc, string kind) {
pragma[nomagic] pragma[nomagic]
private predicate allocReachesVariable(Variable v, Expr alloc, string kind) { private predicate allocReachesVariable(Variable v, Expr alloc, string kind) {
exists(Expr mid | exists(Expr mid |
not v instanceof LocalScopeVariable and not v instanceof StackVariable and
v.getAnAssignedValue() = mid and v.getAnAssignedValue() = mid and
allocReaches0(mid, alloc, kind) allocReaches0(mid, alloc, kind)
) )
@@ -76,7 +76,7 @@ private predicate allocReaches0(Expr e, Expr alloc, string kind) {
allocExprOrIndirect(alloc, kind) and allocExprOrIndirect(alloc, kind) and
e = alloc e = alloc
or or
exists(SsaDefinition def, LocalScopeVariable v | exists(SsaDefinition def, StackVariable v |
// alloc via SSA // alloc via SSA
allocReaches0(def.getAnUltimateDefiningValue(v), alloc, kind) and allocReaches0(def.getAnUltimateDefiningValue(v), alloc, kind) and
e = def.getAUse(v) e = def.getAUse(v)

View File

@@ -18,7 +18,7 @@ class MallocCall extends FunctionCall {
Expr getAllocatedSize() { Expr getAllocatedSize() {
if this.getArgument(0) instanceof VariableAccess if this.getArgument(0) instanceof VariableAccess
then then
exists(LocalScopeVariable v, ControlFlowNode def | exists(StackVariable v, ControlFlowNode def |
definitionUsePair(v, def, this.getArgument(0)) and definitionUsePair(v, def, this.getArgument(0)) and
exprDefinition(v, def, result) exprDefinition(v, def, result)
) )

View File

@@ -20,11 +20,10 @@ class ReturnPointsToExpr extends PointsToExpr {
ReturnStmt getReturnStmt() { result.getExpr().getFullyConverted() = this } ReturnStmt getReturnStmt() { result.getExpr().getFullyConverted() = this }
} }
from ReturnPointsToExpr ret, LocalVariable local, float confidence from ReturnPointsToExpr ret, StackVariable local, float confidence
where where
ret.pointsTo() = local and ret.pointsTo() = local and
ret.getReturnStmt().getEnclosingFunction() = local.getFunction() and ret.getReturnStmt().getEnclosingFunction() = local.getFunction() and
not local.isStatic() and
confidence = ret.confidence() and confidence = ret.confidence() and
confidence > 0.01 confidence > 0.01
select ret, select ret,

View File

@@ -20,11 +20,10 @@ class ScopeUtilityClass extends Class {
Call getAUse() { result = this.getAConstructor().getACallToThisFunction() } Call getAUse() { result = this.getAConstructor().getACallToThisFunction() }
} }
from LocalScopeVariable v, ControlFlowNode def from StackVariable v, ControlFlowNode def
where where
definition(v, def) and definition(v, def) and
not definitionUsePair(v, def, _) and not definitionUsePair(v, def, _) and
not v.isStatic() and
not v.getAnAccess().isAddressOfAccess() and not v.getAnAccess().isAddressOfAccess() and
// parameter initializers are not in the call-graph at the moment // parameter initializers are not in the call-graph at the moment
not v.(Parameter).getInitializer().getExpr() = def and not v.(Parameter).getInitializer().getExpr() = def and

View File

@@ -10,10 +10,10 @@
*/ */
import cpp import cpp
import semmle.code.cpp.controlflow.LocalScopeVariableReachability import semmle.code.cpp.controlflow.StackVariableReachability
/** `e` is an expression that frees the memory pointed to by `v`. */ /** `e` is an expression that frees the memory pointed to by `v`. */
predicate isFreeExpr(Expr e, LocalScopeVariable v) { predicate isFreeExpr(Expr e, StackVariable v) {
exists(VariableAccess va | va.getTarget() = v | exists(VariableAccess va | va.getTarget() = v |
exists(FunctionCall fc | fc = e | exists(FunctionCall fc | fc = e |
fc.getTarget().hasGlobalOrStdName("free") and fc.getTarget().hasGlobalOrStdName("free") and
@@ -27,7 +27,7 @@ predicate isFreeExpr(Expr e, LocalScopeVariable v) {
} }
/** `e` is an expression that (may) dereference `v`. */ /** `e` is an expression that (may) dereference `v`. */
predicate isDerefExpr(Expr e, LocalScopeVariable v) { predicate isDerefExpr(Expr e, StackVariable v) {
v.getAnAccess() = e and dereferenced(e) v.getAnAccess() = e and dereferenced(e)
or or
isDerefByCallExpr(_, _, e, v) isDerefByCallExpr(_, _, e, v)
@@ -39,27 +39,27 @@ predicate isDerefExpr(Expr e, LocalScopeVariable v) {
* or a source code function that dereferences the relevant * or a source code function that dereferences the relevant
* parameter. * parameter.
*/ */
predicate isDerefByCallExpr(Call c, int i, VariableAccess va, LocalScopeVariable v) { predicate isDerefByCallExpr(Call c, int i, VariableAccess va, StackVariable v) {
v.getAnAccess() = va and v.getAnAccess() = va and
va = c.getAnArgumentSubExpr(i) and va = c.getAnArgumentSubExpr(i) and
not c.passesByReference(i, va) and not c.passesByReference(i, va) and
(c.getTarget().hasEntryPoint() implies isDerefExpr(_, c.getTarget().getParameter(i))) (c.getTarget().hasEntryPoint() implies isDerefExpr(_, c.getTarget().getParameter(i)))
} }
class UseAfterFreeReachability extends LocalScopeVariableReachability { class UseAfterFreeReachability extends StackVariableReachability {
UseAfterFreeReachability() { this = "UseAfterFree" } UseAfterFreeReachability() { this = "UseAfterFree" }
override predicate isSource(ControlFlowNode node, LocalScopeVariable v) { isFreeExpr(node, v) } override predicate isSource(ControlFlowNode node, StackVariable v) { isFreeExpr(node, v) }
override predicate isSink(ControlFlowNode node, LocalScopeVariable v) { isDerefExpr(node, v) } override predicate isSink(ControlFlowNode node, StackVariable v) { isDerefExpr(node, v) }
override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) { override predicate isBarrier(ControlFlowNode node, StackVariable v) {
definitionBarrier(v, node) or definitionBarrier(v, node) or
isFreeExpr(node, v) isFreeExpr(node, v)
} }
} }
from UseAfterFreeReachability r, LocalScopeVariable v, Expr free, Expr e from UseAfterFreeReachability r, StackVariable v, Expr free, Expr e
where r.reaches(free, v, e) where r.reaches(free, v, e)
select e, "Memory pointed to by '" + v.getName().toString() + "' may have been previously freed $@", select e, "Memory pointed to by '" + v.getName().toString() + "' may have been previously freed $@",
free, "here" free, "here"

View File

@@ -18,49 +18,39 @@ optimizing compiler.
<recommendation> <recommendation>
<p> <p>
Solutions to this problem can be thought of as falling into one of two Solutions to this problem can be thought of as falling into one of two
categories: (1) rewrite the signed expression so that overflow cannot occur categories:
but the signedness remains, or (2) rewrite (or cast) the signed expression
into unsigned form.
</p> </p>
<p> <ol>
Below we list examples of expressions where signed overflow may <li>Rewrite the signed expression so that overflow cannot occur
occur, along with proposed solutions. The list should not be but the signedness remains.</li>
considered exhaustive. <li>Change the variables and all their uses to be unsigned.</li>
</p> </ol>
<p> <p>
Given <code>unsigned short i, delta</code> and <code>i + delta &lt; i</code>, The following cases all fall into the first category.
it is possible to rewrite it as <code>(unsigned short)(i + delta)&nbsp;&lt;&nbsp;i</code>.
Note that <code>i + delta</code>does not actually overflow, due to <code>int</code> promotion
</p> </p>
<p> <ol>
Given <code>unsigned short i, delta</code> and <code>i + delta &lt; i</code>, <li>
it is also possible to rewrite it as <code>USHORT_MAX - delta</code>. It must be true Given <code>unsigned short n1, delta</code> and <code>n1 + delta &lt; n1</code>,
that <code>delta &gt; 0</code> and the <code>limits.h</code> or <code>climits</code> it is possible to rewrite it as <code>(unsigned short)(n1 + delta)&nbsp;&lt;&nbsp;n1</code>.
Note that <code>n1 + delta</code> does not actually overflow, due to <code>int</code> promotion.
</li>
<li>
Given <code>unsigned short n1, delta</code> and <code>n1 + delta &lt; n1</code>,
it is also possible to rewrite it as <code>n1 &gt; USHORT_MAX - delta</code>. The
<code>limits.h</code> or <code>climits</code> header must then be included.
</li>
<li>
Given <code>int n1, delta</code> and <code>n1 + delta &lt; n1</code>,
it is possible to rewrite it as <code>n1 &gt; INT_MAX - delta</code>. It must be true
that <code>delta &gt;= 0</code> and the <code>limits.h</code> or <code>climits</code>
header has been included. header has been included.
</p> </li>
</ol>
<p>
Given <code>int i, delta</code> and <code>i + delta &lt; i</code>,
it is possible to rewrite it as <code>INT_MAX - delta</code>. It must be true
that <code>delta &gt; 0</code> and the <code>limits.h</code> or <code>climits</code>
header has been included.
</p>
<p>
Given <code>int i, delta</code> and <code>i + delta &lt; i</code>,
it is also possible to rewrite it as <code>(unsigned)i + delta &lt; i</code>.
Note that program semantics are affected by this change.
</p>
<p>
Given <code>int i, delta</code> and <code>i + delta &lt; i</code>,
it is also possible to rewrite it as <code>unsigned int i, delta</code> and
<code>i + delta &lt; i</code>. Note that program semantics are
affected by this change.
</p>
</recommendation> </recommendation>
<example> <example>
@@ -68,7 +58,7 @@ affected by this change.
In the following example, even though <code>delta</code> has been declared In the following example, even though <code>delta</code> has been declared
<code>unsigned short</code>, C/C++ type promotion rules require that its <code>unsigned short</code>, C/C++ type promotion rules require that its
type is promoted to the larger type used in the addition and comparison, type is promoted to the larger type used in the addition and comparison,
namely a <code>signed int</code>. Addition is performed on namely a <code>signed int</code>. Addition is performed on
signed integers, and may have undefined behavior if an overflow occurs. signed integers, and may have undefined behavior if an overflow occurs.
As a result, the entire (comparison) expression may also have an undefined As a result, the entire (comparison) expression may also have an undefined
result. result.
@@ -87,10 +77,10 @@ are avoided.
<sample src="SignedOverflowCheck-good1.cpp" /> <sample src="SignedOverflowCheck-good1.cpp" />
<p> <p>
In the following example, even though both <code>n</code> and <code>delta</code> In the following example, even though both <code>n</code> and <code>delta</code>
have been declared <code>unsigned short</code>, both are promoted to have been declared <code>unsigned short</code>, both are promoted to
<code>signed int</code> prior to addition. Because we started out with the <code>signed int</code> prior to addition. Because we started out with the
narrower <code>short</code> type, the addition is guaranteed not to overflow narrower <code>short</code> type, the addition is guaranteed not to overflow
and is therefore defined. But the fact that <code>n1 + delta</code> never and is therefore defined. But the fact that <code>n1 + delta</code> never
overflows means that the condition <code>n1 + delta &lt; n1</code> will never overflows means that the condition <code>n1 + delta &lt; n1</code> will never
hold true, which likely is not what the programmer intended. (see also the hold true, which likely is not what the programmer intended. (see also the
<code>cpp/bad-addition-overflow-check</code> query). <code>cpp/bad-addition-overflow-check</code> query).
@@ -98,7 +88,7 @@ hold true, which likely is not what the programmer intended. (see also the
<sample src="SignedOverflowCheck-bad2.cpp" /> <sample src="SignedOverflowCheck-bad2.cpp" />
<p> <p>
The next example provides a solution to the previous one. Even though The next example provides a solution to the previous one. Even though
<code>i + delta</code> does not overflow, casting it to an <code>n1 + delta</code> does not overflow, casting it to an
<code>unsigned short</code> truncates the addition modulo 2^16, <code>unsigned short</code> truncates the addition modulo 2^16,
so that <code>unsigned short</code> "wrap around" may now be observed. so that <code>unsigned short</code> "wrap around" may now be observed.
Furthermore, since the left-hand side is now of type <code>unsigned short</code>, Furthermore, since the left-hand side is now of type <code>unsigned short</code>,

View File

@@ -14,6 +14,7 @@
import cpp import cpp
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
private import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis private import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
private import semmle.code.cpp.commons.Exclusions
from RelationalOperation ro, AddExpr add, Expr expr1, Expr expr2 from RelationalOperation ro, AddExpr add, Expr expr1, Expr expr2
where where
@@ -22,7 +23,7 @@ where
ro.getAnOperand() = expr2 and ro.getAnOperand() = expr2 and
globalValueNumber(expr1) = globalValueNumber(expr2) and globalValueNumber(expr1) = globalValueNumber(expr2) and
add.getUnspecifiedType().(IntegralType).isSigned() and add.getUnspecifiedType().(IntegralType).isSigned() and
not exists(MacroInvocation mi | mi.getAnAffectedElement() = add) and not isFromMacroDefinition(ro) and
exprMightOverflowPositively(add) and exprMightOverflowPositively(add) and
exists(Compilation c | c.getAFileCompiled() = ro.getFile() | exists(Compilation c | c.getAFileCompiled() = ro.getFile() |
not c.getAnArgument() = "-fwrapv" and not c.getAnArgument() = "-fwrapv" and

View File

@@ -37,7 +37,7 @@ predicate flowsToExprImpl(Expr source, Expr sink, boolean pathMightOverflow) {
pathMightOverflow = false and pathMightOverflow = false and
source.(FunctionCall).getTarget().(Snprintf).returnsFullFormatLength() source.(FunctionCall).getTarget().(Snprintf).returnsFullFormatLength()
or or
exists(RangeSsaDefinition def, LocalScopeVariable v | exists(RangeSsaDefinition def, StackVariable v |
flowsToDef(source, def, v, pathMightOverflow) and flowsToDef(source, def, v, pathMightOverflow) and
sink = def.getAUse(v) sink = def.getAUse(v)
) )
@@ -63,9 +63,7 @@ predicate flowsToExprImpl(Expr source, Expr sink, boolean pathMightOverflow) {
* `pathMightOverflow` is true if there is an arithmetic operation * `pathMightOverflow` is true if there is an arithmetic operation
* on the path that might overflow. * on the path that might overflow.
*/ */
predicate flowsToDef( predicate flowsToDef(Expr source, RangeSsaDefinition def, StackVariable v, boolean pathMightOverflow) {
Expr source, RangeSsaDefinition def, LocalScopeVariable v, boolean pathMightOverflow
) {
// Might the current definition overflow? // Might the current definition overflow?
exists(boolean otherMightOverflow | flowsToDefImpl(source, def, v, otherMightOverflow) | exists(boolean otherMightOverflow | flowsToDefImpl(source, def, v, otherMightOverflow) |
if defMightOverflow(def, v) if defMightOverflow(def, v)
@@ -86,7 +84,7 @@ predicate flowsToDef(
* the path. But it is a good way to reduce the number of false positives. * the path. But it is a good way to reduce the number of false positives.
*/ */
predicate flowsToDefImpl( predicate flowsToDefImpl(
Expr source, RangeSsaDefinition def, LocalScopeVariable v, boolean pathMightOverflow Expr source, RangeSsaDefinition def, StackVariable v, boolean pathMightOverflow
) { ) {
// Assignment or initialization: `e = v;` // Assignment or initialization: `e = v;`
exists(Expr e | exists(Expr e |

View File

@@ -12,24 +12,24 @@
*/ */
import cpp import cpp
import semmle.code.cpp.controlflow.LocalScopeVariableReachability import semmle.code.cpp.controlflow.StackVariableReachability
class UndefReachability extends LocalScopeVariableReachability { class UndefReachability extends StackVariableReachability {
UndefReachability() { this = "UndefReachability" } UndefReachability() { this = "UndefReachability" }
override predicate isSource(ControlFlowNode node, LocalScopeVariable v) { override predicate isSource(ControlFlowNode node, StackVariable v) {
candidateVariable(v) and candidateVariable(v) and
node = v.getParentScope() and node = v.getParentScope() and
not v instanceof Parameter and not v instanceof Parameter and
not v.hasInitializer() not v.hasInitializer()
} }
override predicate isSink(ControlFlowNode node, LocalScopeVariable v) { override predicate isSink(ControlFlowNode node, StackVariable v) {
candidateVariable(v) and candidateVariable(v) and
node = v.getAnAccess() node = v.getAnAccess()
} }
override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) { override predicate isBarrier(ControlFlowNode node, StackVariable v) {
node.(AssignExpr).getLValue() = v.getAnAccess() node.(AssignExpr).getLValue() = v.getAnAccess()
} }
} }

View File

@@ -11,7 +11,7 @@
*/ */
import cpp import cpp
import semmle.code.cpp.controlflow.LocalScopeVariableReachability import semmle.code.cpp.controlflow.StackVariableReachability
import semmle.code.cpp.commons.NullTermination import semmle.code.cpp.commons.NullTermination
/** /**
@@ -22,10 +22,10 @@ DeclStmt declWithNoInit(LocalVariable v) {
not exists(v.getInitializer()) not exists(v.getInitializer())
} }
class ImproperNullTerminationReachability extends LocalScopeVariableReachabilityWithReassignment { class ImproperNullTerminationReachability extends StackVariableReachabilityWithReassignment {
ImproperNullTerminationReachability() { this = "ImproperNullTerminationReachability" } ImproperNullTerminationReachability() { this = "ImproperNullTerminationReachability" }
override predicate isSourceActual(ControlFlowNode node, LocalScopeVariable v) { override predicate isSourceActual(ControlFlowNode node, StackVariable v) {
node = declWithNoInit(v) node = declWithNoInit(v)
or or
exists(Call c, VariableAccess va | exists(Call c, VariableAccess va |
@@ -36,12 +36,12 @@ class ImproperNullTerminationReachability extends LocalScopeVariableReachability
) )
} }
override predicate isSinkActual(ControlFlowNode node, LocalScopeVariable v) { override predicate isSinkActual(ControlFlowNode node, StackVariable v) {
node.(VariableAccess).getTarget() = v and node.(VariableAccess).getTarget() = v and
variableMustBeNullTerminated(node) variableMustBeNullTerminated(node)
} }
override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) { override predicate isBarrier(ControlFlowNode node, StackVariable v) {
exprDefinition(v, node, _) or exprDefinition(v, node, _) or
mayAddNullTerminator(node, v.getAnAccess()) or mayAddNullTerminator(node, v.getAnAccess()) or
isSinkActual(node, v) // only report first use isSinkActual(node, v) // only report first use

View File

@@ -42,10 +42,8 @@ predicate hasNontrivialConversion(Expr e) {
hasNontrivialConversion(e.getConversion()) hasNontrivialConversion(e.getConversion())
} }
from LocalScopeVariable var, VariableAccess va, ReturnStmt r from StackVariable var, VariableAccess va, ReturnStmt r
where where
not var.isStatic() and
not var.isThreadLocal() and
not var.getUnspecifiedType() instanceof ReferenceType and not var.getUnspecifiedType() instanceof ReferenceType and
not r.isFromUninstantiatedTemplate(_) and not r.isFromUninstantiatedTemplate(_) and
va = var.getAnAccess() and va = var.getAnAccess() and

View File

@@ -12,7 +12,7 @@
*/ */
import cpp import cpp
import semmle.code.cpp.controlflow.LocalScopeVariableReachability import semmle.code.cpp.controlflow.StackVariableReachability
/** /**
* Auxiliary predicate: Types that don't require initialization * Auxiliary predicate: Types that don't require initialization
@@ -40,23 +40,17 @@ DeclStmt declWithNoInit(LocalVariable v) {
result.getADeclaration() = v and result.getADeclaration() = v and
not exists(v.getInitializer()) and not exists(v.getInitializer()) and
/* The type of the variable is not stack-allocated. */ /* The type of the variable is not stack-allocated. */
not allocatedType(v.getType()) and not allocatedType(v.getType())
/* The variable is not static (otherwise it is zeroed). */
not v.isStatic() and
/* The variable is not extern (otherwise it is zeroed). */
not v.hasSpecifier("extern")
} }
class UninitialisedLocalReachability extends LocalScopeVariableReachability { class UninitialisedLocalReachability extends StackVariableReachability {
UninitialisedLocalReachability() { this = "UninitialisedLocal" } UninitialisedLocalReachability() { this = "UninitialisedLocal" }
override predicate isSource(ControlFlowNode node, LocalScopeVariable v) { override predicate isSource(ControlFlowNode node, StackVariable v) { node = declWithNoInit(v) }
node = declWithNoInit(v)
}
override predicate isSink(ControlFlowNode node, LocalScopeVariable v) { useOfVarActual(v, node) } override predicate isSink(ControlFlowNode node, StackVariable v) { useOfVarActual(v, node) }
override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) { override predicate isBarrier(ControlFlowNode node, StackVariable v) {
// only report the _first_ possibly uninitialized use // only report the _first_ possibly uninitialized use
useOfVarActual(v, node) or useOfVarActual(v, node) or
definitionBarrier(v, node) definitionBarrier(v, node)

View File

@@ -84,10 +84,10 @@ predicate hasZeroParamDecl(Function f) {
} }
// True if this file (or header) was compiled as a C file // True if this file (or header) was compiled as a C file
predicate isCompiledAsC(Function f) { predicate isCompiledAsC(File f) {
exists(File file | file.compiledAsC() | f.compiledAsC()
file = f.getFile() or file.getAnIncludedFile+() = f.getFile() or
) exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
} }
from FunctionCall fc, Function f, Parameter p from FunctionCall fc, Function f, Parameter p
@@ -95,7 +95,7 @@ where
f = fc.getTarget() and f = fc.getTarget() and
p = f.getAParameter() and p = f.getAParameter() and
hasZeroParamDecl(f) and hasZeroParamDecl(f) and
isCompiledAsC(f) and isCompiledAsC(f.getFile()) and
not f.isVarargs() and not f.isVarargs() and
not f instanceof BuiltInFunction and not f instanceof BuiltInFunction and
p.getIndex() < fc.getNumberOfArguments() and p.getIndex() < fc.getNumberOfArguments() and

View File

@@ -24,10 +24,10 @@ predicate hasZeroParamDecl(Function f) {
} }
// True if this file (or header) was compiled as a C file // True if this file (or header) was compiled as a C file
predicate isCompiledAsC(Function f) { predicate isCompiledAsC(File f) {
exists(File file | file.compiledAsC() | f.compiledAsC()
file = f.getFile() or file.getAnIncludedFile+() = f.getFile() or
) exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
} }
from FunctionCall fc, Function f from FunctionCall fc, Function f
@@ -36,7 +36,7 @@ where
not f.isVarargs() and not f.isVarargs() and
not f instanceof BuiltInFunction and not f instanceof BuiltInFunction and
hasZeroParamDecl(f) and hasZeroParamDecl(f) and
isCompiledAsC(f) and isCompiledAsC(f.getFile()) and
// There is an explicit declaration of the function whose parameter count is larger // There is an explicit declaration of the function whose parameter count is larger
// than the number of call arguments // than the number of call arguments
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() | exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |

View File

@@ -25,10 +25,10 @@ predicate hasZeroParamDecl(Function f) {
} }
// True if this file (or header) was compiled as a C file // True if this file (or header) was compiled as a C file
predicate isCompiledAsC(Function f) { predicate isCompiledAsC(File f) {
exists(File file | file.compiledAsC() | f.compiledAsC()
file = f.getFile() or file.getAnIncludedFile+() = f.getFile() or
) exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
} }
from FunctionCall fc, Function f from FunctionCall fc, Function f
@@ -36,7 +36,7 @@ where
f = fc.getTarget() and f = fc.getTarget() and
not f.isVarargs() and not f.isVarargs() and
hasZeroParamDecl(f) and hasZeroParamDecl(f) and
isCompiledAsC(f) and isCompiledAsC(f.getFile()) and
exists(f.getBlock()) and exists(f.getBlock()) and
// There must not exist a declaration with the number of parameters // There must not exist a declaration with the number of parameters
// at least as large as the number of call arguments // at least as large as the number of call arguments

View File

@@ -16,7 +16,7 @@ private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
import semmle.code.cpp.security.TaintTracking import semmle.code.cpp.security.TaintTracking
predicate hasUpperBound(VariableAccess offsetExpr) { predicate hasUpperBound(VariableAccess offsetExpr) {
exists(BasicBlock controlled, LocalScopeVariable offsetVar, SsaDefinition def | exists(BasicBlock controlled, StackVariable offsetVar, SsaDefinition def |
controlled.contains(offsetExpr) and controlled.contains(offsetExpr) and
linearBoundControls(controlled, def, offsetVar) and linearBoundControls(controlled, def, offsetVar) and
offsetExpr = def.getAUse(offsetVar) offsetExpr = def.getAUse(offsetVar)
@@ -24,7 +24,7 @@ predicate hasUpperBound(VariableAccess offsetExpr) {
} }
pragma[noinline] pragma[noinline]
predicate linearBoundControls(BasicBlock controlled, SsaDefinition def, LocalScopeVariable offsetVar) { predicate linearBoundControls(BasicBlock controlled, SsaDefinition def, StackVariable offsetVar) {
exists(GuardCondition guard, boolean branch | exists(GuardCondition guard, boolean branch |
guard.controls(controlled, branch) and guard.controls(controlled, branch) and
cmpWithLinearBound(guard, def.getAUse(offsetVar), Lesser(), branch) cmpWithLinearBound(guard, def.getAUse(offsetVar), Lesser(), branch)

View File

@@ -44,7 +44,7 @@ Element friendlyLoc(Expr e) {
not e instanceof Access and not e instanceof Call and result = e not e instanceof Access and not e instanceof Call and result = e
} }
from Loop l, RelationalOperation rel, Expr small, Expr large from Loop l, RelationalOperation rel, VariableAccess small, Expr large
where where
small = rel.getLesserOperand() and small = rel.getLesserOperand() and
large = rel.getGreaterOperand() and large = rel.getGreaterOperand() and
@@ -60,7 +60,7 @@ where
not getComparisonSize(large.(SubExpr).getLeftOperand().getExplicitlyConverted()) <= getComparisonSize(small) and not getComparisonSize(large.(SubExpr).getLeftOperand().getExplicitlyConverted()) <= getComparisonSize(small) and
not getComparisonSize(large.(RShiftExpr).getLeftOperand().getExplicitlyConverted()) <= getComparisonSize(small) and not getComparisonSize(large.(RShiftExpr).getLeftOperand().getExplicitlyConverted()) <= getComparisonSize(small) and
// ignore loop-invariant smaller variables // ignore loop-invariant smaller variables
loopVariant(small.getAChild*(), l) loopVariant(small, l)
select rel, select rel,
"Comparison between $@ of type " + small.getType().getName() + " and $@ of wider type " + "Comparison between $@ of type " + small.getType().getName() + " and $@ of wider type " +
large.getType().getName() + ".", friendlyLoc(small), small.toString(), friendlyLoc(large), large.getType().getName() + ".", friendlyLoc(small), small.toString(), friendlyLoc(large),

View File

@@ -82,7 +82,7 @@ FunctionCall stat(Expr path, Expr buf) {
predicate referenceTo(Expr source, Expr use) { predicate referenceTo(Expr source, Expr use) {
source = use source = use
or or
exists(SsaDefinition def, LocalScopeVariable v | exists(SsaDefinition def, StackVariable v |
def.getAnUltimateDefiningValue(v) = source and def.getAUse(v) = use def.getAnUltimateDefiningValue(v) = source and def.getAUse(v) = use
) )
} }
@@ -109,9 +109,7 @@ where
) )
) and ) and
// checkUse and opUse refer to the same SSA variable // checkUse and opUse refer to the same SSA variable
exists(SsaDefinition def, LocalScopeVariable v | exists(SsaDefinition def, StackVariable v | def.getAUse(v) = checkUse and def.getAUse(v) = opUse) and
def.getAUse(v) = checkUse and def.getAUse(v) = opUse
) and
// opUse looks like an operation on a filename // opUse looks like an operation on a filename
fc = filenameOperation(opUse) and fc = filenameOperation(opUse) and
// the return value of check is used (possibly with one step of // the return value of check is used (possibly with one step of

View File

@@ -100,9 +100,9 @@ Type baseType(Type t) {
*/ */
predicate exprSourceType(Expr use, Type sourceType, Location sourceLoc) { predicate exprSourceType(Expr use, Type sourceType, Location sourceLoc) {
// Reaching definitions. // Reaching definitions.
if exists(SsaDefinition def, LocalScopeVariable v | use = def.getAUse(v)) if exists(SsaDefinition def, StackVariable v | use = def.getAUse(v))
then then
exists(SsaDefinition def, LocalScopeVariable v | use = def.getAUse(v) | exists(SsaDefinition def, StackVariable v | use = def.getAUse(v) |
defSourceType(def, v, sourceType, sourceLoc) defSourceType(def, v, sourceType, sourceLoc)
) )
else else
@@ -137,7 +137,7 @@ predicate exprSourceType(Expr use, Type sourceType, Location sourceLoc) {
* Holds if there is a pointer expression with type `sourceType` at * Holds if there is a pointer expression with type `sourceType` at
* location `sourceLoc` which might define the value of `v` at `def`. * location `sourceLoc` which might define the value of `v` at `def`.
*/ */
predicate defSourceType(SsaDefinition def, LocalScopeVariable v, Type sourceType, Location sourceLoc) { predicate defSourceType(SsaDefinition def, StackVariable v, Type sourceType, Location sourceLoc) {
exprSourceType(def.getDefiningValue(v), sourceType, sourceLoc) exprSourceType(def.getDefiningValue(v), sourceType, sourceLoc)
or or
defSourceType(def.getAPhiInput(v), v, sourceType, sourceLoc) defSourceType(def.getAPhiInput(v), v, sourceType, sourceLoc)

View File

@@ -20,10 +20,9 @@ class ReturnPointsToExpr extends PointsToExpr {
ReturnStmt getReturnStmt() { result.getExpr() = this } ReturnStmt getReturnStmt() { result.getExpr() = this }
} }
from ReturnPointsToExpr ret, LocalVariable dest from ReturnPointsToExpr ret, StackVariable dest
where where
ret.pointsTo() = dest and ret.pointsTo() = dest and
ret.getReturnStmt().getParentStmt().getEnclosingFunction() = dest.getFunction() and ret.getReturnStmt().getParentStmt().getEnclosingFunction() = dest.getFunction()
not dest.isStatic()
select ret.getReturnStmt(), select ret.getReturnStmt(),
"AV Rule 111: A function shall not return a pointer or reference to a non-static local object." "AV Rule 111: A function shall not return a pointer or reference to a non-static local object."

View File

@@ -13,6 +13,11 @@ Element stmtEnclosingElement(Stmt s) {
/** /**
* Gets the enclosing element of expression `e`. * Gets the enclosing element of expression `e`.
*/ */
// The `pragma[nomagic]` is a workaround to prevent this cached stage (and all
// subsequent stages) from being evaluated twice. See QL-888. It has the effect
// of making the `Conversion` class predicate get the same optimization in all
// queries.
pragma[nomagic]
cached cached
Element exprEnclosingElement(Expr e) { Element exprEnclosingElement(Expr e) {
result = exprEnclosingElement(e.getParent()) result = exprEnclosingElement(e.getParent())

View File

@@ -307,8 +307,8 @@ class ParameterDeclarationEntry extends VariableDeclarationEntry {
* } * }
* ``` * ```
* *
* Local variables can be static; use the `isStatic` member predicate to * See also `StackVariable`, which is the class of local-scope variables
* detect those. * without statics and thread-locals.
*/ */
class LocalScopeVariable extends Variable, @localscopevariable { class LocalScopeVariable extends Variable, @localscopevariable {
/** Gets the function to which this variable belongs. */ /** Gets the function to which this variable belongs. */
@@ -316,12 +316,21 @@ class LocalScopeVariable extends Variable, @localscopevariable {
} }
/** /**
* DEPRECATED: use `LocalScopeVariable` instead. * A C/C++ variable with _automatic storage duration_. In other words, a
* function parameter or a local variable that is not static or thread-local.
* For example, the variables `a` and `b` in the following code.
* ```
* void myFunction(int a) {
* int b;
* static int c;
* }
* ```
*/ */
deprecated class StackVariable extends Variable { class StackVariable extends LocalScopeVariable {
StackVariable() { this instanceof LocalScopeVariable } StackVariable() {
not this.isStatic() and
Function getFunction() { result = this.(LocalScopeVariable).getFunction() } not this.isThreadLocal()
}
} }
/** /**
@@ -496,7 +505,7 @@ class TemplateVariable extends Variable {
* `myTemplateFunction<T>`: * `myTemplateFunction<T>`:
* ``` * ```
* void myFunction() { * void myFunction() {
* T a; * float a;
* } * }
* *
* template<type T> * template<type T>
@@ -509,9 +518,6 @@ class TemplateVariable extends Variable {
* myTemplateFunction<int>(); * myTemplateFunction<int>();
* ``` * ```
*/ */
class SemanticStackVariable extends LocalScopeVariable { class SemanticStackVariable extends StackVariable {
SemanticStackVariable() { SemanticStackVariable() { not this.isFromUninstantiatedTemplate(_) }
not this.isStatic() and
not this.isFromUninstantiatedTemplate(_)
}
} }

View File

@@ -94,11 +94,18 @@ predicate functionContainsPreprocCode(Function f) {
* ``` * ```
*/ */
predicate isFromMacroDefinition(Element e) { predicate isFromMacroDefinition(Element e) {
exists(MacroInvocation mi | exists(MacroInvocation mi, Location eLocation, Location miLocation |
// e is in mi
mi.getAnExpandedElement() = e and mi.getAnExpandedElement() = e and
// and e was apparently not passed in as a macro parameter eLocation = e.getLocation() and
e.getLocation().getStartLine() = mi.getLocation().getStartLine() and miLocation = mi.getLocation() and
e.getLocation().getStartColumn() = mi.getLocation().getStartColumn() // If the location of `e` coincides with the macro invocation, then `e` did
// not come from a macro argument. The inequalities here could also be
// equalities, but that confuses the join orderer into joining on the source
// locations too early.
// There are cases where the start location of a non-argument element comes
// right after the invocation's open parenthesis, so it appears to be more
// robust to match on the end location instead.
eLocation.getEndLine() >= miLocation.getEndLine() and
eLocation.getEndColumn() >= miLocation.getEndColumn()
) )
} }

View File

@@ -3,7 +3,7 @@ private import semmle.code.cpp.models.interfaces.ArrayFunction
private import semmle.code.cpp.models.implementations.Strcat private import semmle.code.cpp.models.implementations.Strcat
private predicate mayAddNullTerminatorHelper(Expr e, VariableAccess va, Expr e0) { private predicate mayAddNullTerminatorHelper(Expr e, VariableAccess va, Expr e0) {
exists(LocalScopeVariable v0, Expr val | exists(StackVariable v0, Expr val |
exprDefinition(v0, e, val) and exprDefinition(v0, e, val) and
val.getAChild*() = va and val.getAChild*() = va and
mayAddNullTerminator(e0, v0.getAnAccess()) mayAddNullTerminator(e0, v0.getAnAccess())
@@ -41,7 +41,7 @@ predicate mayAddNullTerminator(Expr e, VariableAccess va) {
or or
// Assignment to non-stack variable // Assignment to non-stack variable
exists(AssignExpr ae | e = ae | exists(AssignExpr ae | e = ae |
not ae.getLValue().(VariableAccess).getTarget() instanceof LocalScopeVariable and not ae.getLValue().(VariableAccess).getTarget() instanceof StackVariable and
ae.getRValue().getAChild*() = va ae.getRValue().getAChild*() = va
) )
or or

View File

@@ -1,5 +1,5 @@
import cpp import cpp
private import semmle.code.cpp.controlflow.LocalScopeVariableReachability private import semmle.code.cpp.controlflow.StackVariableReachability
private import semmle.code.cpp.dataflow.EscapesTree private import semmle.code.cpp.dataflow.EscapesTree
/** /**
@@ -108,7 +108,7 @@ library class DefOrUse extends ControlFlowNodeBase {
/* /*
* Implementation detail: this predicate and its private auxiliary * Implementation detail: this predicate and its private auxiliary
* predicates are instances of the more general predicates in * predicates are instances of the more general predicates in
* LocalScopeVariableReachability.qll, and should be kept in sync. * StackVariableReachability.qll, and should be kept in sync.
* *
* Unfortunately, caching of abstract predicates does not work well, so the * Unfortunately, caching of abstract predicates does not work well, so the
* predicates are duplicated for now. * predicates are duplicated for now.

View File

@@ -371,7 +371,7 @@ private int int_value(Expr e) {
/** An `SsaDefinition` with an additional predicate `isLt`. */ /** An `SsaDefinition` with an additional predicate `isLt`. */
class GuardedSsa extends SsaDefinition { class GuardedSsa extends SsaDefinition {
/** Holds if this `SsaDefinition` is guarded such that `this(var) < gt + k` is `testIsTrue` in `block`. */ /** Holds if this `SsaDefinition` is guarded such that `this(var) < gt + k` is `testIsTrue` in `block`. */
predicate isLt(LocalScopeVariable var, Expr gt, int k, BasicBlock block, boolean testIsTrue) { predicate isLt(StackVariable var, Expr gt, int k, BasicBlock block, boolean testIsTrue) {
exists(Expr luse, GuardCondition test | this.getAUse(var) = luse | exists(Expr luse, GuardCondition test | this.getAUse(var) = luse |
test.ensuresLt(luse, gt, k, block, testIsTrue) test.ensuresLt(luse, gt, k, block, testIsTrue)
) )

View File

@@ -1,6 +1,12 @@
/**
* DEPRECATED: Use `StackVariableReachability` instead.
*/
import cpp import cpp
/** /**
* DEPRECATED: Use `StackVariableReachability` instead.
*
* A reachability analysis for control-flow nodes involving stack variables. * A reachability analysis for control-flow nodes involving stack variables.
* This defines sources, sinks, and any other configurable aspect of the * This defines sources, sinks, and any other configurable aspect of the
* analysis. Multiple analyses can coexist. To create an analysis, extend this * analysis. Multiple analyses can coexist. To create an analysis, extend this
@@ -18,7 +24,7 @@ import cpp
* Then, to query whether there is flow between some source and sink, call the * Then, to query whether there is flow between some source and sink, call the
* `reaches` predicate on an instance of `MyAnalysisConfiguration`. * `reaches` predicate on an instance of `MyAnalysisConfiguration`.
*/ */
abstract class LocalScopeVariableReachability extends string { abstract deprecated class LocalScopeVariableReachability extends string {
bindingset[this] bindingset[this]
LocalScopeVariableReachability() { length() >= 0 } LocalScopeVariableReachability() { length() >= 0 }
@@ -207,6 +213,8 @@ predicate bbSuccessorEntryReachesLoopInvariant(
} }
/** /**
* DEPRECATED: Use `StackVariableReachabilityWithReassignment` instead.
*
* Reachability analysis for control-flow nodes involving stack variables. * Reachability analysis for control-flow nodes involving stack variables.
* Unlike `LocalScopeVariableReachability`, this analysis takes variable * Unlike `LocalScopeVariableReachability`, this analysis takes variable
* reassignments into account. * reassignments into account.
@@ -216,7 +224,7 @@ predicate bbSuccessorEntryReachesLoopInvariant(
* `isSource` and `isSink`, and that there is a `reachesTo` predicate in * `isSource` and `isSink`, and that there is a `reachesTo` predicate in
* addition to `reaches`. * addition to `reaches`.
*/ */
abstract class LocalScopeVariableReachabilityWithReassignment extends LocalScopeVariableReachability { abstract deprecated class LocalScopeVariableReachabilityWithReassignment extends LocalScopeVariableReachability {
bindingset[this] bindingset[this]
LocalScopeVariableReachabilityWithReassignment() { length() >= 0 } LocalScopeVariableReachabilityWithReassignment() { length() >= 0 }
@@ -314,12 +322,14 @@ abstract class LocalScopeVariableReachabilityWithReassignment extends LocalScope
} }
/** /**
* DEPRECATED: Use `StackVariableReachabilityExt` instead.
*
* Same as `LocalScopeVariableReachability`, but `isBarrier` works on control-flow * Same as `LocalScopeVariableReachability`, but `isBarrier` works on control-flow
* edges rather than nodes and is therefore parameterized by the original * edges rather than nodes and is therefore parameterized by the original
* source node as well. Otherwise, this class is used like * source node as well. Otherwise, this class is used like
* `LocalScopeVariableReachability`. * `LocalScopeVariableReachability`.
*/ */
abstract class LocalScopeVariableReachabilityExt extends string { abstract deprecated class LocalScopeVariableReachabilityExt extends string {
bindingset[this] bindingset[this]
LocalScopeVariableReachabilityExt() { length() >= 0 } LocalScopeVariableReachabilityExt() { length() >= 0 }

View File

@@ -9,11 +9,11 @@ library class StandardSSA extends SSAHelper {
/** /**
* A definition of one or more SSA variables, including phi node definitions. * A definition of one or more SSA variables, including phi node definitions.
* An _SSA variable_, as defined in the literature, is effectively the pair of * An _SSA variable_, as defined in the literature, is effectively the pair of
* an `SsaDefinition d` and a `LocalScopeVariable v`, written `(d, v)` in this * an `SsaDefinition d` and a `StackVariable v`, written `(d, v)` in this
* documentation. Note that definitions and uses can be coincident due to the * documentation. Note that definitions and uses can be coincident due to the
* presence of parameter definitions and phi nodes. * presence of parameter definitions and phi nodes.
* *
* Not all `LocalScopeVariable`s of a function have SSA definitions. If the variable * Not all `StackVariable`s of a function have SSA definitions. If the variable
* has its address taken, either explicitly or implicitly, then it is excluded * has its address taken, either explicitly or implicitly, then it is excluded
* from analysis. `SsaDefinition`s are not generated in locations that are * from analysis. `SsaDefinition`s are not generated in locations that are
* statically seen to be unreachable. * statically seen to be unreachable.
@@ -22,21 +22,19 @@ class SsaDefinition extends ControlFlowNodeBase {
SsaDefinition() { exists(StandardSSA x | x.ssa_defn(_, this, _, _)) } SsaDefinition() { exists(StandardSSA x | x.ssa_defn(_, this, _, _)) }
/** /**
* Gets a variable corresponding to an SSA LocalScopeVariable defined by * Gets a variable corresponding to an SSA StackVariable defined by
* this definition. * this definition.
*/ */
LocalScopeVariable getAVariable() { exists(StandardSSA x | x.ssa_defn(result, this, _, _)) } StackVariable getAVariable() { exists(StandardSSA x | x.ssa_defn(result, this, _, _)) }
/** /**
* Gets a string representation of the SSA variable represented by the pair * Gets a string representation of the SSA variable represented by the pair
* `(this, v)`. * `(this, v)`.
*/ */
string toString(LocalScopeVariable v) { exists(StandardSSA x | result = x.toString(this, v)) } string toString(StackVariable v) { exists(StandardSSA x | result = x.toString(this, v)) }
/** Gets a use of the SSA variable represented by the pair `(this, v)`. */ /** Gets a use of the SSA variable represented by the pair `(this, v)`. */
VariableAccess getAUse(LocalScopeVariable v) { VariableAccess getAUse(StackVariable v) { exists(StandardSSA x | result = x.getAUse(this, v)) }
exists(StandardSSA x | result = x.getAUse(this, v))
}
/** /**
* Gets the control-flow node for this definition. This will usually be the * Gets the control-flow node for this definition. This will usually be the
@@ -55,9 +53,7 @@ class SsaDefinition extends ControlFlowNodeBase {
BasicBlock getBasicBlock() { result.contains(getDefinition()) } BasicBlock getBasicBlock() { result.contains(getDefinition()) }
/** Holds if this definition is a phi node for variable `v`. */ /** Holds if this definition is a phi node for variable `v`. */
predicate isPhiNode(LocalScopeVariable v) { predicate isPhiNode(StackVariable v) { exists(StandardSSA x | x.phi_node(v, this.(BasicBlock))) }
exists(StandardSSA x | x.phi_node(v, this.(BasicBlock)))
}
Location getLocation() { result = this.(ControlFlowNode).getLocation() } Location getLocation() { result = this.(ControlFlowNode).getLocation() }
@@ -68,7 +64,7 @@ class SsaDefinition extends ControlFlowNodeBase {
* Holds if the SSA variable `(result, v)` is an input to the phi definition * Holds if the SSA variable `(result, v)` is an input to the phi definition
* `(this, v)`. * `(this, v)`.
*/ */
SsaDefinition getAPhiInput(LocalScopeVariable v) { SsaDefinition getAPhiInput(StackVariable v) {
this.isPhiNode(v) and this.isPhiNode(v) and
result.reachesEndOfBB(v, this.(BasicBlock).getAPredecessor()) result.reachesEndOfBB(v, this.(BasicBlock).getAPredecessor())
} }
@@ -92,7 +88,7 @@ class SsaDefinition extends ControlFlowNodeBase {
* instead covered via `definedByParameter` and `getDefinition`, * instead covered via `definedByParameter` and `getDefinition`,
* respectively. * respectively.
*/ */
Expr getDefiningValue(LocalScopeVariable v) { Expr getDefiningValue(StackVariable v) {
exists(ControlFlowNode def | def = this.getDefinition() | exists(ControlFlowNode def | def = this.getDefinition() |
def = v.getInitializer().getExpr() and def = result def = v.getInitializer().getExpr() and def = result
or or
@@ -117,7 +113,7 @@ class SsaDefinition extends ControlFlowNodeBase {
} }
/** Holds if `(this, v)` reaches the end of basic block `b`. */ /** Holds if `(this, v)` reaches the end of basic block `b`. */
predicate reachesEndOfBB(LocalScopeVariable v, BasicBlock b) { predicate reachesEndOfBB(StackVariable v, BasicBlock b) {
exists(StandardSSA x | x.ssaDefinitionReachesEndOfBB(v, this, b)) exists(StandardSSA x | x.ssaDefinitionReachesEndOfBB(v, this, b))
} }
@@ -125,7 +121,7 @@ class SsaDefinition extends ControlFlowNodeBase {
* Gets a definition that ultimately defines this variable and is not * Gets a definition that ultimately defines this variable and is not
* itself a phi node. * itself a phi node.
*/ */
SsaDefinition getAnUltimateSsaDefinition(LocalScopeVariable v) { SsaDefinition getAnUltimateSsaDefinition(StackVariable v) {
result = this.getAPhiInput(v).getAnUltimateSsaDefinition(v) result = this.getAPhiInput(v).getAnUltimateSsaDefinition(v)
or or
v = this.getAVariable() and v = this.getAVariable() and
@@ -138,7 +134,7 @@ class SsaDefinition extends ControlFlowNodeBase {
* recursing backwards through phi definitions. Not all definitions have a * recursing backwards through phi definitions. Not all definitions have a
* defining expression---see the documentation for `getDefiningValue`. * defining expression---see the documentation for `getDefiningValue`.
*/ */
Expr getAnUltimateDefiningValue(LocalScopeVariable v) { Expr getAnUltimateDefiningValue(StackVariable v) {
result = this.getAnUltimateSsaDefinition(v).getDefiningValue(v) result = this.getAnUltimateSsaDefinition(v).getDefiningValue(v)
} }
@@ -149,7 +145,7 @@ class SsaDefinition extends ControlFlowNodeBase {
* `getAnUltimateSsaDefinition` to refer to a predicate named * `getAnUltimateSsaDefinition` to refer to a predicate named
* `getAnUltimateSsaDefinition` in this class. * `getAnUltimateSsaDefinition` in this class.
*/ */
deprecated Expr getAnUltimateDefinition(LocalScopeVariable v) { deprecated Expr getAnUltimateDefinition(StackVariable v) {
result = this.getAnUltimateDefiningValue(v) result = this.getAnUltimateDefiningValue(v)
} }
} }

View File

@@ -21,11 +21,9 @@ private predicate dominanceFrontier(BasicBlock x, BasicBlock w) {
} }
/** /**
* Extended version of `definition` that also includes parameters but excludes * Extended version of `definition` that also includes parameters.
* static variables.
*/ */
predicate var_definition(LocalScopeVariable v, ControlFlowNode node) { predicate var_definition(StackVariable v, ControlFlowNode node) {
not v.isStatic() and
not addressTakenVariable(v) and not addressTakenVariable(v) and
not unreachable(node) and not unreachable(node) and
( (
@@ -51,7 +49,7 @@ predicate var_definition(LocalScopeVariable v, ControlFlowNode node) {
* analysis because the pointer could be used to change the value at * analysis because the pointer could be used to change the value at
* any moment. * any moment.
*/ */
private predicate addressTakenVariable(LocalScopeVariable var) { private predicate addressTakenVariable(StackVariable var) {
// If the type of the variable is a reference type, then it is safe (as // If the type of the variable is a reference type, then it is safe (as
// far as SSA is concerned) to take its address, because this does not // far as SSA is concerned) to take its address, because this does not
// enable the variable to be modified indirectly. Obviously the // enable the variable to be modified indirectly. Obviously the
@@ -71,7 +69,7 @@ private predicate addressTakenVariable(LocalScopeVariable var) {
) )
} }
private predicate isReferenceVar(LocalScopeVariable v) { private predicate isReferenceVar(StackVariable v) {
v.getUnspecifiedType() instanceof ReferenceType v.getUnspecifiedType() instanceof ReferenceType
} }
@@ -79,22 +77,22 @@ private predicate isReferenceVar(LocalScopeVariable v) {
* This predicate is the same as `var_definition`, but annotated with * This predicate is the same as `var_definition`, but annotated with
* the basic block and index of the control flow node. * the basic block and index of the control flow node.
*/ */
private predicate variableUpdate(LocalScopeVariable v, ControlFlowNode n, BasicBlock b, int i) { private predicate variableUpdate(StackVariable v, ControlFlowNode n, BasicBlock b, int i) {
var_definition(v, n) and n = b.getNode(i) var_definition(v, n) and n = b.getNode(i)
} }
private predicate ssa_use(LocalScopeVariable v, VariableAccess node, BasicBlock b, int index) { private predicate ssa_use(StackVariable v, VariableAccess node, BasicBlock b, int index) {
useOfVar(v, node) and b.getNode(index) = node useOfVar(v, node) and b.getNode(index) = node
} }
private predicate live_at_start_of_bb(LocalScopeVariable v, BasicBlock b) { private predicate live_at_start_of_bb(StackVariable v, BasicBlock b) {
exists(int i | ssa_use(v, _, b, i) | not exists(int j | variableUpdate(v, _, b, j) | j < i)) exists(int i | ssa_use(v, _, b, i) | not exists(int j | variableUpdate(v, _, b, j) | j < i))
or or
live_at_exit_of_bb(v, b) and not variableUpdate(v, _, b, _) live_at_exit_of_bb(v, b) and not variableUpdate(v, _, b, _)
} }
pragma[noinline] pragma[noinline]
private predicate live_at_exit_of_bb(LocalScopeVariable v, BasicBlock b) { private predicate live_at_exit_of_bb(StackVariable v, BasicBlock b) {
live_at_start_of_bb(v, b.getASuccessor()) live_at_start_of_bb(v, b.getASuccessor())
} }
@@ -110,12 +108,12 @@ library class SSAHelper extends int {
* basic block `b`. * basic block `b`.
*/ */
cached cached
predicate custom_phi_node(LocalScopeVariable v, BasicBlock b) { none() } predicate custom_phi_node(StackVariable v, BasicBlock b) { none() }
/** /**
* Remove any custom phi nodes that are invalid. * Remove any custom phi nodes that are invalid.
*/ */
private predicate sanitized_custom_phi_node(LocalScopeVariable v, BasicBlock b) { private predicate sanitized_custom_phi_node(StackVariable v, BasicBlock b) {
custom_phi_node(v, b) and custom_phi_node(v, b) and
not addressTakenVariable(v) and not addressTakenVariable(v) and
not isReferenceVar(v) and not isReferenceVar(v) and
@@ -127,7 +125,7 @@ library class SSAHelper extends int {
* `b`. * `b`.
*/ */
cached cached
predicate phi_node(LocalScopeVariable v, BasicBlock b) { predicate phi_node(StackVariable v, BasicBlock b) {
frontier_phi_node(v, b) or sanitized_custom_phi_node(v, b) frontier_phi_node(v, b) or sanitized_custom_phi_node(v, b)
} }
@@ -138,13 +136,13 @@ library class SSAHelper extends int {
* definitions). This is known as the iterated dominance frontier. See * definitions). This is known as the iterated dominance frontier. See
* Modern Compiler Implementation by Andrew Appel. * Modern Compiler Implementation by Andrew Appel.
*/ */
private predicate frontier_phi_node(LocalScopeVariable v, BasicBlock b) { private predicate frontier_phi_node(StackVariable v, BasicBlock b) {
exists(BasicBlock x | dominanceFrontier(x, b) and ssa_defn_rec(v, x)) and exists(BasicBlock x | dominanceFrontier(x, b) and ssa_defn_rec(v, x)) and
/* We can also eliminate those nodes where the variable is not live on any incoming edge */ /* We can also eliminate those nodes where the variable is not live on any incoming edge */
live_at_start_of_bb(v, b) live_at_start_of_bb(v, b)
} }
private predicate ssa_defn_rec(LocalScopeVariable v, BasicBlock b) { private predicate ssa_defn_rec(StackVariable v, BasicBlock b) {
phi_node(v, b) phi_node(v, b)
or or
variableUpdate(v, _, b, _) variableUpdate(v, _, b, _)
@@ -155,7 +153,7 @@ library class SSAHelper extends int {
* position `index` in block `b`. This includes definitions from phi nodes. * position `index` in block `b`. This includes definitions from phi nodes.
*/ */
cached cached
predicate ssa_defn(LocalScopeVariable v, ControlFlowNode node, BasicBlock b, int index) { predicate ssa_defn(StackVariable v, ControlFlowNode node, BasicBlock b, int index) {
phi_node(v, b) and b.getStart() = node and index = -1 phi_node(v, b) and b.getStart() = node and index = -1
or or
variableUpdate(v, node, b, index) variableUpdate(v, node, b, index)
@@ -179,7 +177,7 @@ library class SSAHelper extends int {
* irrelevant indices at which there is no definition or use when traversing * irrelevant indices at which there is no definition or use when traversing
* basic blocks. * basic blocks.
*/ */
private predicate defUseRank(LocalScopeVariable v, BasicBlock b, int rankix, int i) { private predicate defUseRank(StackVariable v, BasicBlock b, int rankix, int i) {
i = rank[rankix](int j | ssa_defn(v, _, b, j) or ssa_use(v, _, b, j)) i = rank[rankix](int j | ssa_defn(v, _, b, j) or ssa_use(v, _, b, j))
} }
@@ -189,7 +187,7 @@ library class SSAHelper extends int {
* the extra rank at the end represents a position past the last node in * the extra rank at the end represents a position past the last node in
* the block. * the block.
*/ */
private int lastRank(LocalScopeVariable v, BasicBlock b) { private int lastRank(StackVariable v, BasicBlock b) {
result = max(int rankix | defUseRank(v, b, rankix, _)) + 1 result = max(int rankix | defUseRank(v, b, rankix, _)) + 1
} }
@@ -197,7 +195,7 @@ library class SSAHelper extends int {
* Holds if SSA variable `(v, def)` is defined at rank index `rankix` in * Holds if SSA variable `(v, def)` is defined at rank index `rankix` in
* basic block `b`. * basic block `b`.
*/ */
private predicate ssaDefRank(LocalScopeVariable v, ControlFlowNode def, BasicBlock b, int rankix) { private predicate ssaDefRank(StackVariable v, ControlFlowNode def, BasicBlock b, int rankix) {
exists(int i | exists(int i |
ssa_defn(v, def, b, i) and ssa_defn(v, def, b, i) and
defUseRank(v, b, rankix, i) defUseRank(v, b, rankix, i)
@@ -210,9 +208,7 @@ library class SSAHelper extends int {
* `v` that comes _at or after_ the reached node. Reaching a node means * `v` that comes _at or after_ the reached node. Reaching a node means
* that the definition is visible to any _use_ at that node. * that the definition is visible to any _use_ at that node.
*/ */
private predicate ssaDefReachesRank( private predicate ssaDefReachesRank(StackVariable v, ControlFlowNode def, BasicBlock b, int rankix) {
LocalScopeVariable v, ControlFlowNode def, BasicBlock b, int rankix
) {
// A definition should not reach its own node unless a loop allows it. // A definition should not reach its own node unless a loop allows it.
// When nodes are both definitions and uses for the same variable, the // When nodes are both definitions and uses for the same variable, the
// use is understood to happen _before_ the definition. Phi nodes are // use is understood to happen _before_ the definition. Phi nodes are
@@ -227,7 +223,7 @@ library class SSAHelper extends int {
/** Holds if SSA variable `(v, def)` reaches the end of block `b`. */ /** Holds if SSA variable `(v, def)` reaches the end of block `b`. */
cached cached
predicate ssaDefinitionReachesEndOfBB(LocalScopeVariable v, ControlFlowNode def, BasicBlock b) { predicate ssaDefinitionReachesEndOfBB(StackVariable v, ControlFlowNode def, BasicBlock b) {
live_at_exit_of_bb(v, b) and ssaDefReachesRank(v, def, b, lastRank(v, b)) live_at_exit_of_bb(v, b) and ssaDefReachesRank(v, def, b, lastRank(v, b))
or or
exists(BasicBlock idom | exists(BasicBlock idom |
@@ -243,7 +239,7 @@ library class SSAHelper extends int {
* reaches the end of `b`. * reaches the end of `b`.
*/ */
pragma[noinline] pragma[noinline]
private predicate noDefinitionsSinceIDominator(LocalScopeVariable v, BasicBlock idom, BasicBlock b) { private predicate noDefinitionsSinceIDominator(StackVariable v, BasicBlock idom, BasicBlock b) {
bbIDominates(idom, b) and // It is sufficient to traverse the dominator graph, cf. discussion above. bbIDominates(idom, b) and // It is sufficient to traverse the dominator graph, cf. discussion above.
live_at_exit_of_bb(v, b) and live_at_exit_of_bb(v, b) and
not ssa_defn(v, _, b, _) not ssa_defn(v, _, b, _)
@@ -253,9 +249,7 @@ library class SSAHelper extends int {
* Holds if SSA variable `(v, def)` reaches `use` within the same basic * Holds if SSA variable `(v, def)` reaches `use` within the same basic
* block, where `use` is a `VariableAccess` of `v`. * block, where `use` is a `VariableAccess` of `v`.
*/ */
private predicate ssaDefinitionReachesUseWithinBB( private predicate ssaDefinitionReachesUseWithinBB(StackVariable v, ControlFlowNode def, Expr use) {
LocalScopeVariable v, ControlFlowNode def, Expr use
) {
exists(BasicBlock b, int rankix, int i | exists(BasicBlock b, int rankix, int i |
ssaDefReachesRank(v, def, b, rankix) and ssaDefReachesRank(v, def, b, rankix) and
defUseRank(v, b, rankix, i) and defUseRank(v, b, rankix, i) and
@@ -266,7 +260,7 @@ library class SSAHelper extends int {
/** /**
* Holds if SSA variable `(v, def)` reaches the control-flow node `use`. * Holds if SSA variable `(v, def)` reaches the control-flow node `use`.
*/ */
private predicate ssaDefinitionReaches(LocalScopeVariable v, ControlFlowNode def, Expr use) { private predicate ssaDefinitionReaches(StackVariable v, ControlFlowNode def, Expr use) {
ssaDefinitionReachesUseWithinBB(v, def, use) ssaDefinitionReachesUseWithinBB(v, def, use)
or or
exists(BasicBlock b | exists(BasicBlock b |
@@ -281,7 +275,7 @@ library class SSAHelper extends int {
* `(node, v)`. * `(node, v)`.
*/ */
cached cached
string toString(ControlFlowNode node, LocalScopeVariable v) { string toString(ControlFlowNode node, StackVariable v) {
if phi_node(v, node.(BasicBlock)) if phi_node(v, node.(BasicBlock))
then result = "SSA phi(" + v.getName() + ")" then result = "SSA phi(" + v.getName() + ")"
else ( else (
@@ -294,7 +288,7 @@ library class SSAHelper extends int {
* access of `v`. * access of `v`.
*/ */
cached cached
VariableAccess getAUse(ControlFlowNode def, LocalScopeVariable v) { VariableAccess getAUse(ControlFlowNode def, StackVariable v) {
ssaDefinitionReaches(v, def, result) and ssaDefinitionReaches(v, def, result) and
ssa_use(v, result, _, _) ssa_use(v, result, _, _)
} }

View File

@@ -1,11 +1,24 @@
import cpp import cpp
/** /**
* DEPRECATED: use `LocalScopeVariableReachability` instead.
*
* A reachability analysis for control-flow nodes involving stack variables. * A reachability analysis for control-flow nodes involving stack variables.
* This defines sources, sinks, and any other configurable aspect of the
* analysis. Multiple analyses can coexist. To create an analysis, extend this
* class with a subclass whose characteristic predicate is a unique singleton
* string. For example, write
*
* ```
* class MyAnalysisConfiguration extends StackVariableReachability {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
* // Override `isBarrier`.
* }
* ```
*
* Then, to query whether there is flow between some source and sink, call the
* `reaches` predicate on an instance of `MyAnalysisConfiguration`.
*/ */
abstract deprecated class StackVariableReachability extends string { abstract class StackVariableReachability extends string {
bindingset[this] bindingset[this]
StackVariableReachability() { length() >= 0 } StackVariableReachability() { length() >= 0 }
@@ -24,13 +37,13 @@ abstract deprecated class StackVariableReachability extends string {
* uses basic blocks internally for better performance: * uses basic blocks internally for better performance:
* *
* ``` * ```
* predicate reaches(ControlFlowNode source, StackVariable v, ControlFlowNode sink) { * predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
* reachesImpl(source, v, sink) * reachesImpl(source, v, sink)
* and * and
* isSink(sink, v) * isSink(sink, v)
* } * }
* *
* predicate reachesImpl(ControlFlowNode source, StackVariable v, ControlFlowNode sink) { * predicate reachesImpl(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
* sink = source.getASuccessor() and isSource(source, v) * sink = source.getASuccessor() and isSource(source, v)
* or * or
* exists(ControlFlowNode mid | reachesImpl(source, v, mid) | * exists(ControlFlowNode mid | reachesImpl(source, v, mid) |
@@ -44,7 +57,7 @@ abstract deprecated class StackVariableReachability extends string {
* In addition to using a better performing implementation, this analysis * In addition to using a better performing implementation, this analysis
* accounts for loops where the condition is provably true upon entry. * accounts for loops where the condition is provably true upon entry.
*/ */
predicate reaches(ControlFlowNode source, StackVariable v, ControlFlowNode sink) { predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
/* /*
* Implementation detail: the predicates in this class are a generalization of * Implementation detail: the predicates in this class are a generalization of
* those in DefinitionsAndUses.qll, and should be kept in sync. * those in DefinitionsAndUses.qll, and should be kept in sync.
@@ -71,7 +84,8 @@ abstract deprecated class StackVariableReachability extends string {
} }
private predicate bbSuccessorEntryReaches( private predicate bbSuccessorEntryReaches(
BasicBlock bb, StackVariable v, ControlFlowNode node, boolean skipsFirstLoopAlwaysTrueUponEntry BasicBlock bb, SemanticStackVariable v, ControlFlowNode node,
boolean skipsFirstLoopAlwaysTrueUponEntry
) { ) {
exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry | exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry |
bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry, bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry,
@@ -85,11 +99,22 @@ abstract deprecated class StackVariableReachability extends string {
) )
} }
private predicate bbEntryReachesLocally(BasicBlock bb, StackVariable v, ControlFlowNode node) { private predicate bbEntryReachesLocally(
exists(int n | node = bb.getNode(n) and isSink(node, v) | BasicBlock bb, SemanticStackVariable v, ControlFlowNode node
not exists(int m | m < n | isBarrier(bb.getNode(m), v)) ) {
exists(int n |
node = bb.getNode(n) and
isSink(node, v)
|
not exists(this.firstBarrierIndexIn(bb, v))
or
n <= this.firstBarrierIndexIn(bb, v)
) )
} }
private int firstBarrierIndexIn(BasicBlock bb, SemanticStackVariable v) {
result = min(int m | isBarrier(bb.getNode(m), v))
}
} }
/** /**
@@ -113,26 +138,32 @@ private predicate bbLoopEntryConditionAlwaysTrueAt(BasicBlock bb, int i, Control
} }
/** /**
* Basic block `pred` ends with a condition belonging to a loop, and that * Basic block `pred` contains all or part of the condition belonging to a loop,
* condition is provably true upon entry. Basic block `succ` is a successor * and there is an edge from `pred` to `succ` that concludes the condition.
* of `pred`, and `skipsLoop` indicates whether `succ` is the false-successor * If the edge corrseponds with the loop condition being found to be `true`, then
* of `pred`. * `skipsLoop` is `false`. Otherwise the edge corresponds with the loop condition
* being found to be `false` and `skipsLoop` is `true`. Non-concluding edges
* within a complex loop condition are not matched by this predicate.
*/ */
private predicate bbLoopConditionAlwaysTrueUponEntrySuccessor( private predicate bbLoopConditionAlwaysTrueUponEntrySuccessor(
BasicBlock pred, BasicBlock succ, boolean skipsLoop BasicBlock pred, BasicBlock succ, boolean skipsLoop
) { ) {
succ = pred.getASuccessor() and exists(Expr cond |
exists(ControlFlowNode last | loopConditionAlwaysTrueUponEntry(_, cond) and
last = pred.getEnd() and cond.getAChild*() = pred.getEnd() and
loopConditionAlwaysTrueUponEntry(_, last) and succ = pred.getASuccessor() and
if succ = pred.getAFalseSuccessor() then skipsLoop = true else skipsLoop = false not cond.getAChild*() = succ.getStart() and
(
succ = pred.getAFalseSuccessor() and
skipsLoop = true
or
succ = pred.getATrueSuccessor() and
skipsLoop = false
)
) )
} }
/** /**
* DEPRECATED: use the corresponding predicate in
* `LocalScopeVariableReachability` instead.
*
* Loop invariant for `bbSuccessorEntryReaches`: * Loop invariant for `bbSuccessorEntryReaches`:
* *
* - `succ` is a successor of `pred`. * - `succ` is a successor of `pred`.
@@ -146,7 +177,7 @@ private predicate bbLoopConditionAlwaysTrueUponEntrySuccessor(
* is provably true upon entry, then `succ` is not allowed to skip * is provably true upon entry, then `succ` is not allowed to skip
* that loop (`succSkipsFirstLoopAlwaysTrueUponEntry = false`). * that loop (`succSkipsFirstLoopAlwaysTrueUponEntry = false`).
*/ */
deprecated predicate bbSuccessorEntryReachesLoopInvariant( predicate bbSuccessorEntryReachesLoopInvariant(
BasicBlock pred, BasicBlock succ, boolean predSkipsFirstLoopAlwaysTrueUponEntry, BasicBlock pred, BasicBlock succ, boolean predSkipsFirstLoopAlwaysTrueUponEntry,
boolean succSkipsFirstLoopAlwaysTrueUponEntry boolean succSkipsFirstLoopAlwaysTrueUponEntry
) { ) {
@@ -162,7 +193,7 @@ deprecated predicate bbSuccessorEntryReachesLoopInvariant(
// The edge from `pred` to `succ` is _not_ from a loop condition provably // The edge from `pred` to `succ` is _not_ from a loop condition provably
// true upon entry, so the values of `predSkipsFirstLoopAlwaysTrueUponEntry` // true upon entry, so the values of `predSkipsFirstLoopAlwaysTrueUponEntry`
// and `succSkipsFirstLoopAlwaysTrueUponEntry` must be the same. // and `succSkipsFirstLoopAlwaysTrueUponEntry` must be the same.
not bbLoopConditionAlwaysTrueUponEntrySuccessor(pred, _, _) and not bbLoopConditionAlwaysTrueUponEntrySuccessor(pred, succ, _) and
succSkipsFirstLoopAlwaysTrueUponEntry = predSkipsFirstLoopAlwaysTrueUponEntry and succSkipsFirstLoopAlwaysTrueUponEntry = predSkipsFirstLoopAlwaysTrueUponEntry and
// Moreover, if `pred` contains the entry point of a loop where the // Moreover, if `pred` contains the entry point of a loop where the
// condition is provably true upon entry, then `succ` is not allowed // condition is provably true upon entry, then `succ` is not allowed
@@ -176,13 +207,16 @@ deprecated predicate bbSuccessorEntryReachesLoopInvariant(
} }
/** /**
* DEPRECATED: use `LocalScopeVariableReachabilityWithReassignment` instead.
*
* Reachability analysis for control-flow nodes involving stack variables. * Reachability analysis for control-flow nodes involving stack variables.
* Unlike `StackVariableReachability`, this analysis takes variable * Unlike `StackVariableReachability`, this analysis takes variable
* reassignments into account. * reassignments into account.
*
* This class is used like `StackVariableReachability`, except that
* subclasses should override `isSourceActual` and `isSinkActual` instead of
* `isSource` and `isSink`, and that there is a `reachesTo` predicate in
* addition to `reaches`.
*/ */
abstract deprecated class StackVariableReachabilityWithReassignment extends StackVariableReachability { abstract class StackVariableReachabilityWithReassignment extends StackVariableReachability {
bindingset[this] bindingset[this]
StackVariableReachabilityWithReassignment() { length() >= 0 } StackVariableReachabilityWithReassignment() { length() >= 0 }
@@ -199,19 +233,19 @@ abstract deprecated class StackVariableReachabilityWithReassignment extends Stac
* performance: * performance:
* *
* ``` * ```
* predicate reaches(ControlFlowNode source, StackVariable v, ControlFlowNode sink) { * predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
* reachesImpl(source, v, sink) * reachesImpl(source, v, sink)
* and * and
* isSinkActual(sink, v) * isSinkActual(sink, v)
* } * }
* *
* predicate reachesImpl(ControlFlowNode source, StackVariable v, ControlFlowNode sink) { * predicate reachesImpl(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
* isSourceActual(source, v) * isSourceActual(source, v)
* and * and
* ( * (
* sink = source.getASuccessor() * sink = source.getASuccessor()
* or * or
* exists(ControlFlowNode mid, StackVariable v0 | reachesImpl(source, v0, mid) | * exists(ControlFlowNode mid, SemanticStackVariable v0 | reachesImpl(source, v0, mid) |
* // ordinary successor * // ordinary successor
* not isBarrier(mid, v) and * not isBarrier(mid, v) and
* sink = mid.getASuccessor() and * sink = mid.getASuccessor() and
@@ -228,7 +262,7 @@ abstract deprecated class StackVariableReachabilityWithReassignment extends Stac
* In addition to using a better performing implementation, this analysis * In addition to using a better performing implementation, this analysis
* accounts for loops where the condition is provably true upon entry. * accounts for loops where the condition is provably true upon entry.
*/ */
override predicate reaches(ControlFlowNode source, StackVariable v, ControlFlowNode sink) { override predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
reachesTo(source, v, sink, _) reachesTo(source, v, sink, _)
} }
@@ -236,7 +270,7 @@ abstract deprecated class StackVariableReachabilityWithReassignment extends Stac
* As `reaches`, but also specifies the last variable it was reassigned to (`v0`). * As `reaches`, but also specifies the last variable it was reassigned to (`v0`).
*/ */
predicate reachesTo( predicate reachesTo(
ControlFlowNode source, StackVariable v, ControlFlowNode sink, StackVariable v0 ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink, SemanticStackVariable v0
) { ) {
exists(ControlFlowNode def | exists(ControlFlowNode def |
actualSourceReaches(source, v, def, v0) and actualSourceReaches(source, v, def, v0) and
@@ -246,17 +280,19 @@ abstract deprecated class StackVariableReachabilityWithReassignment extends Stac
} }
private predicate actualSourceReaches( private predicate actualSourceReaches(
ControlFlowNode source, StackVariable v, ControlFlowNode def, StackVariable v0 ControlFlowNode source, SemanticStackVariable v, ControlFlowNode def, SemanticStackVariable v0
) { ) {
isSourceActual(source, v) and def = source and v0 = v isSourceActual(source, v) and def = source and v0 = v
or or
exists(ControlFlowNode source1, StackVariable v1 | actualSourceReaches(source, v, source1, v1) | exists(ControlFlowNode source1, SemanticStackVariable v1 |
actualSourceReaches(source, v, source1, v1)
|
reassignment(source1, v1, def, v0) reassignment(source1, v1, def, v0)
) )
} }
private predicate reassignment( private predicate reassignment(
ControlFlowNode source, StackVariable v, ControlFlowNode def, StackVariable v0 ControlFlowNode source, SemanticStackVariable v, ControlFlowNode def, SemanticStackVariable v0
) { ) {
StackVariableReachability.super.reaches(source, v, def) and StackVariableReachability.super.reaches(source, v, def) and
exprDefinition(v0, def, v.getAnAccess()) exprDefinition(v0, def, v.getAnAccess())
@@ -278,13 +314,12 @@ abstract deprecated class StackVariableReachabilityWithReassignment extends Stac
} }
/** /**
* DEPRECATED: use `LocalScopeVariableReachabilityExt` instead.
*
* Same as `StackVariableReachability`, but `isBarrier` works on control-flow * Same as `StackVariableReachability`, but `isBarrier` works on control-flow
* edges rather than nodes and is therefore parameterized by the original * edges rather than nodes and is therefore parameterized by the original
* source node as well. * source node as well. Otherwise, this class is used like
* `StackVariableReachability`.
*/ */
abstract deprecated class StackVariableReachabilityExt extends string { abstract class StackVariableReachabilityExt extends string {
bindingset[this] bindingset[this]
StackVariableReachabilityExt() { length() >= 0 } StackVariableReachabilityExt() { length() >= 0 }
@@ -300,7 +335,7 @@ abstract deprecated class StackVariableReachabilityExt extends string {
); );
/** See `StackVariableReachability.reaches`. */ /** See `StackVariableReachability.reaches`. */
predicate reaches(ControlFlowNode source, StackVariable v, ControlFlowNode sink) { predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
exists(BasicBlock bb, int i | exists(BasicBlock bb, int i |
isSource(source, v) and isSource(source, v) and
bb.getNode(i) = source and bb.getNode(i) = source and
@@ -321,7 +356,7 @@ abstract deprecated class StackVariableReachabilityExt extends string {
} }
private predicate bbSuccessorEntryReaches( private predicate bbSuccessorEntryReaches(
ControlFlowNode source, BasicBlock bb, StackVariable v, ControlFlowNode node, ControlFlowNode source, BasicBlock bb, SemanticStackVariable v, ControlFlowNode node,
boolean skipsFirstLoopAlwaysTrueUponEntry boolean skipsFirstLoopAlwaysTrueUponEntry
) { ) {
exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry | exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry |
@@ -338,7 +373,7 @@ abstract deprecated class StackVariableReachabilityExt extends string {
} }
private predicate bbEntryReachesLocally( private predicate bbEntryReachesLocally(
ControlFlowNode source, BasicBlock bb, StackVariable v, ControlFlowNode node ControlFlowNode source, BasicBlock bb, SemanticStackVariable v, ControlFlowNode node
) { ) {
isSource(source, v) and isSource(source, v) and
exists(int n | node = bb.getNode(n) and isSink(node, v) | exists(int n | node = bb.getNode(n) and isSink(node, v) |

View File

@@ -924,7 +924,7 @@ library class LoopEntryConditionEvaluator extends ExprEvaluator {
/* /*
* Use primitive basic blocks in reachability analysis for better performance. * Use primitive basic blocks in reachability analysis for better performance.
* This is similar to the pattern used in e.g. `DefinitionsAndUses` and * This is similar to the pattern used in e.g. `DefinitionsAndUses` and
* `LocalScopeVariableReachability`. * `StackVariableReachability`.
*/ */
exists(PrimitiveBasicBlock bb1, int pos1 | bb1.getNode(pos1) = valueOrDef | exists(PrimitiveBasicBlock bb1, int pos1 | bb1.getNode(pos1) = valueOrDef |

View File

@@ -61,7 +61,7 @@ predicate stackPointerFlowsToUse(Expr use, Type useType, Expr source, boolean is
stackPointerFlowsToUse(use.(PointerAddExpr).getAnOperand(), useType, source, isLocal) stackPointerFlowsToUse(use.(PointerAddExpr).getAnOperand(), useType, source, isLocal)
or or
// Indirect use of a stack address. // Indirect use of a stack address.
exists(SsaDefinition def, LocalScopeVariable var | exists(SsaDefinition def, StackVariable var |
stackPointerFlowsToDef(def, var, useType, source, isLocal) and stackPointerFlowsToDef(def, var, useType, source, isLocal) and
use = def.getAUse(var) use = def.getAUse(var)
) )
@@ -97,8 +97,7 @@ private PointerType getExprPtrType(Expr use) { result = use.getUnspecifiedType()
predicate stackReferenceFlowsToUse(Expr use, Type useType, Expr source, boolean isLocal) { predicate stackReferenceFlowsToUse(Expr use, Type useType, Expr source, boolean isLocal) {
// Stack variables // Stack variables
exists(LocalScopeVariable var | exists(StackVariable var |
not var.isStatic() and
use = source and use = source and
source = var.getAnAccess() and source = var.getAnAccess() and
isLocal = true and isLocal = true and
@@ -140,7 +139,7 @@ predicate stackReferenceFlowsToUse(Expr use, Type useType, Expr source, boolean
stackPointerFlowsToUse(use.(PointerDereferenceExpr).getOperand(), useType, source, isLocal) stackPointerFlowsToUse(use.(PointerDereferenceExpr).getOperand(), useType, source, isLocal)
or or
// Indirect use of a stack reference, via a reference variable. // Indirect use of a stack reference, via a reference variable.
exists(SsaDefinition def, LocalScopeVariable var | exists(SsaDefinition def, StackVariable var |
stackReferenceFlowsToDef(def, var, useType, source, isLocal) and stackReferenceFlowsToDef(def, var, useType, source, isLocal) and
use = def.getAUse(var) use = def.getAUse(var)
) )
@@ -162,7 +161,7 @@ predicate stackReferenceFlowsToUse(Expr use, Type useType, Expr source, boolean
* addresses through SSA definitions. * addresses through SSA definitions.
*/ */
predicate stackPointerFlowsToDef( predicate stackPointerFlowsToDef(
SsaDefinition def, LocalScopeVariable var, Type useType, Expr source, boolean isLocal SsaDefinition def, StackVariable var, Type useType, Expr source, boolean isLocal
) { ) {
stackPointerFlowsToUse(def.getDefiningValue(var), useType, source, isLocal) stackPointerFlowsToUse(def.getDefiningValue(var), useType, source, isLocal)
or or
@@ -184,7 +183,7 @@ predicate stackPointerFlowsToDef(
* int&, rather than pointers. * int&, rather than pointers.
*/ */
predicate stackReferenceFlowsToDef( predicate stackReferenceFlowsToDef(
SsaDefinition def, LocalScopeVariable var, Type useType, Expr source, boolean isLocal SsaDefinition def, StackVariable var, Type useType, Expr source, boolean isLocal
) { ) {
// Check that the type of the variable is a reference type and delegate // Check that the type of the variable is a reference type and delegate
// the rest of the work to stackReferenceFlowsToDef_Impl. // the rest of the work to stackReferenceFlowsToDef_Impl.
@@ -197,7 +196,7 @@ predicate stackReferenceFlowsToDef(
* predicate. * predicate.
*/ */
predicate stackReferenceFlowsToDef_Impl( predicate stackReferenceFlowsToDef_Impl(
SsaDefinition def, LocalScopeVariable var, Type useType, Expr source, boolean isLocal SsaDefinition def, StackVariable var, Type useType, Expr source, boolean isLocal
) { ) {
stackReferenceFlowsToUse(def.getDefiningValue(var), useType, source, isLocal) stackReferenceFlowsToUse(def.getDefiningValue(var), useType, source, isLocal)
or or
@@ -213,7 +212,7 @@ predicate stackReferenceFlowsToDef_Impl(
} }
/** The type of the variable is a reference type, such as int&. */ /** The type of the variable is a reference type, such as int&. */
predicate isReferenceVariable(LocalScopeVariable var) { predicate isReferenceVariable(StackVariable var) {
var.getUnspecifiedType() instanceof ReferenceType var.getUnspecifiedType() instanceof ReferenceType
} }
@@ -284,7 +283,7 @@ predicate memberFcnMightRunOnStack(MemberFunction fcn, Type useType) {
predicate constructorMightRunOnStack(Constructor constructor) { predicate constructorMightRunOnStack(Constructor constructor) {
exists(ConstructorCall call | call.getTarget() = constructor | exists(ConstructorCall call | call.getTarget() = constructor |
// Call to a constructor from a stack variable's initializer. // Call to a constructor from a stack variable's initializer.
exists(LocalScopeVariable var | var.getInitializer().getExpr() = call) exists(StackVariable var | var.getInitializer().getExpr() = call)
or or
// Call to a constructor from another constructor which might // Call to a constructor from another constructor which might
// also run on the stack. // also run on the stack.

View File

@@ -510,13 +510,20 @@ private predicate simpleParameterFlow(
pragma[noinline] pragma[noinline]
private predicate simpleArgumentFlowsThrough0( private predicate simpleArgumentFlowsThrough0(
ParameterNode p, ReturnNode ret, ReturnKind kind, DataFlowType t, Configuration config
) {
simpleParameterFlow(p, ret, t, config) and
kind = ret.getKind()
}
pragma[noinline]
private predicate simpleArgumentFlowsThrough1(
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
) { ) {
nodeCand1(arg, unbind(config)) and nodeCand1(arg, unbind(config)) and
not outBarrier(arg, config) and not outBarrier(arg, config) and
exists(ParameterNode p, ReturnNode ret | exists(ParameterNode p, ReturnNode ret |
simpleParameterFlow(p, ret, t, config) and simpleArgumentFlowsThrough0(p, ret, kind, t, config) and
kind = ret.getKind() and
viableParamArg(call, p, arg) viableParamArg(call, p, arg)
) )
} }
@@ -534,7 +541,7 @@ private predicate simpleArgumentFlowsThrough(
exists(DataFlowCall call, ReturnKind kind | exists(DataFlowCall call, ReturnKind kind |
nodeCand1(out, unbind(config)) and nodeCand1(out, unbind(config)) and
not inBarrier(out, config) and not inBarrier(out, config) and
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and simpleArgumentFlowsThrough1(call, arg, kind, t, config) and
out = getAnOutNode(call, kind) out = getAnOutNode(call, kind)
) )
} }

View File

@@ -510,13 +510,20 @@ private predicate simpleParameterFlow(
pragma[noinline] pragma[noinline]
private predicate simpleArgumentFlowsThrough0( private predicate simpleArgumentFlowsThrough0(
ParameterNode p, ReturnNode ret, ReturnKind kind, DataFlowType t, Configuration config
) {
simpleParameterFlow(p, ret, t, config) and
kind = ret.getKind()
}
pragma[noinline]
private predicate simpleArgumentFlowsThrough1(
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
) { ) {
nodeCand1(arg, unbind(config)) and nodeCand1(arg, unbind(config)) and
not outBarrier(arg, config) and not outBarrier(arg, config) and
exists(ParameterNode p, ReturnNode ret | exists(ParameterNode p, ReturnNode ret |
simpleParameterFlow(p, ret, t, config) and simpleArgumentFlowsThrough0(p, ret, kind, t, config) and
kind = ret.getKind() and
viableParamArg(call, p, arg) viableParamArg(call, p, arg)
) )
} }
@@ -534,7 +541,7 @@ private predicate simpleArgumentFlowsThrough(
exists(DataFlowCall call, ReturnKind kind | exists(DataFlowCall call, ReturnKind kind |
nodeCand1(out, unbind(config)) and nodeCand1(out, unbind(config)) and
not inBarrier(out, config) and not inBarrier(out, config) and
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and simpleArgumentFlowsThrough1(call, arg, kind, t, config) and
out = getAnOutNode(call, kind) out = getAnOutNode(call, kind)
) )
} }

View File

@@ -510,13 +510,20 @@ private predicate simpleParameterFlow(
pragma[noinline] pragma[noinline]
private predicate simpleArgumentFlowsThrough0( private predicate simpleArgumentFlowsThrough0(
ParameterNode p, ReturnNode ret, ReturnKind kind, DataFlowType t, Configuration config
) {
simpleParameterFlow(p, ret, t, config) and
kind = ret.getKind()
}
pragma[noinline]
private predicate simpleArgumentFlowsThrough1(
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
) { ) {
nodeCand1(arg, unbind(config)) and nodeCand1(arg, unbind(config)) and
not outBarrier(arg, config) and not outBarrier(arg, config) and
exists(ParameterNode p, ReturnNode ret | exists(ParameterNode p, ReturnNode ret |
simpleParameterFlow(p, ret, t, config) and simpleArgumentFlowsThrough0(p, ret, kind, t, config) and
kind = ret.getKind() and
viableParamArg(call, p, arg) viableParamArg(call, p, arg)
) )
} }
@@ -534,7 +541,7 @@ private predicate simpleArgumentFlowsThrough(
exists(DataFlowCall call, ReturnKind kind | exists(DataFlowCall call, ReturnKind kind |
nodeCand1(out, unbind(config)) and nodeCand1(out, unbind(config)) and
not inBarrier(out, config) and not inBarrier(out, config) and
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and simpleArgumentFlowsThrough1(call, arg, kind, t, config) and
out = getAnOutNode(call, kind) out = getAnOutNode(call, kind)
) )
} }

View File

@@ -510,13 +510,20 @@ private predicate simpleParameterFlow(
pragma[noinline] pragma[noinline]
private predicate simpleArgumentFlowsThrough0( private predicate simpleArgumentFlowsThrough0(
ParameterNode p, ReturnNode ret, ReturnKind kind, DataFlowType t, Configuration config
) {
simpleParameterFlow(p, ret, t, config) and
kind = ret.getKind()
}
pragma[noinline]
private predicate simpleArgumentFlowsThrough1(
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
) { ) {
nodeCand1(arg, unbind(config)) and nodeCand1(arg, unbind(config)) and
not outBarrier(arg, config) and not outBarrier(arg, config) and
exists(ParameterNode p, ReturnNode ret | exists(ParameterNode p, ReturnNode ret |
simpleParameterFlow(p, ret, t, config) and simpleArgumentFlowsThrough0(p, ret, kind, t, config) and
kind = ret.getKind() and
viableParamArg(call, p, arg) viableParamArg(call, p, arg)
) )
} }
@@ -534,7 +541,7 @@ private predicate simpleArgumentFlowsThrough(
exists(DataFlowCall call, ReturnKind kind | exists(DataFlowCall call, ReturnKind kind |
nodeCand1(out, unbind(config)) and nodeCand1(out, unbind(config)) and
not inBarrier(out, config) and not inBarrier(out, config) and
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and simpleArgumentFlowsThrough1(call, arg, kind, t, config) and
out = getAnOutNode(call, kind) out = getAnOutNode(call, kind)
) )
} }

View File

@@ -360,7 +360,7 @@ private module ImplCommon {
*/ */
cached cached
predicate read(Node node1, Content f, Node node2) { predicate read(Node node1, Content f, Node node2) {
readStep(node1, f, node2) and storeStep(_, f, _) readStep(node1, f, node2)
or or
exists(DataFlowCall call, ReturnKind kind | exists(DataFlowCall call, ReturnKind kind |
read0(call, kind, node1, f) and read0(call, kind, node1, f) and

View File

@@ -510,13 +510,20 @@ private predicate simpleParameterFlow(
pragma[noinline] pragma[noinline]
private predicate simpleArgumentFlowsThrough0( private predicate simpleArgumentFlowsThrough0(
ParameterNode p, ReturnNode ret, ReturnKind kind, DataFlowType t, Configuration config
) {
simpleParameterFlow(p, ret, t, config) and
kind = ret.getKind()
}
pragma[noinline]
private predicate simpleArgumentFlowsThrough1(
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
) { ) {
nodeCand1(arg, unbind(config)) and nodeCand1(arg, unbind(config)) and
not outBarrier(arg, config) and not outBarrier(arg, config) and
exists(ParameterNode p, ReturnNode ret | exists(ParameterNode p, ReturnNode ret |
simpleParameterFlow(p, ret, t, config) and simpleArgumentFlowsThrough0(p, ret, kind, t, config) and
kind = ret.getKind() and
viableParamArg(call, p, arg) viableParamArg(call, p, arg)
) )
} }
@@ -534,7 +541,7 @@ private predicate simpleArgumentFlowsThrough(
exists(DataFlowCall call, ReturnKind kind | exists(DataFlowCall call, ReturnKind kind |
nodeCand1(out, unbind(config)) and nodeCand1(out, unbind(config)) and
not inBarrier(out, config) and not inBarrier(out, config) and
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and simpleArgumentFlowsThrough1(call, arg, kind, t, config) and
out = getAnOutNode(call, kind) out = getAnOutNode(call, kind)
) )
} }

View File

@@ -88,7 +88,7 @@ class FlowVar extends TFlowVar {
* `FlowVar` instance for the uninitialized value of that variable. * `FlowVar` instance for the uninitialized value of that variable.
*/ */
cached cached
abstract predicate definedByInitialValue(LocalScopeVariable v); abstract predicate definedByInitialValue(StackVariable v);
/** Gets a textual representation of this element. */ /** Gets a textual representation of this element. */
cached cached
@@ -268,7 +268,7 @@ module FlowVar_internal {
* Holds if `sbb` is the `SubBasicBlock` where `v` receives its initial value. * Holds if `sbb` is the `SubBasicBlock` where `v` receives its initial value.
* See the documentation for `FlowVar.definedByInitialValue`. * See the documentation for `FlowVar.definedByInitialValue`.
*/ */
predicate blockVarDefinedByVariable(SubBasicBlock sbb, LocalScopeVariable v) { predicate blockVarDefinedByVariable(SubBasicBlock sbb, StackVariable v) {
sbb = v.(Parameter).getFunction().getEntryPoint() sbb = v.(Parameter).getFunction().getEntryPoint()
or or
exists(DeclStmt declStmt | exists(DeclStmt declStmt |
@@ -279,7 +279,7 @@ module FlowVar_internal {
} }
newtype TFlowVar = newtype TFlowVar =
TSsaVar(SsaDefinition def, LocalScopeVariable v) { TSsaVar(SsaDefinition def, StackVariable v) {
fullySupportedSsaVariable(v) and fullySupportedSsaVariable(v) and
v = def.getAVariable() v = def.getAVariable()
} or } or
@@ -303,7 +303,7 @@ module FlowVar_internal {
*/ */
class SsaVar extends TSsaVar, FlowVar { class SsaVar extends TSsaVar, FlowVar {
SsaDefinition def; SsaDefinition def;
LocalScopeVariable v; StackVariable v;
SsaVar() { this = TSsaVar(def, v) } SsaVar() { this = TSsaVar(def, v) }
@@ -343,7 +343,7 @@ module FlowVar_internal {
override predicate definedPartiallyAt(Expr e) { none() } override predicate definedPartiallyAt(Expr e) { none() }
override predicate definedByInitialValue(LocalScopeVariable param) { override predicate definedByInitialValue(StackVariable param) {
def.definedByParameter(param) and def.definedByParameter(param) and
param = v param = v
} }
@@ -407,7 +407,7 @@ module FlowVar_internal {
getAReachedBlockVarSBB(this).getANode() = p.getFunction() getAReachedBlockVarSBB(this).getANode() = p.getFunction()
} }
override predicate definedByInitialValue(LocalScopeVariable lsv) { override predicate definedByInitialValue(StackVariable lsv) {
blockVarDefinedByVariable(sbb, lsv) and blockVarDefinedByVariable(sbb, lsv) and
lsv = v lsv = v
} }
@@ -647,11 +647,8 @@ module FlowVar_internal {
/** /**
* A local variable that is uninitialized immediately after its declaration. * A local variable that is uninitialized immediately after its declaration.
*/ */
class UninitializedLocalVariable extends LocalVariable { class UninitializedLocalVariable extends LocalVariable, StackVariable {
UninitializedLocalVariable() { UninitializedLocalVariable() { not this.hasInitializer() }
not this.hasInitializer() and
not this.isStatic()
}
} }
/** Holds if `va` may be an uninitialized access to `v`. */ /** Holds if `va` may be an uninitialized access to `v`. */

View File

@@ -62,11 +62,10 @@ abstract class CrementOperation extends UnaryArithmeticOperation {
override predicate mayBeImpure() { any() } override predicate mayBeImpure() { any() }
override predicate mayBeGloballyImpure() { override predicate mayBeGloballyImpure() {
not exists(VariableAccess va, LocalScopeVariable v | not exists(VariableAccess va, StackVariable v |
va = this.getOperand() and va = this.getOperand() and
v = va.getTarget() and v = va.getTarget() and
not va.getConversion+() instanceof ReferenceDereferenceExpr and not va.getConversion+() instanceof ReferenceDereferenceExpr
not v.isStatic()
) )
} }
} }

View File

@@ -21,11 +21,10 @@ abstract class Assignment extends Operation {
override predicate mayBeGloballyImpure() { override predicate mayBeGloballyImpure() {
this.getRValue().mayBeGloballyImpure() this.getRValue().mayBeGloballyImpure()
or or
not exists(VariableAccess va, LocalScopeVariable v | not exists(VariableAccess va, StackVariable v |
va = this.getLValue() and va = this.getLValue() and
v = va.getTarget() and v = va.getTarget() and
not va.getConversion+() instanceof ReferenceDereferenceExpr and not va.getConversion+() instanceof ReferenceDereferenceExpr
not v.isStatic()
) )
} }
} }

View File

@@ -510,13 +510,20 @@ private predicate simpleParameterFlow(
pragma[noinline] pragma[noinline]
private predicate simpleArgumentFlowsThrough0( private predicate simpleArgumentFlowsThrough0(
ParameterNode p, ReturnNode ret, ReturnKind kind, DataFlowType t, Configuration config
) {
simpleParameterFlow(p, ret, t, config) and
kind = ret.getKind()
}
pragma[noinline]
private predicate simpleArgumentFlowsThrough1(
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
) { ) {
nodeCand1(arg, unbind(config)) and nodeCand1(arg, unbind(config)) and
not outBarrier(arg, config) and not outBarrier(arg, config) and
exists(ParameterNode p, ReturnNode ret | exists(ParameterNode p, ReturnNode ret |
simpleParameterFlow(p, ret, t, config) and simpleArgumentFlowsThrough0(p, ret, kind, t, config) and
kind = ret.getKind() and
viableParamArg(call, p, arg) viableParamArg(call, p, arg)
) )
} }
@@ -534,7 +541,7 @@ private predicate simpleArgumentFlowsThrough(
exists(DataFlowCall call, ReturnKind kind | exists(DataFlowCall call, ReturnKind kind |
nodeCand1(out, unbind(config)) and nodeCand1(out, unbind(config)) and
not inBarrier(out, config) and not inBarrier(out, config) and
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and simpleArgumentFlowsThrough1(call, arg, kind, t, config) and
out = getAnOutNode(call, kind) out = getAnOutNode(call, kind)
) )
} }

View File

@@ -510,13 +510,20 @@ private predicate simpleParameterFlow(
pragma[noinline] pragma[noinline]
private predicate simpleArgumentFlowsThrough0( private predicate simpleArgumentFlowsThrough0(
ParameterNode p, ReturnNode ret, ReturnKind kind, DataFlowType t, Configuration config
) {
simpleParameterFlow(p, ret, t, config) and
kind = ret.getKind()
}
pragma[noinline]
private predicate simpleArgumentFlowsThrough1(
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
) { ) {
nodeCand1(arg, unbind(config)) and nodeCand1(arg, unbind(config)) and
not outBarrier(arg, config) and not outBarrier(arg, config) and
exists(ParameterNode p, ReturnNode ret | exists(ParameterNode p, ReturnNode ret |
simpleParameterFlow(p, ret, t, config) and simpleArgumentFlowsThrough0(p, ret, kind, t, config) and
kind = ret.getKind() and
viableParamArg(call, p, arg) viableParamArg(call, p, arg)
) )
} }
@@ -534,7 +541,7 @@ private predicate simpleArgumentFlowsThrough(
exists(DataFlowCall call, ReturnKind kind | exists(DataFlowCall call, ReturnKind kind |
nodeCand1(out, unbind(config)) and nodeCand1(out, unbind(config)) and
not inBarrier(out, config) and not inBarrier(out, config) and
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and simpleArgumentFlowsThrough1(call, arg, kind, t, config) and
out = getAnOutNode(call, kind) out = getAnOutNode(call, kind)
) )
} }

View File

@@ -510,13 +510,20 @@ private predicate simpleParameterFlow(
pragma[noinline] pragma[noinline]
private predicate simpleArgumentFlowsThrough0( private predicate simpleArgumentFlowsThrough0(
ParameterNode p, ReturnNode ret, ReturnKind kind, DataFlowType t, Configuration config
) {
simpleParameterFlow(p, ret, t, config) and
kind = ret.getKind()
}
pragma[noinline]
private predicate simpleArgumentFlowsThrough1(
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
) { ) {
nodeCand1(arg, unbind(config)) and nodeCand1(arg, unbind(config)) and
not outBarrier(arg, config) and not outBarrier(arg, config) and
exists(ParameterNode p, ReturnNode ret | exists(ParameterNode p, ReturnNode ret |
simpleParameterFlow(p, ret, t, config) and simpleArgumentFlowsThrough0(p, ret, kind, t, config) and
kind = ret.getKind() and
viableParamArg(call, p, arg) viableParamArg(call, p, arg)
) )
} }
@@ -534,7 +541,7 @@ private predicate simpleArgumentFlowsThrough(
exists(DataFlowCall call, ReturnKind kind | exists(DataFlowCall call, ReturnKind kind |
nodeCand1(out, unbind(config)) and nodeCand1(out, unbind(config)) and
not inBarrier(out, config) and not inBarrier(out, config) and
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and simpleArgumentFlowsThrough1(call, arg, kind, t, config) and
out = getAnOutNode(call, kind) out = getAnOutNode(call, kind)
) )
} }

View File

@@ -510,13 +510,20 @@ private predicate simpleParameterFlow(
pragma[noinline] pragma[noinline]
private predicate simpleArgumentFlowsThrough0( private predicate simpleArgumentFlowsThrough0(
ParameterNode p, ReturnNode ret, ReturnKind kind, DataFlowType t, Configuration config
) {
simpleParameterFlow(p, ret, t, config) and
kind = ret.getKind()
}
pragma[noinline]
private predicate simpleArgumentFlowsThrough1(
DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config DataFlowCall call, ArgumentNode arg, ReturnKind kind, DataFlowType t, Configuration config
) { ) {
nodeCand1(arg, unbind(config)) and nodeCand1(arg, unbind(config)) and
not outBarrier(arg, config) and not outBarrier(arg, config) and
exists(ParameterNode p, ReturnNode ret | exists(ParameterNode p, ReturnNode ret |
simpleParameterFlow(p, ret, t, config) and simpleArgumentFlowsThrough0(p, ret, kind, t, config) and
kind = ret.getKind() and
viableParamArg(call, p, arg) viableParamArg(call, p, arg)
) )
} }
@@ -534,7 +541,7 @@ private predicate simpleArgumentFlowsThrough(
exists(DataFlowCall call, ReturnKind kind | exists(DataFlowCall call, ReturnKind kind |
nodeCand1(out, unbind(config)) and nodeCand1(out, unbind(config)) and
not inBarrier(out, config) and not inBarrier(out, config) and
simpleArgumentFlowsThrough0(call, arg, kind, t, config) and simpleArgumentFlowsThrough1(call, arg, kind, t, config) and
out = getAnOutNode(call, kind) out = getAnOutNode(call, kind)
) )
} }

View File

@@ -360,7 +360,7 @@ private module ImplCommon {
*/ */
cached cached
predicate read(Node node1, Content f, Node node2) { predicate read(Node node1, Content f, Node node2) {
readStep(node1, f, node2) and storeStep(_, f, _) readStep(node1, f, node2)
or or
exists(DataFlowCall call, ReturnKind kind | exists(DataFlowCall call, ReturnKind kind |
read0(call, kind, node1, f) and read0(call, kind, node1, f) and

View File

@@ -359,24 +359,25 @@ class Instruction extends Construction::TInstruction {
*/ */
int getDisplayIndexInBlock() { int getDisplayIndexInBlock() {
exists(IRBlock block | exists(IRBlock block |
block = getBlock() and this = block.getInstruction(result)
( or
exists(int index, int phiCount | this = rank[-result - 1](PhiInstruction phiInstr |
phiCount = count(block.getAPhiInstruction()) and phiInstr = block.getAPhiInstruction()
this = block.getInstruction(index) and |
result = index + phiCount phiInstr order by phiInstr.getUniqueId()
) )
or
this instanceof PhiInstruction and
this = rank[result + 1](PhiInstruction phiInstr |
phiInstr = block.getAPhiInstruction()
|
phiInstr order by phiInstr.getUniqueId()
)
)
) )
} }
private int getLineRank() {
this = rank[result](Instruction instr |
instr.getAST().getFile() = getAST().getFile() and
instr.getAST().getLocation().getStartLine() = getAST().getLocation().getStartLine()
|
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
)
}
/** /**
* Gets a human-readable string that uniquely identifies this instruction * Gets a human-readable string that uniquely identifies this instruction
* within the function. This string is used to refer to this instruction when * within the function. This string is used to refer to this instruction when
@@ -385,8 +386,7 @@ class Instruction extends Construction::TInstruction {
* Example: `r1_1` * Example: `r1_1`
*/ */
string getResultId() { string getResultId() {
result = getResultPrefix() + getBlock().getDisplayIndex().toString() + "_" + result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank()
getDisplayIndexInBlock().toString()
} }
/** /**

View File

@@ -359,24 +359,25 @@ class Instruction extends Construction::TInstruction {
*/ */
int getDisplayIndexInBlock() { int getDisplayIndexInBlock() {
exists(IRBlock block | exists(IRBlock block |
block = getBlock() and this = block.getInstruction(result)
( or
exists(int index, int phiCount | this = rank[-result - 1](PhiInstruction phiInstr |
phiCount = count(block.getAPhiInstruction()) and phiInstr = block.getAPhiInstruction()
this = block.getInstruction(index) and |
result = index + phiCount phiInstr order by phiInstr.getUniqueId()
) )
or
this instanceof PhiInstruction and
this = rank[result + 1](PhiInstruction phiInstr |
phiInstr = block.getAPhiInstruction()
|
phiInstr order by phiInstr.getUniqueId()
)
)
) )
} }
private int getLineRank() {
this = rank[result](Instruction instr |
instr.getAST().getFile() = getAST().getFile() and
instr.getAST().getLocation().getStartLine() = getAST().getLocation().getStartLine()
|
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
)
}
/** /**
* Gets a human-readable string that uniquely identifies this instruction * Gets a human-readable string that uniquely identifies this instruction
* within the function. This string is used to refer to this instruction when * within the function. This string is used to refer to this instruction when
@@ -385,8 +386,7 @@ class Instruction extends Construction::TInstruction {
* Example: `r1_1` * Example: `r1_1`
*/ */
string getResultId() { string getResultId() {
result = getResultPrefix() + getBlock().getDisplayIndex().toString() + "_" + result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank()
getDisplayIndexInBlock().toString()
} }
/** /**

View File

@@ -359,24 +359,25 @@ class Instruction extends Construction::TInstruction {
*/ */
int getDisplayIndexInBlock() { int getDisplayIndexInBlock() {
exists(IRBlock block | exists(IRBlock block |
block = getBlock() and this = block.getInstruction(result)
( or
exists(int index, int phiCount | this = rank[-result - 1](PhiInstruction phiInstr |
phiCount = count(block.getAPhiInstruction()) and phiInstr = block.getAPhiInstruction()
this = block.getInstruction(index) and |
result = index + phiCount phiInstr order by phiInstr.getUniqueId()
) )
or
this instanceof PhiInstruction and
this = rank[result + 1](PhiInstruction phiInstr |
phiInstr = block.getAPhiInstruction()
|
phiInstr order by phiInstr.getUniqueId()
)
)
) )
} }
private int getLineRank() {
this = rank[result](Instruction instr |
instr.getAST().getFile() = getAST().getFile() and
instr.getAST().getLocation().getStartLine() = getAST().getLocation().getStartLine()
|
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
)
}
/** /**
* Gets a human-readable string that uniquely identifies this instruction * Gets a human-readable string that uniquely identifies this instruction
* within the function. This string is used to refer to this instruction when * within the function. This string is used to refer to this instruction when
@@ -385,8 +386,7 @@ class Instruction extends Construction::TInstruction {
* Example: `r1_1` * Example: `r1_1`
*/ */
string getResultId() { string getResultId() {
result = getResultPrefix() + getBlock().getDisplayIndex().toString() + "_" + result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank()
getDisplayIndexInBlock().toString()
} }
/** /**

View File

@@ -1,5 +1,6 @@
import AliasAnalysis import AliasAnalysis
private import SimpleSSAImports private import SimpleSSAImports
import SimpleSSAPublicImports
private class IntValue = Ints::IntValue; private class IntValue = Ints::IntValue;

View File

@@ -2,4 +2,3 @@ import semmle.code.cpp.ir.implementation.raw.IR
import semmle.code.cpp.ir.internal.IntegerConstant as Ints import semmle.code.cpp.ir.internal.IntegerConstant as Ints
import semmle.code.cpp.ir.implementation.internal.OperandTag import semmle.code.cpp.ir.implementation.internal.OperandTag
import semmle.code.cpp.ir.internal.IRCppLanguage as Language import semmle.code.cpp.ir.internal.IRCppLanguage as Language
import semmle.code.cpp.ir.internal.Overlap

View File

@@ -0,0 +1 @@
import semmle.code.cpp.ir.internal.Overlap

View File

@@ -32,7 +32,7 @@ class StringLiteral = Cpp::StringLiteral;
class Variable = Cpp::Variable; class Variable = Cpp::Variable;
class AutomaticVariable = Cpp::LocalScopeVariable; class AutomaticVariable = Cpp::StackVariable;
class StaticVariable = Cpp::Variable; class StaticVariable = Cpp::Variable;
@@ -66,10 +66,7 @@ int getTypeSize(Type type) { result = type.getSize() }
int getPointerSize() { exists(Cpp::NullPointerType nullptr | result = nullptr.getSize()) } int getPointerSize() { exists(Cpp::NullPointerType nullptr | result = nullptr.getSize()) }
predicate isVariableAutomatic(Variable var) { predicate isVariableAutomatic(Cpp::StackVariable var) { any() }
var instanceof Cpp::LocalScopeVariable and
not var.(Cpp::LocalScopeVariable).isStatic()
}
string getStringLiteralText(StringLiteral s) { string getStringLiteralText(StringLiteral s) {
result = s.getValueText().replaceAll("\n", " ").replaceAll("\r", "").replaceAll("\t", " ") result = s.getValueText().replaceAll("\n", " ").replaceAll("\r", "").replaceAll("\t", " ")

View File

@@ -24,7 +24,7 @@ predicate nanExcludingComparison(ComparisonOperation guard, boolean polarity) {
* by virtue of the guard in `def`. * by virtue of the guard in `def`.
*/ */
private predicate excludesNan(RangeSsaDefinition def, VariableAccess v) { private predicate excludesNan(RangeSsaDefinition def, VariableAccess v) {
exists(VariableAccess inCond, ComparisonOperation guard, boolean branch, LocalScopeVariable lsv | exists(VariableAccess inCond, ComparisonOperation guard, boolean branch, StackVariable lsv |
def.isGuardPhi(inCond, guard, branch) and def.isGuardPhi(inCond, guard, branch) and
inCond.getTarget() = lsv and inCond.getTarget() = lsv and
v = def.getAUse(lsv) and v = def.getAUse(lsv) and

View File

@@ -31,7 +31,7 @@ library class RangeSSA extends SSAHelper {
/** /**
* Add a phi node on the out-edge of a guard. * Add a phi node on the out-edge of a guard.
*/ */
override predicate custom_phi_node(LocalScopeVariable v, BasicBlock b) { override predicate custom_phi_node(StackVariable v, BasicBlock b) {
guard_defn(v.getAnAccess(), _, b, _) guard_defn(v.getAnAccess(), _, b, _)
} }
} }
@@ -67,19 +67,19 @@ class RangeSsaDefinition extends ControlFlowNodeBase {
RangeSsaDefinition() { exists(RangeSSA x | x.ssa_defn(_, this, _, _)) } RangeSsaDefinition() { exists(RangeSSA x | x.ssa_defn(_, this, _, _)) }
/** /**
* Gets a variable corresponding to a SSA LocalScopeVariable defined by * Gets a variable corresponding to a SSA StackVariable defined by
* this definition. * this definition.
*/ */
LocalScopeVariable getAVariable() { exists(RangeSSA x | x.ssa_defn(result, this, _, _)) } StackVariable getAVariable() { exists(RangeSSA x | x.ssa_defn(result, this, _, _)) }
/** /**
* A string representation of the SSA variable represented by the pair * A string representation of the SSA variable represented by the pair
* `(this, v)`. * `(this, v)`.
*/ */
string toString(LocalScopeVariable v) { exists(RangeSSA x | result = x.toString(this, v)) } string toString(StackVariable v) { exists(RangeSSA x | result = x.toString(this, v)) }
/** Gets a use of the SSA variable represented by the pair `(this, v)`. */ /** Gets a use of the SSA variable represented by the pair `(this, v)`. */
VariableAccess getAUse(LocalScopeVariable v) { exists(RangeSSA x | result = x.getAUse(this, v)) } VariableAccess getAUse(StackVariable v) { exists(RangeSSA x | result = x.getAUse(this, v)) }
/** Gets the control flow node for this definition. */ /** Gets the control flow node for this definition. */
ControlFlowNode getDefinition() { result = this } ControlFlowNode getDefinition() { result = this }
@@ -87,9 +87,7 @@ class RangeSsaDefinition extends ControlFlowNodeBase {
BasicBlock getBasicBlock() { result.contains(getDefinition()) } BasicBlock getBasicBlock() { result.contains(getDefinition()) }
/** Whether this definition is a phi node for variable `v`. */ /** Whether this definition is a phi node for variable `v`. */
predicate isPhiNode(LocalScopeVariable v) { predicate isPhiNode(StackVariable v) { exists(RangeSSA x | x.phi_node(v, this.(BasicBlock))) }
exists(RangeSSA x | x.phi_node(v, this.(BasicBlock)))
}
/** /**
* If this definition is a phi node corresponding to a guard, * If this definition is a phi node corresponding to a guard,
@@ -104,7 +102,7 @@ class RangeSsaDefinition extends ControlFlowNodeBase {
/** Whether this definition is from a parameter */ /** Whether this definition is from a parameter */
predicate definedByParameter(Parameter p) { this = p.getFunction().getEntryPoint() } predicate definedByParameter(Parameter p) { this = p.getFunction().getEntryPoint() }
RangeSsaDefinition getAPhiInput(LocalScopeVariable v) { RangeSsaDefinition getAPhiInput(StackVariable v) {
this.isPhiNode(v) and this.isPhiNode(v) and
exists(BasicBlock pred | exists(BasicBlock pred |
pred = this.(BasicBlock).getAPredecessor() and pred = this.(BasicBlock).getAPredecessor() and
@@ -137,7 +135,7 @@ class RangeSsaDefinition extends ControlFlowNodeBase {
} }
/** Gets the expression assigned to this SsaDefinition. */ /** Gets the expression assigned to this SsaDefinition. */
Expr getDefiningValue(LocalScopeVariable v) { Expr getDefiningValue(StackVariable v) {
exists(ControlFlowNode def | def = this.getDefinition() | exists(ControlFlowNode def | def = this.getDefinition() |
def = v.getInitializer().getExpr() and def = result def = v.getInitializer().getExpr() and def = result
or or
@@ -155,7 +153,7 @@ class RangeSsaDefinition extends ControlFlowNodeBase {
) )
} }
predicate reachesEndOfBB(LocalScopeVariable v, BasicBlock b) { predicate reachesEndOfBB(StackVariable v, BasicBlock b) {
exists(RangeSSA x | x.ssaDefinitionReachesEndOfBB(v, this, b)) exists(RangeSSA x | x.ssaDefinitionReachesEndOfBB(v, this, b))
} }
} }

View File

@@ -120,7 +120,7 @@ private predicate analyzableExpr(Expr e) {
exists(exprMinVal(e.(Conversion).getExpr())) or exists(exprMinVal(e.(Conversion).getExpr())) or
// Also allow variable accesses, provided that they have SSA // Also allow variable accesses, provided that they have SSA
// information. // information.
exists(RangeSsaDefinition def, LocalScopeVariable v | e = def.getAUse(v)) exists(RangeSsaDefinition def, StackVariable v | e = def.getAUse(v))
) )
} }
@@ -136,7 +136,7 @@ private predicate analyzableExpr(Expr e) {
* here. * here.
*/ */
private predicate defDependsOnDef( private predicate defDependsOnDef(
RangeSsaDefinition def, LocalScopeVariable v, RangeSsaDefinition srcDef, LocalScopeVariable srcVar RangeSsaDefinition def, StackVariable v, RangeSsaDefinition srcDef, StackVariable srcVar
) { ) {
// Definitions with a defining value. // Definitions with a defining value.
exists(Expr expr | assignmentDef(def, v, expr) | exprDependsOnDef(expr, srcDef, srcVar)) exists(Expr expr | assignmentDef(def, v, expr) | exprDependsOnDef(expr, srcDef, srcVar))
@@ -171,7 +171,7 @@ private predicate defDependsOnDef(
* Helper predicate for `defDependsOnDef`. This predicate matches * Helper predicate for `defDependsOnDef`. This predicate matches
* the structure of `getLowerBoundsImpl` and `getUpperBoundsImpl`. * the structure of `getLowerBoundsImpl` and `getUpperBoundsImpl`.
*/ */
private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, LocalScopeVariable srcVar) { private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, StackVariable srcVar) {
exists(UnaryMinusExpr negateExpr | e = negateExpr | exists(UnaryMinusExpr negateExpr | e = negateExpr |
exprDependsOnDef(negateExpr.getOperand(), srcDef, srcVar) exprDependsOnDef(negateExpr.getOperand(), srcDef, srcVar)
) )
@@ -226,7 +226,7 @@ private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, LocalScope
* the structure of `getPhiLowerBounds` and `getPhiUpperBounds`. * the structure of `getPhiLowerBounds` and `getPhiUpperBounds`.
*/ */
private predicate phiDependsOnDef( private predicate phiDependsOnDef(
RangeSsaDefinition phi, LocalScopeVariable v, RangeSsaDefinition srcDef, LocalScopeVariable srcVar RangeSsaDefinition phi, StackVariable v, RangeSsaDefinition srcDef, StackVariable srcVar
) { ) {
exists(VariableAccess access, ComparisonOperation guard | exists(VariableAccess access, ComparisonOperation guard |
access = v.getAnAccess() and access = v.getAnAccess() and
@@ -241,19 +241,17 @@ private predicate phiDependsOnDef(
/** The transitive closure of `defDependsOnDef`. */ /** The transitive closure of `defDependsOnDef`. */
private predicate defDependsOnDefTransitively( private predicate defDependsOnDefTransitively(
RangeSsaDefinition def, LocalScopeVariable v, RangeSsaDefinition srcDef, LocalScopeVariable srcVar RangeSsaDefinition def, StackVariable v, RangeSsaDefinition srcDef, StackVariable srcVar
) { ) {
defDependsOnDef(def, v, srcDef, srcVar) defDependsOnDef(def, v, srcDef, srcVar)
or or
exists(RangeSsaDefinition midDef, LocalScopeVariable midVar | exists(RangeSsaDefinition midDef, StackVariable midVar | defDependsOnDef(def, v, midDef, midVar) |
defDependsOnDef(def, v, midDef, midVar)
|
defDependsOnDefTransitively(midDef, midVar, srcDef, srcVar) defDependsOnDefTransitively(midDef, midVar, srcDef, srcVar)
) )
} }
/** The set of definitions that depend recursively on themselves. */ /** The set of definitions that depend recursively on themselves. */
private predicate isRecursiveDef(RangeSsaDefinition def, LocalScopeVariable v) { private predicate isRecursiveDef(RangeSsaDefinition def, StackVariable v) {
defDependsOnDefTransitively(def, v, def, v) defDependsOnDefTransitively(def, v, def, v)
} }
@@ -271,7 +269,7 @@ private predicate isRecursiveDef(RangeSsaDefinition def, LocalScopeVariable v) {
* *
* This predicate finds all the definitions in the first set. * This predicate finds all the definitions in the first set.
*/ */
private predicate assignmentDef(RangeSsaDefinition def, LocalScopeVariable v, Expr expr) { private predicate assignmentDef(RangeSsaDefinition def, StackVariable v, Expr expr) {
v.getUnspecifiedType() instanceof ArithmeticType and v.getUnspecifiedType() instanceof ArithmeticType and
( (
def = v.getInitializer().getExpr() and def = expr def = v.getInitializer().getExpr() and def = expr
@@ -285,7 +283,7 @@ private predicate assignmentDef(RangeSsaDefinition def, LocalScopeVariable v, Ex
} }
/** See comment above sourceDef. */ /** See comment above sourceDef. */
private predicate analyzableDef(RangeSsaDefinition def, LocalScopeVariable v) { private predicate analyzableDef(RangeSsaDefinition def, StackVariable v) {
assignmentDef(def, v, _) or defDependsOnDef(def, v, _, _) assignmentDef(def, v, _) or defDependsOnDef(def, v, _, _)
} }
@@ -613,7 +611,7 @@ private float getLowerBoundsImpl(Expr expr) {
) )
or or
// Use SSA to get the lower bounds for a variable use. // Use SSA to get the lower bounds for a variable use.
exists(RangeSsaDefinition def, LocalScopeVariable v | expr = def.getAUse(v) | exists(RangeSsaDefinition def, StackVariable v | expr = def.getAUse(v) |
result = getDefLowerBounds(def, v) result = getDefLowerBounds(def, v)
) )
} }
@@ -766,7 +764,7 @@ private float getUpperBoundsImpl(Expr expr) {
) )
or or
// Use SSA to get the upper bounds for a variable use. // Use SSA to get the upper bounds for a variable use.
exists(RangeSsaDefinition def, LocalScopeVariable v | expr = def.getAUse(v) | exists(RangeSsaDefinition def, StackVariable v | expr = def.getAUse(v) |
result = getDefUpperBounds(def, v) result = getDefUpperBounds(def, v)
) )
} }
@@ -860,7 +858,7 @@ private float boolConversionUpperBound(Expr expr) {
* In this example, the lower bound of x is 0, but we can * In this example, the lower bound of x is 0, but we can
* use the guard to deduce that the lower bound is 2 inside the block. * use the guard to deduce that the lower bound is 2 inside the block.
*/ */
private float getPhiLowerBounds(LocalScopeVariable v, RangeSsaDefinition phi) { private float getPhiLowerBounds(StackVariable v, RangeSsaDefinition phi) {
exists( exists(
VariableAccess access, ComparisonOperation guard, boolean branch, float defLB, float guardLB VariableAccess access, ComparisonOperation guard, boolean branch, float defLB, float guardLB
| |
@@ -877,7 +875,7 @@ private float getPhiLowerBounds(LocalScopeVariable v, RangeSsaDefinition phi) {
} }
/** See comment for `getPhiLowerBounds`, above. */ /** See comment for `getPhiLowerBounds`, above. */
private float getPhiUpperBounds(LocalScopeVariable v, RangeSsaDefinition phi) { private float getPhiUpperBounds(StackVariable v, RangeSsaDefinition phi) {
exists( exists(
VariableAccess access, ComparisonOperation guard, boolean branch, float defUB, float guardUB VariableAccess access, ComparisonOperation guard, boolean branch, float defUB, float guardUB
| |
@@ -894,7 +892,7 @@ private float getPhiUpperBounds(LocalScopeVariable v, RangeSsaDefinition phi) {
} }
/** Only to be called by `getDefLowerBounds`. */ /** Only to be called by `getDefLowerBounds`. */
private float getDefLowerBoundsImpl(RangeSsaDefinition def, LocalScopeVariable v) { private float getDefLowerBoundsImpl(RangeSsaDefinition def, StackVariable v) {
// Definitions with a defining value. // Definitions with a defining value.
exists(Expr expr | assignmentDef(def, v, expr) | result = getFullyConvertedLowerBounds(expr)) exists(Expr expr | assignmentDef(def, v, expr) | result = getFullyConvertedLowerBounds(expr))
or or
@@ -936,7 +934,7 @@ private float getDefLowerBoundsImpl(RangeSsaDefinition def, LocalScopeVariable v
} }
/** Only to be called by `getDefUpperBounds`. */ /** Only to be called by `getDefUpperBounds`. */
private float getDefUpperBoundsImpl(RangeSsaDefinition def, LocalScopeVariable v) { private float getDefUpperBoundsImpl(RangeSsaDefinition def, StackVariable v) {
// Definitions with a defining value. // Definitions with a defining value.
exists(Expr expr | assignmentDef(def, v, expr) | result = getFullyConvertedUpperBounds(expr)) exists(Expr expr | assignmentDef(def, v, expr) | result = getFullyConvertedUpperBounds(expr))
or or
@@ -982,7 +980,7 @@ private float getDefUpperBoundsImpl(RangeSsaDefinition def, LocalScopeVariable v
* done by `getDefLowerBoundsImpl`, but this is where widening is applied * done by `getDefLowerBoundsImpl`, but this is where widening is applied
* to prevent the analysis from exploding due to a recursive definition. * to prevent the analysis from exploding due to a recursive definition.
*/ */
private float getDefLowerBounds(RangeSsaDefinition def, LocalScopeVariable v) { private float getDefLowerBounds(RangeSsaDefinition def, StackVariable v) {
exists(float newLB, float truncatedLB | exists(float newLB, float truncatedLB |
newLB = getDefLowerBoundsImpl(def, v) and newLB = getDefLowerBoundsImpl(def, v) and
if varMinVal(v) <= newLB and newLB <= varMaxVal(v) if varMinVal(v) <= newLB and newLB <= varMaxVal(v)
@@ -1011,7 +1009,7 @@ private float getDefLowerBounds(RangeSsaDefinition def, LocalScopeVariable v) {
} }
/** See comment for `getDefLowerBounds`, above. */ /** See comment for `getDefLowerBounds`, above. */
private float getDefUpperBounds(RangeSsaDefinition def, LocalScopeVariable v) { private float getDefUpperBounds(RangeSsaDefinition def, StackVariable v) {
exists(float newUB, float truncatedUB | exists(float newUB, float truncatedUB |
newUB = getDefUpperBoundsImpl(def, v) and newUB = getDefUpperBoundsImpl(def, v) and
if varMinVal(v) <= newUB and newUB <= varMaxVal(v) if varMinVal(v) <= newUB and newUB <= varMaxVal(v)
@@ -1044,9 +1042,7 @@ private float getDefUpperBounds(RangeSsaDefinition def, LocalScopeVariable v) {
* unanalyzable definitions (such as function parameters) and make their * unanalyzable definitions (such as function parameters) and make their
* bounds unknown. * bounds unknown.
*/ */
private predicate unanalyzableDefBounds( private predicate unanalyzableDefBounds(RangeSsaDefinition def, StackVariable v, float lb, float ub) {
RangeSsaDefinition def, LocalScopeVariable v, float lb, float ub
) {
v = def.getAVariable() and v = def.getAVariable() and
not analyzableDef(def, v) and not analyzableDef(def, v) and
lb = varMinVal(v) and lb = varMinVal(v) and
@@ -1268,13 +1264,13 @@ private module SimpleRangeAnalysisCached {
/** Holds if the definition might overflow negatively. */ /** Holds if the definition might overflow negatively. */
cached cached
predicate defMightOverflowNegatively(RangeSsaDefinition def, LocalScopeVariable v) { predicate defMightOverflowNegatively(RangeSsaDefinition def, StackVariable v) {
getDefLowerBoundsImpl(def, v) < varMinVal(v) getDefLowerBoundsImpl(def, v) < varMinVal(v)
} }
/** Holds if the definition might overflow positively. */ /** Holds if the definition might overflow positively. */
cached cached
predicate defMightOverflowPositively(RangeSsaDefinition def, LocalScopeVariable v) { predicate defMightOverflowPositively(RangeSsaDefinition def, StackVariable v) {
getDefUpperBoundsImpl(def, v) > varMaxVal(v) getDefUpperBoundsImpl(def, v) > varMaxVal(v)
} }
@@ -1283,7 +1279,7 @@ private module SimpleRangeAnalysisCached {
* negatively). * negatively).
*/ */
cached cached
predicate defMightOverflow(RangeSsaDefinition def, LocalScopeVariable v) { predicate defMightOverflow(RangeSsaDefinition def, StackVariable v) {
defMightOverflowNegatively(def, v) or defMightOverflowNegatively(def, v) or
defMightOverflowPositively(def, v) defMightOverflowPositively(def, v)
} }

View File

@@ -170,8 +170,8 @@ private newtype GVNBase =
GVN_FloatConst(float val, Type t) { mk_FloatConst(val, t, _) } or GVN_FloatConst(float val, Type t) { mk_FloatConst(val, t, _) } or
// If the local variable does not have a defining value, then // If the local variable does not have a defining value, then
// we use the SsaDefinition as its global value number. // we use the SsaDefinition as its global value number.
GVN_UndefinedLocalScopeVariable(LocalScopeVariable x, SsaDefinition def) { GVN_UndefinedStackVariable(StackVariable x, SsaDefinition def) {
mk_UndefinedLocalScopeVariable(x, def, _) mk_UndefinedStackVariable(x, def, _)
} or } or
// Variables with no SSA information. As a crude (but safe) // Variables with no SSA information. As a crude (but safe)
// approximation, we use `mostRecentSideEffect` to compute a definition // approximation, we use `mostRecentSideEffect` to compute a definition
@@ -235,8 +235,8 @@ class GVN extends GVNBase {
if this instanceof GVN_FloatConst if this instanceof GVN_FloatConst
then result = "FloatConst" then result = "FloatConst"
else else
if this instanceof GVN_UndefinedLocalScopeVariable if this instanceof GVN_UndefinedStackVariable
then result = "UndefinedLocalScopeVariable" then result = "UndefinedStackVariable"
else else
if this instanceof GVN_OtherVariable if this instanceof GVN_OtherVariable
then result = "OtherVariable" then result = "OtherVariable"
@@ -307,7 +307,7 @@ private predicate mk_FloatConst(float val, Type t, Expr e) {
t = e.getUnspecifiedType() t = e.getUnspecifiedType()
} }
private predicate analyzableLocalScopeVariable(VariableAccess access) { private predicate analyzableStackVariable(VariableAccess access) {
strictcount(SsaDefinition def | def.getAUse(_) = access | def) = 1 and strictcount(SsaDefinition def | def.getAUse(_) = access | def) = 1 and
strictcount(SsaDefinition def, Variable v | def.getAUse(v) = access | v) = 1 and strictcount(SsaDefinition def, Variable v | def.getAUse(v) = access | v) = 1 and
count(SsaDefinition def, Variable v | count(SsaDefinition def, Variable v |
@@ -322,10 +322,10 @@ private predicate analyzableLocalScopeVariable(VariableAccess access) {
// defining value. If there is a defining value, then there is no // defining value. If there is a defining value, then there is no
// need to generate a fresh `GVN` for the access because `globalValueNumber` // need to generate a fresh `GVN` for the access because `globalValueNumber`
// will follow the chain and use the GVN of the defining value. // will follow the chain and use the GVN of the defining value.
private predicate mk_UndefinedLocalScopeVariable( private predicate mk_UndefinedStackVariable(
LocalScopeVariable x, SsaDefinition def, VariableAccess access StackVariable x, SsaDefinition def, VariableAccess access
) { ) {
analyzableLocalScopeVariable(access) and analyzableStackVariable(access) and
access = def.getAUse(x) and access = def.getAUse(x) and
not exists(def.getDefiningValue(x)) not exists(def.getDefiningValue(x))
} }
@@ -515,16 +515,16 @@ GVN globalValueNumber(Expr e) {
) )
or or
// Local variable with a defining value. // Local variable with a defining value.
exists(LocalScopeVariable x, SsaDefinition def | exists(StackVariable x, SsaDefinition def |
analyzableLocalScopeVariable(e) and analyzableStackVariable(e) and
e = def.getAUse(x) and e = def.getAUse(x) and
result = globalValueNumber(def.getDefiningValue(x).getFullyConverted()) result = globalValueNumber(def.getDefiningValue(x).getFullyConverted())
) )
or or
// Local variable without a defining value. // Local variable without a defining value.
exists(LocalScopeVariable x, SsaDefinition def | exists(StackVariable x, SsaDefinition def |
mk_UndefinedLocalScopeVariable(x, def, e) and mk_UndefinedStackVariable(x, def, e) and
result = GVN_UndefinedLocalScopeVariable(x, def) result = GVN_UndefinedStackVariable(x, def)
) )
or or
// Variable with no SSA information. // Variable with no SSA information.
@@ -594,7 +594,7 @@ private predicate analyzableConst(Expr e) {
*/ */
private predicate analyzableExpr(Expr e) { private predicate analyzableExpr(Expr e) {
analyzableConst(e) or analyzableConst(e) or
analyzableLocalScopeVariable(e) or analyzableStackVariable(e) or
analyzableDotFieldAccess(e) or analyzableDotFieldAccess(e) or
analyzablePointerFieldAccess(e) or analyzablePointerFieldAccess(e) or
analyzableImplicitThisFieldAccess(e) or analyzableImplicitThisFieldAccess(e) or

View File

@@ -112,7 +112,7 @@ class UMLType extends UMLElement {
else result = this.getUMLName() else result = this.getUMLName()
} }
string toString() { result = this.getUMLName() } override string toString() { result = this.getUMLName() }
} }
/** /**
@@ -163,7 +163,7 @@ class UMLProperty extends UMLElement {
result.getDeclaringType() = this.getUMLType().getCType() result.getDeclaringType() = this.getUMLType().getCType()
} }
string toString() { override string toString() {
if this.isEnumConstant() if this.isEnumConstant()
then result = "- <<enum constant>> " + this.getUMLName() then result = "- <<enum constant>> " + this.getUMLName()
else result = "- " + this.getUMLName() else result = "- " + this.getUMLName()
@@ -196,7 +196,7 @@ class UMLOperation extends UMLElement {
result.getDeclaringType() = this.getUMLType().getCType() result.getDeclaringType() = this.getUMLType().getCType()
} }
string toString() { result = "+ " + this.getUMLName() } override string toString() { result = "+ " + this.getUMLName() }
} }
/** /**
@@ -221,7 +221,7 @@ class UMLAssociation extends UMLProperty {
/** /**
* Gets the C field corresponding to this property, if any. * Gets the C field corresponding to this property, if any.
*/ */
Field getCField() { override Field getCField() {
result.hasName(this.getLabel()) and result.hasName(this.getLabel()) and
result.getDeclaringType() = this.getSource().getCType() result.getDeclaringType() = this.getSource().getCType()
} }
@@ -271,7 +271,7 @@ class UMLInheritance extends UMLElement {
) )
} }
string toString() { override string toString() {
result = this.getUMLClient().getUMLName() + " implements " + this.getUMLSupplier().getUMLName() result = this.getUMLClient().getUMLName() + " implements " + this.getUMLSupplier().getUMLName()
} }
} }
@@ -303,5 +303,5 @@ class UMLPackage extends UMLElement {
else result = this.getUMLName() else result = this.getUMLName()
} }
string toString() { result = this.getUMLQualifiedName() } override string toString() { result = this.getUMLQualifiedName() }
} }

View File

@@ -9,12 +9,12 @@ import cpp
import semmle.code.cpp.controlflow.SSA import semmle.code.cpp.controlflow.SSA
/* /*
* Count of number of uses of a LocalScopeVariable where no corresponding SSA definition exists, * Count of number of uses of a StackVariable where no corresponding SSA definition exists,
* but at least one SSA definition for that variable can reach that use. * but at least one SSA definition for that variable can reach that use.
* Should always be zero *regardless* of the input * Should always be zero *regardless* of the input
*/ */
select count(LocalScopeVariable v, Expr use | select count(StackVariable v, Expr use |
exists(SsaDefinition def, BasicBlock db, BasicBlock ub | exists(SsaDefinition def, BasicBlock db, BasicBlock ub |
def.getAUse(v) = use and db.contains(def.getDefinition()) and ub.contains(use) def.getAUse(v) = use and db.contains(def.getDefinition()) and ub.contains(use)
| |

View File

@@ -7,6 +7,6 @@
import cpp import cpp
import semmle.code.cpp.controlflow.SSA import semmle.code.cpp.controlflow.SSA
from SsaDefinition def, LocalScopeVariable var, Expr use from SsaDefinition def, StackVariable var, Expr use
where def.getAUse(var) = use where def.getAUse(var) = use
select def, def.toString(var), use select def, def.toString(var), use

View File

@@ -12,7 +12,7 @@ import semmle.code.cpp.controlflow.SSA
* Should always be zero *regardless* of the input * Should always be zero *regardless* of the input
*/ */
select count(SsaDefinition d, LocalScopeVariable v, Expr u | select count(SsaDefinition d, StackVariable v, Expr u |
d.getAUse(v) = u and d.getAUse(v) = u and
not exists(BasicBlock bd, BasicBlock bu | not exists(BasicBlock bd, BasicBlock bu |
bd.contains(mkElement(d).(ControlFlowNode)) and bu.contains(u) bd.contains(mkElement(d).(ControlFlowNode)) and bu.contains(u)

View File

@@ -8,7 +8,7 @@ import cpp
import semmle.code.cpp.controlflow.SSA import semmle.code.cpp.controlflow.SSA
import semmle.code.cpp.controlflow.Guards import semmle.code.cpp.controlflow.Guards
from GuardedSsa def, LocalScopeVariable var, Expr other, int k, int start, int end, string op from GuardedSsa def, StackVariable var, Expr other, int k, int start, int end, string op
where where
exists(BasicBlock block | exists(BasicBlock block |
def.isLt(var, other, k, block, true) and op = "<" def.isLt(var, other, k, block, true) and op = "<"

View File

@@ -8,8 +8,7 @@ import cpp
import semmle.code.cpp.controlflow.SSA import semmle.code.cpp.controlflow.SSA
from from
File file, SsaDefinition phi, LocalScopeVariable var, SsaDefinition input, int philine, File file, SsaDefinition phi, StackVariable var, SsaDefinition input, int philine, int inputline
int inputline
where where
phi.getAPhiInput(var) = input and phi.getAPhiInput(var) = input and
file = phi.getLocation().getFile() and file = phi.getLocation().getFile() and

View File

@@ -12,7 +12,7 @@ import semmle.code.cpp.controlflow.SSA
* Should always be zero *regardless* of the input * Should always be zero *regardless* of the input
*/ */
select count(SsaDefinition d1, SsaDefinition d2, Expr u, LocalScopeVariable v | select count(SsaDefinition d1, SsaDefinition d2, Expr u, StackVariable v |
d1.getAUse(v) = u and d1.getAUse(v) = u and
d2.getAUse(v) = u and d2.getAUse(v) = u and
not d1 = d2 not d1 = d2

View File

@@ -7,6 +7,6 @@
import cpp import cpp
import semmle.code.cpp.controlflow.SSA import semmle.code.cpp.controlflow.SSA
from SsaDefinition def, LocalScopeVariable var, Expr use from SsaDefinition def, StackVariable var, Expr use
where def.getAUse(var) = use where def.getAUse(var) = use
select def, def.toString(var), use select def, def.toString(var), use

View File

@@ -1,5 +1,5 @@
import semmle.code.cpp.dataflow.internal.FlowVar import semmle.code.cpp.dataflow.internal.FlowVar
from LocalScopeVariable var, VariableAccess va from Variable var, VariableAccess va
where FlowVar_internal::mayBeUsedUninitialized(var, va) where FlowVar_internal::mayBeUsedUninitialized(var, va)
select var, va select var, va

View File

@@ -1,5 +1,5 @@
import semmle.code.cpp.dataflow.internal.FlowVar import semmle.code.cpp.dataflow.internal.FlowVar
from LocalScopeVariable var, VariableAccess va from Variable var, VariableAccess va
where FlowVar_internal::mayBeUsedUninitialized(var, va) where FlowVar_internal::mayBeUsedUninitialized(var, va)
select var, va select var, va

View File

@@ -1,5 +1,5 @@
import cpp import cpp
from LocalScopeVariable v, ControlFlowNode d from StackVariable v, ControlFlowNode d
where definition(v, d) where definition(v, d)
select v, d select v, d

View File

@@ -1,24 +1,22 @@
import cpp import cpp
import semmle.code.cpp.controlflow.LocalScopeVariableReachability import semmle.code.cpp.controlflow.StackVariableReachability
// Test that def/use algorithm is an instance of LocalScopeVariableReachability // Test that def/use algorithm is an instance of StackVariableReachability
class MyDefOrUse extends LocalScopeVariableReachability { class MyDefOrUse extends StackVariableReachability {
MyDefOrUse() { this = "MyDefUse" } MyDefOrUse() { this = "MyDefUse" }
override predicate isSource(ControlFlowNode node, LocalScopeVariable v) { definition(v, node) } override predicate isSource(ControlFlowNode node, StackVariable v) { definition(v, node) }
override predicate isSink(ControlFlowNode node, LocalScopeVariable v) { useOfVar(v, node) } override predicate isSink(ControlFlowNode node, StackVariable v) { useOfVar(v, node) }
override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) { override predicate isBarrier(ControlFlowNode node, StackVariable v) { definitionBarrier(v, node) }
definitionBarrier(v, node)
}
} }
predicate equivalence() { predicate equivalence() {
forall(LocalScopeVariable v, Expr first, Expr second | definitionUsePair(v, first, second) | forall(StackVariable v, Expr first, Expr second | definitionUsePair(v, first, second) |
exists(MyDefOrUse x | x.reaches(first, v, second)) exists(MyDefOrUse x | x.reaches(first, v, second))
) and ) and
forall(LocalScopeVariable v, Expr first, Expr second | forall(StackVariable v, Expr first, Expr second |
exists(MyDefOrUse x | x.reaches(first, v, second)) exists(MyDefOrUse x | x.reaches(first, v, second))
| |
definitionUsePair(v, first, second) definitionUsePair(v, first, second)

View File

@@ -1,5 +1,5 @@
import cpp import cpp
from LocalScopeVariable v, ControlFlowNode def, Expr e from StackVariable v, ControlFlowNode def, Expr e
where exprDefinition(v, def, e) where exprDefinition(v, def, e)
select v, def, e select v, def, e

View File

@@ -1,5 +1,5 @@
import cpp import cpp
from LocalScopeVariable v, VariableAccess use from StackVariable v, VariableAccess use
where useOfVar(v, use) where useOfVar(v, use)
select v, use select v, use

View File

@@ -1,9 +1,9 @@
import cpp import cpp
from LocalScopeVariable v, VariableAccess use from StackVariable v, VariableAccess use
where where
useOfVarActual(v, use) and useOfVarActual(v, use) and
// Also check that `useOfVarActual` is a subset of `useOfVar`; if not // Also check that `useOfVarActual` is a subset of `useOfVar`; if not
// the query will not return any results // the query will not return any results
forall(LocalScopeVariable v0, VariableAccess use0 | useOfVarActual(v0, use0) | useOfVar(v0, use0)) forall(StackVariable v0, VariableAccess use0 | useOfVarActual(v0, use0) | useOfVar(v0, use0))
select v, use select v, use

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
import cpp import cpp
from LocalScopeVariable v, ControlFlowNode def, Expr e from StackVariable v, ControlFlowNode def, Expr e
where exprDefinition(v, def, e) where exprDefinition(v, def, e)
select v, def, e select v, def, e

View File

@@ -10,12 +10,12 @@ import cpp
import semmle.code.cpp.rangeanalysis.RangeSSA import semmle.code.cpp.rangeanalysis.RangeSSA
/* /*
* Count of number of uses of a LocalScopeVariable where no corresponding SSA definition exists, * Count of number of uses of a StackVariable where no corresponding SSA definition exists,
* but at least one SSA definition for that variable can reach that use. * but at least one SSA definition for that variable can reach that use.
* Should always be zero *regardless* of the input * Should always be zero *regardless* of the input
*/ */
select count(LocalScopeVariable v, Expr use | select count(StackVariable v, Expr use |
exists(RangeSsaDefinition def, BasicBlock db, BasicBlock ub | exists(RangeSsaDefinition def, BasicBlock db, BasicBlock ub |
def.getAUse(v) = use and db.contains(def.getDefinition()) and ub.contains(use) def.getAUse(v) = use and db.contains(def.getDefinition()) and ub.contains(use)
| |

View File

@@ -7,6 +7,6 @@
import cpp import cpp
import semmle.code.cpp.rangeanalysis.RangeSSA import semmle.code.cpp.rangeanalysis.RangeSSA
from RangeSsaDefinition def, LocalScopeVariable var, Expr use from RangeSsaDefinition def, StackVariable var, Expr use
where def.getAUse(var) = use where def.getAUse(var) = use
select def, def.toString(var), use select def, def.toString(var), use

View File

@@ -12,7 +12,7 @@ import semmle.code.cpp.rangeanalysis.RangeSSA
* Should always be zero *regardless* of the input * Should always be zero *regardless* of the input
*/ */
select count(RangeSsaDefinition d, LocalScopeVariable v, Expr u | select count(RangeSsaDefinition d, StackVariable v, Expr u |
d.getAUse(v) = u and d.getAUse(v) = u and
not exists(BasicBlock bd, BasicBlock bu | not exists(BasicBlock bd, BasicBlock bu |
bd.contains(mkElement(d).(ControlFlowNode)) and bu.contains(u) bd.contains(mkElement(d).(ControlFlowNode)) and bu.contains(u)

View File

@@ -7,9 +7,7 @@
import cpp import cpp
import semmle.code.cpp.rangeanalysis.RangeSSA import semmle.code.cpp.rangeanalysis.RangeSSA
from from RangeSsaDefinition phi, StackVariable var, RangeSsaDefinition input, int philine, int inputline
RangeSsaDefinition phi, LocalScopeVariable var, RangeSsaDefinition input, int philine,
int inputline
where where
phi.getAPhiInput(var) = input and phi.getAPhiInput(var) = input and
philine = phi.getLocation().getStartLine() and philine = phi.getLocation().getStartLine() and

View File

@@ -12,7 +12,7 @@ import semmle.code.cpp.rangeanalysis.RangeSSA
* Should always be zero *regardless* of the input * Should always be zero *regardless* of the input
*/ */
select count(RangeSsaDefinition d1, RangeSsaDefinition d2, Expr u, LocalScopeVariable v | select count(RangeSsaDefinition d1, RangeSsaDefinition d2, Expr u, StackVariable v |
d1.getAUse(v) = u and d1.getAUse(v) = u and
d2.getAUse(v) = u and d2.getAUse(v) = u and
not d1 = d2 not d1 = d2

View File

@@ -2,13 +2,9 @@
| file://:0:0:0:0 | gp_offset | file://:0:0:0:0 | unsigned int | Field | | | | file://:0:0:0:0 | gp_offset | file://:0:0:0:0 | unsigned int | Field | | |
| file://:0:0:0:0 | overflow_arg_area | file://:0:0:0:0 | void * | Field | | | | file://:0:0:0:0 | overflow_arg_area | file://:0:0:0:0 | void * | Field | | |
| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | __va_list_tag && | SemanticStackVariable | | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | __va_list_tag && | SemanticStackVariable | | |
| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | __va_list_tag && | StackVariable | | |
| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | address && | SemanticStackVariable | | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | address && | SemanticStackVariable | | |
| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | address && | StackVariable | | |
| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | const __va_list_tag & | SemanticStackVariable | | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | const __va_list_tag & | SemanticStackVariable | | |
| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | const __va_list_tag & | StackVariable | | |
| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | const address & | SemanticStackVariable | | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | const address & | SemanticStackVariable | | |
| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | const address & | StackVariable | | |
| file://:0:0:0:0 | reg_save_area | file://:0:0:0:0 | void * | Field | | | | file://:0:0:0:0 | reg_save_area | file://:0:0:0:0 | void * | Field | | |
| variables.cpp:1:12:1:12 | i | file://:0:0:0:0 | int | GlobalVariable | | | | variables.cpp:1:12:1:12 | i | file://:0:0:0:0 | int | GlobalVariable | | |
| variables.cpp:2:12:2:12 | i | file://:0:0:0:0 | int | GlobalVariable | | | | variables.cpp:2:12:2:12 | i | file://:0:0:0:0 | int | GlobalVariable | | |
@@ -38,11 +34,10 @@
| variables.cpp:37:6:37:8 | ap3 | file://:0:0:0:0 | int * | GlobalVariable | | | | variables.cpp:37:6:37:8 | ap3 | file://:0:0:0:0 | int * | GlobalVariable | | |
| variables.cpp:41:7:41:11 | local | file://:0:0:0:0 | char[] | LocalVariable | | | | variables.cpp:41:7:41:11 | local | file://:0:0:0:0 | char[] | LocalVariable | | |
| variables.cpp:41:7:41:11 | local | file://:0:0:0:0 | char[] | SemanticStackVariable | | | | variables.cpp:41:7:41:11 | local | file://:0:0:0:0 | char[] | SemanticStackVariable | | |
| variables.cpp:41:7:41:11 | local | file://:0:0:0:0 | char[] | StackVariable | | |
| variables.cpp:43:14:43:18 | local | file://:0:0:0:0 | int | LocalVariable | | static | | variables.cpp:43:14:43:18 | local | file://:0:0:0:0 | int | LocalVariable | | static |
| variables.cpp:43:14:43:18 | local | file://:0:0:0:0 | int | StackVariable | | static |
| variables.cpp:48:9:48:12 | name | file://:0:0:0:0 | char * | Field | | | | variables.cpp:48:9:48:12 | name | file://:0:0:0:0 | char * | Field | | |
| variables.cpp:49:12:49:17 | number | file://:0:0:0:0 | long | Field | | | | variables.cpp:49:12:49:17 | number | file://:0:0:0:0 | long | Field | | |
| variables.cpp:50:9:50:14 | street | file://:0:0:0:0 | char * | Field | | | | variables.cpp:50:9:50:14 | street | file://:0:0:0:0 | char * | Field | | |
| variables.cpp:51:9:51:12 | town | file://:0:0:0:0 | char * | Field | | | | variables.cpp:51:9:51:12 | town | file://:0:0:0:0 | char * | Field | | |
| variables.cpp:52:16:52:22 | country | file://:0:0:0:0 | char * | MemberVariable | | static | | variables.cpp:52:16:52:22 | country | file://:0:0:0:0 | char * | MemberVariable | | static |
| variables.cpp:56:14:56:29 | externInFunction | file://:0:0:0:0 | int | GlobalVariable | | |

View File

@@ -51,3 +51,7 @@ struct address {
char* town; char* town;
static char* country; static char* country;
}; };
void hasExtern() {
extern int externInFunction;
}

View File

@@ -0,0 +1,52 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p>
The <code>requestValidationMode</code> attribute in ASP.NET is used to configure built-in validation to
protect applications against code injections. Downgrading or disabling
this configuration is not recommended. The default value of 4.5
is the only recommended value, as previous versions only test a subset of requests.
</p>
</overview>
<recommendation>
<p>
Always set <code>requestValidationMode</code> to 4.5, or leave it at its default value.
</p>
</recommendation>
<example>
<p>
The following example shows the <code>requestValidationMode</code>
attribute set to a value of 4.0, which disables some protections and
ignores individual <code>Page</code> directives:
</p>
<sample src="ASPNetRequestValidationModeBad.config" />
<p>
Setting the value to 4.5 enables request validation for all requests:
</p>
<sample src="ASPNetRequestValidationModeGood.config" />
</example>
<references>
<li>
Microsoft:
<a
href="https://docs.microsoft.com/en-us/dotnet/api/system.web.configuration.httpruntimesection.requestvalidationmode?view=netframework-4.8">HttpRuntimeSection.RequestValidationMode Property
</a>.
</li>
<li>
OWASP:
<a
href="https://www.owasp.org/index.php/ASP.NET_Request_Validation">ASP.NET Request Validation</a>.
</li>
</references>
</qhelp>

Some files were not shown because too many files have changed in this diff Show More