mirror of
https://github.com/github/codeql.git
synced 2025-12-22 11:46:32 +01:00
Merge from master and resolve conflicts
This commit is contained in:
@@ -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()`.
|
||||||
|
|||||||
@@ -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()`.
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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.
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
25
change-notes/1.24/analysis-csharp.md
Normal file
25
change-notes/1.24/analysis-csharp.md
Normal 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
|
||||||
|
|
||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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) |
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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 < i</code>,
|
The following cases all fall into the first category.
|
||||||
it is possible to rewrite it as <code>(unsigned short)(i + delta) < 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 < 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 < n1</code>,
|
||||||
that <code>delta > 0</code> and the <code>limits.h</code> or <code>climits</code>
|
it is possible to rewrite it as <code>(unsigned short)(n1 + delta) < 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 < n1</code>,
|
||||||
|
it is also possible to rewrite it as <code>n1 > 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 < n1</code>,
|
||||||
|
it is possible to rewrite it as <code>n1 > INT_MAX - delta</code>. It must be true
|
||||||
|
that <code>delta >= 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 < i</code>,
|
|
||||||
it is possible to rewrite it as <code>INT_MAX - delta</code>. It must be true
|
|
||||||
that <code>delta > 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 < i</code>,
|
|
||||||
it is also possible to rewrite it as <code>(unsigned)i + delta < i</code>.
|
|
||||||
Note that program semantics are affected by this change.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Given <code>int i, delta</code> and <code>i + delta < i</code>,
|
|
||||||
it is also possible to rewrite it as <code>unsigned int i, delta</code> and
|
|
||||||
<code>i + delta < 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 < n1</code> will never
|
overflows means that the condition <code>n1 + delta < 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>,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 |
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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() |
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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."
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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(_)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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, _, _)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) |
|
||||||
|
|||||||
@@ -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 |
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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`. */
|
||||||
|
|||||||
@@ -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()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
import semmle.code.cpp.ir.internal.Overlap
|
||||||
@@ -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", " ")
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 = "<"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -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 | | |
|
||||||
|
|||||||
@@ -51,3 +51,7 @@ struct address {
|
|||||||
char* town;
|
char* town;
|
||||||
static char* country;
|
static char* country;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void hasExtern() {
|
||||||
|
extern int externInFunction;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
Reference in New Issue
Block a user