mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
Merge branch 'master' into rdmarsh/cpp/ir-callee-side-effects
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
{ "provide": [ "*/ql/src/qlpack.yml",
|
||||
"*/ql/test/qlpack.yml",
|
||||
"*/upgrades/qlpack.yml",
|
||||
"misc/legacy-support/*/qlpack.yml",
|
||||
"misc/suite-helpers/qlpack.yml",
|
||||
"codeql/.codeqlmanifest.json" ] }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Contributing to QL
|
||||
# Contributing to CodeQL
|
||||
|
||||
We welcome contributions to our standard library and standard checks. Got an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request!
|
||||
|
||||
@@ -9,13 +9,13 @@ Before we accept your pull request, we require that you have agreed to our Contr
|
||||
If you have an idea for a query that you would like to share with other Semmle users, please open a pull request to add it to this repository.
|
||||
Follow the steps below to help other users understand what your query does, and to ensure that your query is consistent with the other Semmle queries.
|
||||
|
||||
1. **Consult the QL documentation for query writers**
|
||||
1. **Consult the documentation for query writers**
|
||||
|
||||
There is lots of useful documentation to help you write QL, ranging from information about query file structure to language-specific tutorials. For more information on the documentation available, see [Writing QL queries](https://help.semmle.com/QL/learn-ql/writing-queries/writing-queries.html) on [help.semmle.com](https://help.semmle.com).
|
||||
There is lots of useful documentation to help you write queries, ranging from information about query file structure to tutorials for specific target languages. For more information on the documentation available, see [Writing CodeQL queries](https://help.semmle.com/QL/learn-ql/writing-queries/writing-queries.html) on [help.semmle.com](https://help.semmle.com).
|
||||
|
||||
2. **Format your QL correctly**
|
||||
2. **Format your code correctly**
|
||||
|
||||
All of Semmle's standard QL queries and libraries are uniformly formatted for clarity and consistency, so we strongly recommend that all QL contributions follow the same formatting guidelines. If you use QL for Eclipse, you can auto-format your query in the [QL editor](https://help.semmle.com/ql-for-eclipse/Content/WebHelp/ql-editor.html). For more information, see the [QL style guide](https://github.com/Semmle/ql/blob/master/docs/ql-style-guide.md).
|
||||
All of Semmle's standard queries and libraries are uniformly formatted for clarity and consistency, so we strongly recommend that all contributions follow the same formatting guidelines. If you use QL for Eclipse, you can auto-format your query in the [QL editor](https://help.semmle.com/ql-for-eclipse/Content/WebHelp/ql-editor.html). For more information, see the [CodeQL style guide](https://github.com/Semmle/ql/blob/master/docs/ql-style-guide.md).
|
||||
|
||||
3. **Make sure your query has the correct metadata**
|
||||
|
||||
@@ -29,7 +29,7 @@ Follow the steps below to help other users understand what your query does, and
|
||||
The `select` statement of your query must be compatible with the query type (determined by the `@kind` metadata property) for alert or path results to be displayed correctly in LGTM and QL for Eclipse.
|
||||
For more information on `select` statement format, see [Introduction to query files](https://help.semmle.com/QL/learn-ql/writing-queries/introduction-to-queries.html#select-clause) on help.semmle.com.
|
||||
|
||||
5. **Save your query in a `.ql` file in correct language directory in this repository**
|
||||
5. **Save your query in a `.ql` file in the correct language directory in this repository**
|
||||
|
||||
There are five language-specific directories in this repository:
|
||||
|
||||
@@ -54,7 +54,7 @@ repositories, which might be made public. We might also use this information
|
||||
to contact you in relation to your contributions, as well as in the
|
||||
normal course of software development. We also store records of your
|
||||
CLA agreements. Under GDPR legislation, we do this
|
||||
on the basis of our legitimate interest in creating the QL product.
|
||||
on the basis of our legitimate interest in creating the CodeQL product.
|
||||
|
||||
Please do get in touch (privacy@semmle.com) if you have any questions about
|
||||
this or our data protection policies.
|
||||
|
||||
14
README.md
14
README.md
@@ -1,16 +1,16 @@
|
||||
# Semmle QL
|
||||
# CodeQL
|
||||
|
||||
This open source repository contains the standard QL libraries and queries that power [LGTM](https://lgtm.com), and the other products that [Semmle](https://semmle.com) makes available to its customers worldwide.
|
||||
This open source repository contains the standard CodeQL libraries and queries that power [LGTM](https://lgtm.com), and the other products that [Semmle](https://semmle.com) makes available to its customers worldwide.
|
||||
|
||||
## How do I learn QL and run queries?
|
||||
## How do I learn CodeQL and run queries?
|
||||
|
||||
There is [extensive documentation](https://help.semmle.com/QL/learn-ql/) on getting started with writing QL.
|
||||
You can use the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com or the [QL for Eclipse](https://lgtm.com/help/lgtm/running-queries-ide) plugin to try out your queries on any open-source project that's currently being analyzed.
|
||||
There is [extensive documentation](https://help.semmle.com/QL/learn-ql/) on getting started with writing CodeQL.
|
||||
You can use the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com or the [QL for Eclipse](https://lgtm.com/help/lgtm/running-queries-ide) plugin to try out your queries on any open source project that's currently being analyzed.
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions to our standard library and standard checks. Do you have an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request! Before you do, though, please take the time to read our [contributing guidelines](CONTRIBUTING.md). You can also consult our [style guides](https://github.com/Semmle/ql/tree/master/docs) to learn how to format your QL for consistency and clarity, how to write query metadata, and how to write query help documentation for your query.
|
||||
We welcome contributions to our standard library and standard checks. Do you have an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request! Before you do, though, please take the time to read our [contributing guidelines](CONTRIBUTING.md). You can also consult our [style guides](https://github.com/Semmle/ql/tree/master/docs) to learn how to format your code for consistency and clarity, how to write query metadata, and how to write query help documentation for your query.
|
||||
|
||||
## License
|
||||
|
||||
The QL queries in this repository are licensed under [Apache License 2.0](LICENSE) by [Semmle](https://semmle.com).
|
||||
The code in this repository is licensed under [Apache License 2.0](LICENSE) by [Semmle](https://semmle.com).
|
||||
|
||||
@@ -9,6 +9,7 @@ The following changes in version 1.23 affect C/C++ analysis in all applications.
|
||||
| **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`). |
|
||||
| Signed overflow check (`cpp/signed-overflow-check`) | correctness, reliability | Finds overflow checks that rely on signed integer addition to overflow, which has undefined behavior. Example: `a + b < a`. |
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
@@ -23,8 +24,10 @@ The following changes in version 1.23 affect C/C++ analysis in all applications.
|
||||
| Too many arguments to formatting function (`cpp/too-many-format-arguments`) | Fewer false positive results | Fixed false positives resulting from mistmatching declarations of a formatting function. |
|
||||
| Unclear comparison precedence (`cpp/comparison-precedence`) | Fewer false positive results | False positives involving template classes and functions have been fixed. |
|
||||
| Comparison of narrow type with wide type in loop condition (`cpp/comparison-with-wider-type`) | Higher precision | The precision of this query has been increased to "high" as the alerts from this query have proved to be valuable on real-world projects. With this precision, results are now displayed by default in LGTM. |
|
||||
| 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 QL libraries
|
||||
## Changes to libraries
|
||||
|
||||
* 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
|
||||
@@ -39,6 +42,10 @@ The following changes in version 1.23 affect C/C++ analysis in all applications.
|
||||
definition of `x` when `x` is a variable of pointer type. It no longer
|
||||
considers deep paths such as `f(&x.myField)` to be definitions of `x`. These
|
||||
changes are in line with the user expectations we've observed.
|
||||
* The data-flow library now makes it easier to specify barriers/sanitizers
|
||||
arising from guards by overriding the predicate
|
||||
`isBarrierGuard`/`isSanitizerGuard` on data-flow and taint-tracking
|
||||
configurations respectively.
|
||||
* There is now a `DataFlow::localExprFlow` predicate and a
|
||||
`TaintTracking::localExprTaint` predicate to make it easy to use the most
|
||||
common case of local data flow and taint: from one `Expr` to another.
|
||||
@@ -46,7 +53,14 @@ The following changes in version 1.23 affect C/C++ analysis in all applications.
|
||||
clarity (e.g. `isOutReturnPointer()` to `isReturnValueDeref()`). The existing member predicates
|
||||
have been deprecated, and will be removed in a future release. Code that uses the old member
|
||||
predicates should be updated to use the corresponding new member predicate.
|
||||
* The predicates `Declaration.hasStdName()` and `Declaration.hasGlobalOrStdName`
|
||||
have been added, simplifying handling of C++ standard library functions.
|
||||
* The control-flow graph is now computed in QL, not in the extractor. This can
|
||||
lead to regressions (or improvements) in how queries are optimized because
|
||||
optimization in QL relies on static size estimates, and the control-flow edge
|
||||
relations will now have different size estimates than before.
|
||||
* Support has been added for non-type template arguments. This means that the
|
||||
return type of `Declaration::getTemplateArgument()` and
|
||||
`Declaration::getATemplateArgument` have changed to `Locatable`. See the
|
||||
documentation for `Declaration::getTemplateArgument()` and
|
||||
`Declaration::getTemplateArgumentKind()` for details.
|
||||
|
||||
@@ -8,7 +8,10 @@ The following changes in version 1.23 affect C# analysis in all applications.
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
| Deserialized delegate (`cs/deserialized-delegate`) | security, external/cwe/cwe-502 | Finds unsafe deserialization of delegate types. |
|
||||
| Deserialization of untrusted data (`cs/unsafe-deserialization-untrusted-input`) | security, external/cwe/cwe-502 | Finds flow of untrusted input to calls to unsafe deserializers. |
|
||||
| Unsafe year argument for 'DateTime' constructor (`cs/unsafe-year-construction`) | reliability, date-time | Finds incorrect manipulation of `DateTime` values, which could lead to invalid dates. |
|
||||
| Unsafe deserializer (`cs/unsafe-deserialization`) | security, external/cwe/cwe-502 | Finds calls to unsafe deserializers. |
|
||||
| Mishandling the Japanese era start date (`cs/mishandling-japanese-era`) | reliability, date-time | Finds hard-coded Japanese era start dates that could be invalid. |
|
||||
|
||||
## Changes to existing queries
|
||||
@@ -24,7 +27,7 @@ The following changes in version 1.23 affect C# analysis in all applications.
|
||||
|
||||
* `nameof` expressions are now extracted correctly when the name is a namespace.
|
||||
|
||||
## Changes to QL libraries
|
||||
## Changes to libraries
|
||||
|
||||
* The new class `NamespaceAccess` models accesses to namespaces, for example in `nameof` expressions.
|
||||
* The data-flow library now makes it easier to specify barriers/sanitizers
|
||||
@@ -43,5 +46,7 @@ The following changes in version 1.23 affect C# analysis in all applications.
|
||||
* There is now a `DataFlow::localExprFlow` predicate and a
|
||||
`TaintTracking::localExprTaint` predicate to make it easy to use the most
|
||||
common case of local data flow and taint: from one `Expr` to another.
|
||||
* Data is now tracked through null-coalescing expressions (`??`).
|
||||
* A new library `semmle.code.csharp.Unification` has been added. This library exposes two predicates `unifiable` and `subsumes` for calculating type unification and type subsumption, respectively.
|
||||
|
||||
## Changes to autobuilder
|
||||
|
||||
@@ -19,7 +19,7 @@ The following changes in version 1.23 affect Java analysis in all applications.
|
||||
| Query built without neutralizing special characters (`java/concatenated-sql-query`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as SQL expressions sinks. |
|
||||
| Useless comparison test (`java/constant-comparison`) | Fewer false positives | Additional overflow check patterns are now recognized and no longer reported. |
|
||||
|
||||
## Changes to QL libraries
|
||||
## Changes to libraries
|
||||
|
||||
* 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
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
## General improvements
|
||||
|
||||
* Suppor for `globalThis` has been added.
|
||||
* Automatic classification of generated and minified files has been improved, in particular files generated by Doxygen are now recognized.
|
||||
|
||||
* Support for `globalThis` has been added.
|
||||
|
||||
* Support for the following frameworks and libraries has been improved:
|
||||
- [firebase](https://www.npmjs.com/package/firebase)
|
||||
@@ -12,8 +14,7 @@
|
||||
|
||||
* The call graph has been improved to resolve method calls in more cases. This may produce more security alerts.
|
||||
|
||||
* TypeScript 3.6 features are supported.
|
||||
|
||||
* TypeScript 3.6 and 3.7 features are now supported.
|
||||
|
||||
## New queries
|
||||
|
||||
@@ -26,6 +27,7 @@
|
||||
| Use of returnless function (`js/use-of-returnless-function`) | maintainability, correctness | Highlights calls where the return value is used, but the callee never returns a value. Results are shown on LGTM by default. |
|
||||
| Useless regular expression character escape (`js/useless-regexp-character-escape`) | correctness, security, external/cwe/cwe-20 | Highlights regular expression strings with useless character escapes, indicating a possible violation of [CWE-20](https://cwe.mitre.org/data/definitions/20.html). Results are shown on LGTM by default. |
|
||||
| Unreachable method overloads (`js/unreachable-method-overloads`) | correctness, typescript | Highlights method overloads that are impossible to use from client code. Results are shown on LGTM by default. |
|
||||
| Ignoring result from pure array method (`js/ignore-array-result`) | maintainability, correctness | Highlights calls to array methods without side effects where the return value is ignored. Results are shown on LGTM by default. |
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
@@ -44,8 +46,9 @@
|
||||
| Stored cross-site scripting (`js/stored-xss`) | Fewer false-positive results | The query now recognizes more sanitizers. |
|
||||
| Uncontrolled command line (`js/command-line-injection`) | More results | This query now treats responses from servers as untrusted. |
|
||||
| Uncontrolled data used in path expression (`js/path-injection`) | Fewer false-positive results | This query now recognizes calls to Express `sendFile` as safe in some cases. |
|
||||
| Unknown directive (`js/unknown-directive`) | Fewer false positive results | This query no longer flags uses of ":", which is sometimes used like a directive. |
|
||||
|
||||
## Changes to QL libraries
|
||||
## Changes to libraries
|
||||
|
||||
* `Expr.getDocumentation()` now handles chain assignments.
|
||||
|
||||
|
||||
@@ -20,3 +20,8 @@
|
||||
|----------------------------|------------------------|------------|
|
||||
| Unreachable code | Fewer false positives | Analysis now accounts for uses of `contextlib.suppress` to suppress exceptions. |
|
||||
| `__iter__` method returns a non-iterator | Better alert message | Alert now highlights which class is expected to be an iterator. |
|
||||
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
* Django library now recognizes positional arguments from a `django.conf.urls.url` regex (Django version 1.x)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Files moved to ``docs`` directory
|
||||
|
||||
Now that all of the QL documentation is in this repository,
|
||||
Now that all of the CodeQL documentation is in this repository,
|
||||
notes on the languages, compilers, and frameworks supported have moved.
|
||||
They're now stored as part of the Sphinx ``support`` project with the other documentation:
|
||||
``docs/language/support``.
|
||||
|
||||
@@ -21,6 +21,7 @@ from Variable v
|
||||
where
|
||||
v.isStatic() and
|
||||
v.hasDefinition() and
|
||||
not v.isConstexpr() and
|
||||
not exists(VariableAccess a | a.getTarget() = v) and
|
||||
not v instanceof MemberVariable and
|
||||
not declarationHasSideEffects(v) and
|
||||
|
||||
@@ -13,7 +13,7 @@ import semmle.code.cpp.pointsto.PointsTo
|
||||
import Negativity
|
||||
|
||||
predicate closeCall(FunctionCall fc, Variable v) {
|
||||
fc.getTarget().hasGlobalName("close") and v.getAnAccess() = fc.getArgument(0)
|
||||
fc.getTarget().hasGlobalOrStdName("close") and v.getAnAccess() = fc.getArgument(0)
|
||||
or
|
||||
exists(FunctionCall midcall, Function mid, int arg |
|
||||
fc.getArgument(arg) = v.getAnAccess() and
|
||||
|
||||
@@ -13,7 +13,7 @@ import semmle.code.cpp.pointsto.PointsTo
|
||||
|
||||
predicate closed(Expr e) {
|
||||
exists(FunctionCall fc |
|
||||
fc.getTarget().hasGlobalName("close") and
|
||||
fc.getTarget().hasGlobalOrStdName("close") and
|
||||
fc.getArgument(0) = e
|
||||
)
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ predicate allocCallOrIndirect(Expr e) {
|
||||
* can cause memory leaks.
|
||||
*/
|
||||
predicate verifiedRealloc(FunctionCall reallocCall, Variable v, ControlFlowNode verified) {
|
||||
reallocCall.getTarget().hasGlobalName("realloc") and
|
||||
reallocCall.getTarget().hasGlobalOrStdName("realloc") and
|
||||
reallocCall.getArgument(0) = v.getAnAccess() and
|
||||
(
|
||||
exists(Variable newV, ControlFlowNode node |
|
||||
@@ -79,7 +79,7 @@ predicate verifiedRealloc(FunctionCall reallocCall, Variable v, ControlFlowNode
|
||||
predicate freeCallOrIndirect(ControlFlowNode n, Variable v) {
|
||||
// direct free call
|
||||
freeCall(n, v.getAnAccess()) and
|
||||
not n.(FunctionCall).getTarget().hasGlobalName("realloc")
|
||||
not n.(FunctionCall).getTarget().hasGlobalOrStdName("realloc")
|
||||
or
|
||||
// verified realloc call
|
||||
verifiedRealloc(_, v, n)
|
||||
|
||||
@@ -13,10 +13,7 @@
|
||||
import cpp
|
||||
|
||||
class MallocCall extends FunctionCall {
|
||||
MallocCall() {
|
||||
this.getTarget().hasGlobalName("malloc") or
|
||||
this.getTarget().hasQualifiedName("std", "malloc")
|
||||
}
|
||||
MallocCall() { this.getTarget().hasGlobalOrStdName("malloc") }
|
||||
|
||||
Expr getAllocatedSize() {
|
||||
if this.getArgument(0) instanceof VariableAccess
|
||||
@@ -36,12 +33,12 @@ predicate spaceProblem(FunctionCall append, string msg) {
|
||||
malloc.getAllocatedSize() = add and
|
||||
buffer.getAnAccess() = strlen.getStringExpr() and
|
||||
(
|
||||
insert.getTarget().hasGlobalName("strcpy") or
|
||||
insert.getTarget().hasGlobalName("strncpy")
|
||||
insert.getTarget().hasGlobalOrStdName("strcpy") or
|
||||
insert.getTarget().hasGlobalOrStdName("strncpy")
|
||||
) and
|
||||
(
|
||||
append.getTarget().hasGlobalName("strcat") or
|
||||
append.getTarget().hasGlobalName("strncat")
|
||||
append.getTarget().hasGlobalOrStdName("strcat") or
|
||||
append.getTarget().hasGlobalOrStdName("strncat")
|
||||
) and
|
||||
malloc.getASuccessor+() = insert and
|
||||
insert.getArgument(1) = buffer.getAnAccess() and
|
||||
|
||||
@@ -25,7 +25,7 @@ import semmle.code.cpp.security.TaintTracking
|
||||
predicate sourceSized(FunctionCall fc, Expr src) {
|
||||
exists(string name |
|
||||
(name = "strncpy" or name = "strncat" or name = "memcpy" or name = "memmove") and
|
||||
fc.getTarget().hasGlobalName(name)
|
||||
fc.getTarget().hasGlobalOrStdName(name)
|
||||
) and
|
||||
exists(Expr dest, Expr size, Variable v |
|
||||
fc.getArgument(0) = dest and
|
||||
|
||||
@@ -60,19 +60,19 @@ predicate overflowOffsetInLoop(BufferAccess bufaccess, string msg) {
|
||||
predicate bufferAndSizeFunction(Function f, int buf, int size) {
|
||||
f.hasGlobalName("read") and buf = 1 and size = 2
|
||||
or
|
||||
f.hasGlobalName("fgets") and buf = 0 and size = 1
|
||||
f.hasGlobalOrStdName("fgets") and buf = 0 and size = 1
|
||||
or
|
||||
f.hasGlobalName("strncpy") and buf = 0 and size = 2
|
||||
f.hasGlobalOrStdName("strncpy") and buf = 0 and size = 2
|
||||
or
|
||||
f.hasGlobalName("strncat") and buf = 0 and size = 2
|
||||
f.hasGlobalOrStdName("strncat") and buf = 0 and size = 2
|
||||
or
|
||||
f.hasGlobalName("memcpy") and buf = 0 and size = 2
|
||||
f.hasGlobalOrStdName("memcpy") and buf = 0 and size = 2
|
||||
or
|
||||
f.hasGlobalName("memmove") and buf = 0 and size = 2
|
||||
f.hasGlobalOrStdName("memmove") and buf = 0 and size = 2
|
||||
or
|
||||
f.hasGlobalName("snprintf") and buf = 0 and size = 1
|
||||
f.hasGlobalOrStdName("snprintf") and buf = 0 and size = 1
|
||||
or
|
||||
f.hasGlobalName("vsnprintf") and buf = 0 and size = 1
|
||||
f.hasGlobalOrStdName("vsnprintf") and buf = 0 and size = 1
|
||||
}
|
||||
|
||||
class CallWithBufferSize extends FunctionCall {
|
||||
|
||||
@@ -17,12 +17,12 @@ import cpp
|
||||
class Allocation extends FunctionCall {
|
||||
Allocation() {
|
||||
exists(string name |
|
||||
this.getTarget().hasGlobalName(name) and
|
||||
this.getTarget().hasGlobalOrStdName(name) and
|
||||
(name = "malloc" or name = "calloc" or name = "realloc")
|
||||
)
|
||||
}
|
||||
|
||||
private string getName() { this.getTarget().hasGlobalName(result) }
|
||||
private string getName() { this.getTarget().hasGlobalOrStdName(result) }
|
||||
|
||||
int getSize() {
|
||||
this.getName() = "malloc" and
|
||||
|
||||
@@ -17,12 +17,12 @@ import cpp
|
||||
class Allocation extends FunctionCall {
|
||||
Allocation() {
|
||||
exists(string name |
|
||||
this.getTarget().hasGlobalName(name) and
|
||||
this.getTarget().hasGlobalOrStdName(name) and
|
||||
(name = "malloc" or name = "calloc" or name = "realloc")
|
||||
)
|
||||
}
|
||||
|
||||
private string getName() { this.getTarget().hasGlobalName(result) }
|
||||
private string getName() { this.getTarget().hasGlobalOrStdName(result) }
|
||||
|
||||
int getSize() {
|
||||
this.getName() = "malloc" and
|
||||
|
||||
@@ -16,7 +16,7 @@ import semmle.code.cpp.controlflow.LocalScopeVariableReachability
|
||||
predicate isFreeExpr(Expr e, LocalScopeVariable v) {
|
||||
exists(VariableAccess va | va.getTarget() = v |
|
||||
exists(FunctionCall fc | fc = e |
|
||||
fc.getTarget().hasGlobalName("free") and
|
||||
fc.getTarget().hasGlobalOrStdName("free") and
|
||||
va = fc.getArgument(0)
|
||||
)
|
||||
or
|
||||
|
||||
@@ -59,7 +59,7 @@ class Options extends string {
|
||||
predicate exits(Function f) {
|
||||
f.getAnAttribute().hasName("noreturn")
|
||||
or
|
||||
exists(string name | f.hasGlobalName(name) |
|
||||
exists(string name | f.hasGlobalOrStdName(name) |
|
||||
name = "exit" or
|
||||
name = "_exit" or
|
||||
name = "abort" or
|
||||
@@ -91,7 +91,7 @@ class Options extends string {
|
||||
* By default holds only for `fgets`.
|
||||
*/
|
||||
predicate alwaysCheckReturnValue(Function f) {
|
||||
f.hasGlobalName("fgets") or
|
||||
f.hasGlobalOrStdName("fgets") or
|
||||
CustomOptions::alwaysCheckReturnValue(f) // old Options.qll
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ where
|
||||
pointlessSelfComparison(cmp) and
|
||||
not nanTest(cmp) and
|
||||
not overflowTest(cmp) and
|
||||
not cmp.isFromTemplateInstantiation(_) and
|
||||
not exists(MacroInvocation mi |
|
||||
// cmp is in mi
|
||||
mi.getAnExpandedElement() = cmp and
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
bool foo(int n1, unsigned short delta) {
|
||||
return n1 + delta < n1; // BAD
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
bool bar(unsigned short n1, unsigned short delta) {
|
||||
// NB: Comparison is always false
|
||||
return n1 + delta < n1; // GOOD (but misleading)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#include <limits.h>
|
||||
bool foo(int n1, unsigned short delta) {
|
||||
return n1 > INT_MAX - delta; // GOOD
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
bool bar(unsigned short n1, unsigned short delta) {
|
||||
return (unsigned short)(n1 + delta) < n1; // GOOD
|
||||
}
|
||||
115
cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.qhelp
Normal file
115
cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.qhelp
Normal file
@@ -0,0 +1,115 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
When checking for integer overflow, you may often write tests like
|
||||
<code>a + b < a</code>. This works fine if <code>a</code> or
|
||||
<code>b</code> are unsigned integers, since any overflow in the addition
|
||||
will cause the value to simply "wrap around." However, using
|
||||
<i>signed</i> integers is problematic because signed overflow has undefined
|
||||
behavior according to the C and C++ standards. If the addition overflows
|
||||
and has an undefined result, the comparison will likewise be undefined;
|
||||
it may produce an unintended result, or may be deleted entirely by an
|
||||
optimizing compiler.
|
||||
</p>
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>
|
||||
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
|
||||
but the signedness remains, or (2) rewrite (or cast) the signed expression
|
||||
into unsigned form.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Below we list examples of expressions where signed overflow may
|
||||
occur, along with proposed solutions. The list should not be
|
||||
considered exhaustive.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Given <code>unsigned short i, delta</code> and <code>i + delta < i</code>,
|
||||
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>
|
||||
Given <code>unsigned short i, delta</code> and <code>i + delta < i</code>,
|
||||
it is also possible to rewrite it as <code>USHORT_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 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>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
In the following example, even though <code>delta</code> has been declared
|
||||
<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,
|
||||
namely a <code>signed int</code>. Addition is performed on
|
||||
signed integers, and may have undefined behavior if an overflow occurs.
|
||||
As a result, the entire (comparison) expression may also have an undefined
|
||||
result.
|
||||
</p>
|
||||
<sample src="SignedOverflowCheck-bad1.cpp" />
|
||||
<p>
|
||||
The following example builds upon the previous one. Instead of
|
||||
performing an addition (which could overflow), we have re-framed the
|
||||
solution so that a subtraction is used instead. Since <code>delta</code>
|
||||
is promoted to a <code>signed int</code> and <code>INT_MAX</code> denotes
|
||||
the largest possible positive value for an <code>signed int</code>,
|
||||
the expression <code>INT_MAX - delta</code> can never be less than zero
|
||||
or more than <code>INT_MAX</code>. Hence, any overflow and underflow
|
||||
are avoided.
|
||||
</p>
|
||||
<sample src="SignedOverflowCheck-good1.cpp" />
|
||||
<p>
|
||||
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
|
||||
<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
|
||||
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
|
||||
hold true, which likely is not what the programmer intended. (see also the
|
||||
<code>cpp/bad-addition-overflow-check</code> query).
|
||||
</p>
|
||||
<sample src="SignedOverflowCheck-bad2.cpp" />
|
||||
<p>
|
||||
The next example provides a solution to the previous one. Even though
|
||||
<code>i + delta</code> does not overflow, casting it to an
|
||||
<code>unsigned short</code> truncates the addition modulo 2^16,
|
||||
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>,
|
||||
the right-hand side does not need to be promoted to a <code>signed int</code>.
|
||||
</p>
|
||||
|
||||
<sample src="SignedOverflowCheck-good2.cpp" />
|
||||
</example>
|
||||
<references>
|
||||
<li><a href="http://c-faq.com/expr/preservingrules.html">comp.lang.c FAQ list · Question 3.19 (Preserving rules)</a></li>
|
||||
<li><a href="https://wiki.sei.cmu.edu/confluence/display/c/INT31-C.+Ensure+that+integer+conversions+do+not+result+in+lost+or+misinterpreted+data">INT31-C. Ensure that integer conversions do not result in lost or misinterpreted data</a></li>
|
||||
<li>W. Dietz, P. Li, J. Regehr, V. Adve. <a href="https://www.cs.utah.edu/~regehr/papers/overflow12.pdf">Understanding Integer Overflow in C/C++</a></li>
|
||||
</references>
|
||||
</qhelp>
|
||||
31
cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.ql
Normal file
31
cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.ql
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* @name Undefined result of signed test for overflow
|
||||
* @description Testing for overflow by adding a value to a variable
|
||||
* to see if it "wraps around" works only for
|
||||
* unsigned integer values.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @id cpp/signed-overflow-check
|
||||
* @tags reliability
|
||||
* security
|
||||
*/
|
||||
|
||||
import cpp
|
||||
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
private import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
|
||||
from RelationalOperation ro, AddExpr add, Expr expr1, Expr expr2
|
||||
where
|
||||
ro.getAnOperand() = add and
|
||||
add.getAnOperand() = expr1 and
|
||||
ro.getAnOperand() = expr2 and
|
||||
globalValueNumber(expr1) = globalValueNumber(expr2) and
|
||||
add.getUnspecifiedType().(IntegralType).isSigned() and
|
||||
not exists(MacroInvocation mi | mi.getAnAffectedElement() = add) and
|
||||
exprMightOverflowPositively(add) and
|
||||
exists(Compilation c | c.getAFileCompiled() = ro.getFile() |
|
||||
not c.getAnArgument() = "-fwrapv" and
|
||||
not c.getAnArgument() = "-fno-strict-overflow"
|
||||
)
|
||||
select ro, "Testing for signed overflow may produce undefined results."
|
||||
@@ -1,33 +0,0 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
import BufferAccess
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
class NetworkFunctionCall extends FunctionCall {
|
||||
NetworkFunctionCall() {
|
||||
getTarget().hasName("ntohd") or
|
||||
getTarget().hasName("ntohf") or
|
||||
getTarget().hasName("ntohl") or
|
||||
getTarget().hasName("ntohll") or
|
||||
getTarget().hasName("ntohs")
|
||||
}
|
||||
}
|
||||
|
||||
class NetworkToBufferSizeConfiguration extends DataFlow::Configuration {
|
||||
NetworkToBufferSizeConfiguration() { this = "NetworkToBufferSizeConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof NetworkFunctionCall }
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
node.asExpr() = any(BufferAccess ba).getAccessedLength()
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node) {
|
||||
exists(GuardCondition gc, GVN gvn |
|
||||
gc.getAChild*() = gvn.getAnExpr() and
|
||||
globalValueNumber(node.asExpr()) = gvn and
|
||||
gc.controls(node.asExpr().getBasicBlock(), _)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ byte order function, such as <code>ntohl</code>.
|
||||
The use of a network-to-host byte order function is therefore a good indicator that the returned
|
||||
value is unvalidated data retrieved from the network, and should not be used without further
|
||||
validation. In particular, the returned value should not be used as an array index or array length
|
||||
value without validation, which may result in a buffer overflow vulnerability.
|
||||
value without validation, as this could result in a buffer overflow vulnerability.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
@@ -31,10 +31,10 @@ it to host byte order. The data is then used as an index in an array access expr
|
||||
there is no validation that the data returned by <code>ntohl</code> is within the bounds of the array,
|
||||
which could lead to reading outside the bounds of the buffer.
|
||||
</p>
|
||||
<sample src="NtohlArrayBad.cpp" />
|
||||
<sample src="NtohlArrayNoBound-bad.cpp" />
|
||||
<p>In the corrected example, the returned data is validated against the known size of the buffer,
|
||||
before being used as an array index.</p>
|
||||
<sample src="NtohlArrayGood.cpp" />
|
||||
<sample src="NtohlArrayNoBound-good.cpp" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
@@ -1,55 +1,7 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
private import semmle.code.cpp.dataflow.RecursionPrevention
|
||||
|
||||
/**
|
||||
* A buffer which includes an allocation size.
|
||||
*/
|
||||
abstract class BufferWithSize extends DataFlow::Node {
|
||||
abstract Expr getSizeExpr();
|
||||
|
||||
BufferAccess getAnAccess() {
|
||||
any(BufferWithSizeConfig bsc).hasFlow(this, DataFlow::exprNode(result.getPointer()))
|
||||
}
|
||||
}
|
||||
|
||||
/** An allocation function. */
|
||||
abstract class Alloc extends Function { }
|
||||
|
||||
/**
|
||||
* Allocation functions identified by the QL for C/C++ standard library.
|
||||
*/
|
||||
class DefaultAlloc extends Alloc {
|
||||
DefaultAlloc() { allocationFunction(this) }
|
||||
}
|
||||
|
||||
/** A buffer created through a call to an allocation function. */
|
||||
class AllocBuffer extends BufferWithSize {
|
||||
FunctionCall call;
|
||||
|
||||
AllocBuffer() {
|
||||
asExpr() = call and
|
||||
call.getTarget() instanceof Alloc
|
||||
}
|
||||
|
||||
override Expr getSizeExpr() { result = call.getArgument(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Find accesses of buffers for which we have a size expression.
|
||||
*/
|
||||
private class BufferWithSizeConfig extends TaintTracking::Configuration {
|
||||
BufferWithSizeConfig() { this = "BufferWithSize" }
|
||||
|
||||
override predicate isSource(DataFlow::Node n) { n = any(BufferWithSize b) }
|
||||
|
||||
override predicate isSink(DataFlow::Node n) { n.asExpr() = any(BufferAccess ae).getPointer() }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node s) {
|
||||
s = any(BufferWithSize b) and
|
||||
s.asExpr().getControlFlowScope() instanceof Alloc
|
||||
}
|
||||
}
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
/**
|
||||
* An access (read or write) to a buffer, provided as a pair of
|
||||
@@ -172,3 +124,31 @@ class MallocSizeExpr extends BufferAccess, FunctionCall {
|
||||
|
||||
override Expr getAccessedLength() { result = getArgument(1) }
|
||||
}
|
||||
|
||||
class NetworkFunctionCall extends FunctionCall {
|
||||
NetworkFunctionCall() {
|
||||
getTarget().hasName("ntohd") or
|
||||
getTarget().hasName("ntohf") or
|
||||
getTarget().hasName("ntohl") or
|
||||
getTarget().hasName("ntohll") or
|
||||
getTarget().hasName("ntohs")
|
||||
}
|
||||
}
|
||||
|
||||
class NetworkToBufferSizeConfiguration extends DataFlow::Configuration {
|
||||
NetworkToBufferSizeConfiguration() { this = "NetworkToBufferSizeConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof NetworkFunctionCall }
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
node.asExpr() = any(BufferAccess ba).getAccessedLength()
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node) {
|
||||
exists(GuardCondition gc, GVN gvn |
|
||||
gc.getAChild*() = gvn.getAnExpr() and
|
||||
globalValueNumber(node.asExpr()) = gvn and
|
||||
gc.controls(node.asExpr().getBasicBlock(), _)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -13,32 +13,33 @@ import semmle.code.cpp.security.boostorg.asio.protocols
|
||||
class ExistsAnyFlowConfig extends DataFlow::Configuration {
|
||||
ExistsAnyFlowConfig() { this = "ExistsAnyFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { any() }
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(BoostorgAsio::SslContextClass c | c.getAContructorCall() = source.asExpr())
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { any() }
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(BoostorgAsio::SslSetOptionsFunction f, FunctionCall fcSetOptions |
|
||||
f.getACallToThisFunction() = fcSetOptions and
|
||||
fcSetOptions.getQualifier() = sink.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
bindingset[flag]
|
||||
predicate isOptionSet(ConstructorCall cc, int flag, FunctionCall fcSetOptions) {
|
||||
exists(
|
||||
BoostorgAsio::SslContextFlowsToSetOptionConfig config, ExistsAnyFlowConfig testConfig,
|
||||
Expr optionsSink
|
||||
|
|
||||
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
|
||||
exists(VariableAccess contextSetOptions |
|
||||
testConfig.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(contextSetOptions)) and
|
||||
exists(BoostorgAsio::SslSetOptionsFunction f | f.getACallToThisFunction() = fcSetOptions |
|
||||
contextSetOptions = fcSetOptions.getQualifier() and
|
||||
forall(
|
||||
Expr optionArgument, BoostorgAsio::SslOptionConfig optionArgConfig,
|
||||
Expr optionArgumentSource
|
||||
|
|
||||
optionArgument = fcSetOptions.getArgument(0) and
|
||||
optionArgConfig
|
||||
.hasFlow(DataFlow::exprNode(optionArgumentSource), DataFlow::exprNode(optionArgument))
|
||||
|
|
||||
optionArgument.getValue().toInt().bitShiftRight(16).bitAnd(flag) = flag
|
||||
)
|
||||
exists(ExistsAnyFlowConfig anyFlowConfig, VariableAccess contextSetOptions |
|
||||
anyFlowConfig.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(contextSetOptions)) and
|
||||
exists(BoostorgAsio::SslSetOptionsFunction f | f.getACallToThisFunction() = fcSetOptions |
|
||||
contextSetOptions = fcSetOptions.getQualifier() and
|
||||
forall(
|
||||
Expr optionArgument, BoostorgAsio::SslOptionConfig optionArgConfig,
|
||||
Expr optionArgumentSource
|
||||
|
|
||||
optionArgument = fcSetOptions.getArgument(0) and
|
||||
optionArgConfig
|
||||
.hasFlow(DataFlow::exprNode(optionArgumentSource), DataFlow::exprNode(optionArgument))
|
||||
|
|
||||
optionArgument.getValue().toInt().bitShiftRight(16).bitAnd(flag) = flag
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -46,43 +47,18 @@ predicate isOptionSet(ConstructorCall cc, int flag, FunctionCall fcSetOptions) {
|
||||
|
||||
bindingset[flag]
|
||||
predicate isOptionNotSet(ConstructorCall cc, int flag) {
|
||||
not exists(
|
||||
BoostorgAsio::SslContextFlowsToSetOptionConfig config, ExistsAnyFlowConfig testConfig,
|
||||
Expr optionsSink
|
||||
|
|
||||
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
|
||||
exists(VariableAccess contextSetOptions |
|
||||
testConfig.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(contextSetOptions)) and
|
||||
exists(FunctionCall fcSetOptions, BoostorgAsio::SslSetOptionsFunction f |
|
||||
f.getACallToThisFunction() = fcSetOptions
|
||||
|
|
||||
contextSetOptions = fcSetOptions.getQualifier() and
|
||||
forall(
|
||||
Expr optionArgument, BoostorgAsio::SslOptionConfig optionArgConfig,
|
||||
Expr optionArgumentSource
|
||||
|
|
||||
optionArgument = fcSetOptions.getArgument(0) and
|
||||
optionArgConfig
|
||||
.hasFlow(DataFlow::exprNode(optionArgumentSource), DataFlow::exprNode(optionArgument))
|
||||
|
|
||||
optionArgument.getValue().toInt().bitShiftRight(16).bitAnd(flag) = flag
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
not exists(FunctionCall fcSetOptions | isOptionSet(cc, flag, fcSetOptions))
|
||||
}
|
||||
|
||||
from
|
||||
BoostorgAsio::SslContextCallTlsProtocolConfig configConstructor,
|
||||
BoostorgAsio::SslContextFlowsToSetOptionConfig config, Expr protocolSource, Expr protocolSink,
|
||||
ConstructorCall cc, Expr e, string msg
|
||||
BoostorgAsio::SslContextCallTlsProtocolConfig configConstructor, Expr protocolSource,
|
||||
Expr protocolSink, ConstructorCall cc, Expr e, string msg
|
||||
where
|
||||
configConstructor.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink)) and
|
||||
cc.getArgument(0) = protocolSink and
|
||||
(
|
||||
BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
|
||||
not exists(Expr optionsSink |
|
||||
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
|
||||
not (
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoSsl3(), _) and
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1(), _) and
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1(), _) and
|
||||
@@ -91,8 +67,7 @@ where
|
||||
or
|
||||
BoostorgAsio::isExprTlsBoostProtocol(protocolSource) and
|
||||
not BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
|
||||
not exists(Expr optionsSink |
|
||||
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
|
||||
not (
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1(), _) and
|
||||
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1(), _) and
|
||||
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2())
|
||||
@@ -7,6 +7,10 @@
|
||||
* @id cpp/ignore-return-value-sal
|
||||
* @problem.severity warning
|
||||
* @tags reliability
|
||||
* external/cwe/cwe-573
|
||||
* external/cwe/cwe-252
|
||||
* @opaque-id SM02344
|
||||
* @microsoft.severity Important
|
||||
*/
|
||||
|
||||
import SAL
|
||||
|
||||
@@ -126,7 +126,7 @@ class SALParameter extends Parameter {
|
||||
}
|
||||
|
||||
/**
|
||||
* A SAL element, i.e. a SAL annotation or a declaration entry
|
||||
* A SAL element, that is, a SAL annotation or a declaration entry
|
||||
* that may have SAL annotations.
|
||||
*/
|
||||
library class SALElement extends Element {
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* @name SAL requires inspecting return value
|
||||
* @description When a return value is discarded even though the SAL annotation
|
||||
* requires inspecting it, a recoverable error may turn into a
|
||||
* whole-program crash.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @tags reliability
|
||||
* external/cwe/cwe-573
|
||||
* external/cwe/cwe-252
|
||||
* @opaque-id SM02344
|
||||
* @microsoft.severity Important
|
||||
* @id cpp/ignorereturnvaluesal
|
||||
*/
|
||||
|
||||
import Microsoft.SAL
|
||||
|
||||
from Function f, FunctionCall call
|
||||
where
|
||||
call.getTarget() = f and
|
||||
call instanceof ExprInVoidContext and
|
||||
any(SALCheckReturn a).getDeclaration() = f and
|
||||
not any(Options o).okToIgnoreReturnValue(call)
|
||||
select call, "Return value of $@ discarded although a SAL annotation " + "requires inspecting it.",
|
||||
f, f.getName()
|
||||
@@ -34,8 +34,10 @@ class FileFunction extends FunctionWithWrappers {
|
||||
nme.matches("CreateFile%")
|
||||
)
|
||||
or
|
||||
this.hasQualifiedName("std", "fopen")
|
||||
or
|
||||
// on any of the fstream classes, or filebuf
|
||||
exists(string nme | this.getDeclaringType().getSimpleName() = nme |
|
||||
exists(string nme | this.getDeclaringType().hasQualifiedName("std", nme) |
|
||||
nme = "basic_fstream" or
|
||||
nme = "basic_ifstream" or
|
||||
nme = "basic_ofstream" or
|
||||
|
||||
@@ -34,7 +34,7 @@ characters before writing to the HTML page.</p>
|
||||
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet">XSS
|
||||
<a href="https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html">XSS
|
||||
(Cross Site Scripting) Prevention Cheat Sheet</a>.
|
||||
</li>
|
||||
<li>
|
||||
|
||||
@@ -17,8 +17,8 @@ import semmle.code.cpp.security.TaintTracking
|
||||
/** A call that prints its arguments to `stdout`. */
|
||||
class PrintStdoutCall extends FunctionCall {
|
||||
PrintStdoutCall() {
|
||||
getTarget().hasGlobalName("puts") or
|
||||
getTarget().hasGlobalName("printf")
|
||||
getTarget().hasGlobalOrStdName("puts") or
|
||||
getTarget().hasGlobalOrStdName("printf")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,7 @@ import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.models.implementations.Memcpy
|
||||
|
||||
class MallocCall extends FunctionCall {
|
||||
MallocCall() {
|
||||
this.getTarget().hasGlobalName("malloc") or
|
||||
this.getTarget().hasQualifiedName("std", "malloc")
|
||||
}
|
||||
MallocCall() { this.getTarget().hasGlobalOrStdName("malloc") }
|
||||
|
||||
Expr getAllocatedSize() {
|
||||
if this.getArgument(0) instanceof VariableAccess
|
||||
|
||||
@@ -37,7 +37,7 @@ which is then subsequently accessed to fetch properties of the device. However,
|
||||
check the return value from the function call to <code>initDeviceConfig</code>. If the
|
||||
device number passed to the <code>notify</code> function was invalid, the
|
||||
<code>initDeviceConfig</code> function will leave the <code>config</code> variable uninitialized,
|
||||
which would result in the <code>notify</code> function accessing uninitialized memory.</p>
|
||||
which will result in the <code>notify</code> function accessing uninitialized memory.</p>
|
||||
|
||||
<sample src="ConditionallyUninitializedVariableBad.c" />
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @name Conditionally uninitialized variable
|
||||
* @description When an initialization function is used to initialize a local variable, but the
|
||||
* returned status code is not checked, the variable may be left in an uninitialized
|
||||
* state, and reading the variable may result in undefined behaviour.
|
||||
* state, and reading the variable may result in undefined behavior.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @opaque-id SM02313
|
||||
|
||||
@@ -19,7 +19,7 @@ int notify(int deviceNumber) {
|
||||
DeviceConfig config;
|
||||
initDeviceConfig(&config, deviceNumber);
|
||||
// BAD: Using config without checking the status code that is returned
|
||||
if (config->isEnabled) {
|
||||
notifyChannel(config->channel);
|
||||
if (config.isEnabled) {
|
||||
notifyChannel(config.channel);
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,8 @@ void notify(int deviceNumber) {
|
||||
int statusCode = initDeviceConfig(&config, deviceNumber);
|
||||
if (statusCode == 0) {
|
||||
// GOOD: Status code returned by initialization function is checked, so this is safe
|
||||
if (config->isEnabled) {
|
||||
notifyChannel(config->channel);
|
||||
if (config.isEnabled) {
|
||||
notifyChannel(config.channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import cpp
|
||||
import external.ExternalArtifact
|
||||
private import semmle.code.cpp.dispatch.VirtualDispatch
|
||||
private import semmle.code.cpp.dispatch.VirtualDispatchPrototype
|
||||
import semmle.code.cpp.NestedFields
|
||||
import Microsoft.SAL
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
@@ -89,9 +89,9 @@ class ParameterNullCheck extends ParameterCheck {
|
||||
(
|
||||
va = this.(NotExpr).getOperand() or
|
||||
va = any(EQExpr eq | eq = this and eq.getAnOperand().getValue() = "0").getAnOperand() or
|
||||
va = getAssertedFalseCondition(this) or
|
||||
va = getCheckedFalseCondition(this) or
|
||||
va = any(NEExpr eq |
|
||||
eq = getAssertedFalseCondition(this) and eq.getAnOperand().getValue() = "0"
|
||||
eq = getCheckedFalseCondition(this) and eq.getAnOperand().getValue() = "0"
|
||||
).getAnOperand()
|
||||
)
|
||||
or
|
||||
@@ -101,7 +101,7 @@ class ParameterNullCheck extends ParameterCheck {
|
||||
va = this or
|
||||
va = any(NEExpr eq | eq = this and eq.getAnOperand().getValue() = "0").getAnOperand() or
|
||||
va = any(EQExpr eq |
|
||||
eq = getAssertedFalseCondition(this) and eq.getAnOperand().getValue() = "0"
|
||||
eq = getCheckedFalseCondition(this) and eq.getAnOperand().getValue() = "0"
|
||||
).getAnOperand()
|
||||
)
|
||||
)
|
||||
@@ -567,7 +567,7 @@ Expr getAnInitializedArgument(Call call) { result = call.getArgument(initialized
|
||||
* the call, under the given context and evidence.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
int conditionallyInitializedArgument(
|
||||
private int conditionallyInitializedArgument(
|
||||
Call call, ConditionalInitializationFunction target, Context c, Evidence e
|
||||
) {
|
||||
target = getTarget(call) and
|
||||
@@ -588,7 +588,7 @@ Expr getAConditionallyInitializedArgument(
|
||||
/**
|
||||
* Gets the type signature for the functions parameters.
|
||||
*/
|
||||
string typeSig(Function f) {
|
||||
private string typeSig(Function f) {
|
||||
result = concat(int i, Type pt |
|
||||
pt = f.getParameter(i).getType()
|
||||
|
|
||||
@@ -599,7 +599,7 @@ string typeSig(Function f) {
|
||||
/**
|
||||
* Holds where qualifiedName and typeSig make up the signature for the function.
|
||||
*/
|
||||
predicate functionSignature(Function f, string qualifiedName, string typeSig) {
|
||||
private predicate functionSignature(Function f, string qualifiedName, string typeSig) {
|
||||
qualifiedName = f.getQualifiedName() and
|
||||
typeSig = typeSig(f)
|
||||
}
|
||||
@@ -611,7 +611,7 @@ predicate functionSignature(Function f, string qualifiedName, string typeSig) {
|
||||
* This is useful for identifying call to target dependencies across libraries, where the libraries
|
||||
* are never statically linked together.
|
||||
*/
|
||||
Function getAPossibleDefinition(Function undefinedFunction) {
|
||||
private Function getAPossibleDefinition(Function undefinedFunction) {
|
||||
not undefinedFunction.isDefined() and
|
||||
exists(string qn, string typeSig |
|
||||
functionSignature(undefinedFunction, qn, typeSig) and functionSignature(result, qn, typeSig)
|
||||
@@ -620,32 +620,47 @@ Function getAPossibleDefinition(Function undefinedFunction) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible target for the Call, using the name and parameter matching if we did not associate
|
||||
* Helper predicate for `getTarget`, that computes possible targets of a `Call`.
|
||||
*
|
||||
* If there is at least one defined target after performing some simple virtual dispatch
|
||||
* resolution, then the result is all the defined targets.
|
||||
*/
|
||||
private Function getTarget1(Call c) {
|
||||
result = VirtualDispatch::getAViableTarget(c) and
|
||||
result.isDefined()
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper predicate for `getTarget`, that computes possible targets of a `Call`.
|
||||
*
|
||||
* If we can use the heuristic matching of functions to find definitions for some of the viable
|
||||
* targets, return those.
|
||||
*/
|
||||
private Function getTarget2(Call c) {
|
||||
not exists(getTarget1(c)) and
|
||||
result = getAPossibleDefinition(VirtualDispatch::getAViableTarget(c))
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper predicate for `getTarget`, that computes possible targets of a `Call`.
|
||||
*
|
||||
* Otherwise, the result is the undefined `Function` instances.
|
||||
*/
|
||||
private Function getTarget3(Call c) {
|
||||
not exists(getTarget1(c)) and
|
||||
not exists(getTarget2(c)) and
|
||||
result = VirtualDispatch::getAViableTarget(c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible target for the `Call`, using the name and parameter matching if we did not associate
|
||||
* this call with a specific definition at link or compile time, and performing simple virtual
|
||||
* dispatch resolution.
|
||||
*/
|
||||
Function getTarget(Call c) {
|
||||
if VirtualDispatch::getAViableTarget(c).isDefined()
|
||||
then
|
||||
/*
|
||||
* If there is at least one defined target after performing some simple virtual dispatch
|
||||
* resolution, then the result is all the defined targets.
|
||||
*/
|
||||
|
||||
result = VirtualDispatch::getAViableTarget(c) and
|
||||
result.isDefined()
|
||||
else
|
||||
if exists(getAPossibleDefinition(VirtualDispatch::getAViableTarget(c)))
|
||||
then
|
||||
/*
|
||||
* If we can use the heuristic matching of functions to find definitions for some of the viable
|
||||
* targets, return those.
|
||||
*/
|
||||
|
||||
result = getAPossibleDefinition(VirtualDispatch::getAViableTarget(c))
|
||||
else
|
||||
// Otherwise, the result is the undefined `Function` instances
|
||||
result = VirtualDispatch::getAViableTarget(c)
|
||||
result = getTarget1(c) or
|
||||
result = getTarget2(c) or
|
||||
result = getTarget3(c)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -669,7 +684,7 @@ FieldAccess getAFieldAccess(Variable v) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a condition which is asserted to be false by the given `ne` expression, according to this pattern:
|
||||
* Gets a condition which is checked to be false by the given `ne` expression, according to this pattern:
|
||||
* ```
|
||||
* int a = !!result;
|
||||
* if (!a) { // <- ne
|
||||
@@ -677,7 +692,7 @@ FieldAccess getAFieldAccess(Variable v) {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
Expr getAssertedFalseCondition(NotExpr ne) {
|
||||
private Expr getCheckedFalseCondition(NotExpr ne) {
|
||||
exists(LocalVariable v |
|
||||
result = v.getInitializer().getExpr().(NotExpr).getOperand().(NotExpr).getOperand() and
|
||||
ne.getOperand() = v.getAnAccess() and
|
||||
|
||||
@@ -190,11 +190,11 @@ private predicate windowsSystemInfo(FunctionCall source, Element use) {
|
||||
// void WINAPI GetSystemInfo(_Out_ LPSYSTEM_INFO lpSystemInfo);
|
||||
// void WINAPI GetNativeSystemInfo(_Out_ LPSYSTEM_INFO lpSystemInfo);
|
||||
(
|
||||
source.getTarget().hasName("GetVersionEx") or
|
||||
source.getTarget().hasName("GetVersionExA") or
|
||||
source.getTarget().hasName("GetVersionExW") or
|
||||
source.getTarget().hasName("GetSystemInfo") or
|
||||
source.getTarget().hasName("GetNativeSystemInfo")
|
||||
source.getTarget().hasGlobalName("GetVersionEx") or
|
||||
source.getTarget().hasGlobalName("GetVersionExA") or
|
||||
source.getTarget().hasGlobalName("GetVersionExW") or
|
||||
source.getTarget().hasGlobalName("GetSystemInfo") or
|
||||
source.getTarget().hasGlobalName("GetNativeSystemInfo")
|
||||
) and
|
||||
use = source.getArgument(0)
|
||||
}
|
||||
@@ -216,9 +216,9 @@ private predicate windowsFolderPath(FunctionCall source, Element use) {
|
||||
// _In_ BOOL fCreate
|
||||
// );
|
||||
(
|
||||
source.getTarget().hasName("SHGetSpecialFolderPath") or
|
||||
source.getTarget().hasName("SHGetSpecialFolderPathA") or
|
||||
source.getTarget().hasName("SHGetSpecialFolderPathW")
|
||||
source.getTarget().hasGlobalName("SHGetSpecialFolderPath") or
|
||||
source.getTarget().hasGlobalName("SHGetSpecialFolderPathA") or
|
||||
source.getTarget().hasGlobalName("SHGetSpecialFolderPathW")
|
||||
) and
|
||||
use = source.getArgument(1)
|
||||
or
|
||||
@@ -228,7 +228,7 @@ private predicate windowsFolderPath(FunctionCall source, Element use) {
|
||||
// _In_opt_ HANDLE hToken,
|
||||
// _Out_ PWSTR *ppszPath
|
||||
// );
|
||||
source.getTarget().hasName("SHGetKnownFolderPath") and
|
||||
source.getTarget().hasGlobalName("SHGetKnownFolderPath") and
|
||||
use = source.getArgument(3)
|
||||
or
|
||||
// HRESULT SHGetFolderPath(
|
||||
@@ -239,9 +239,9 @@ private predicate windowsFolderPath(FunctionCall source, Element use) {
|
||||
// _Out_ LPTSTR pszPath
|
||||
// );
|
||||
(
|
||||
source.getTarget().hasName("SHGetFolderPath") or
|
||||
source.getTarget().hasName("SHGetFolderPathA") or
|
||||
source.getTarget().hasName("SHGetFolderPathW")
|
||||
source.getTarget().hasGlobalName("SHGetFolderPath") or
|
||||
source.getTarget().hasGlobalName("SHGetFolderPathA") or
|
||||
source.getTarget().hasGlobalName("SHGetFolderPathW")
|
||||
) and
|
||||
use = source.getArgument(4)
|
||||
or
|
||||
@@ -254,9 +254,9 @@ private predicate windowsFolderPath(FunctionCall source, Element use) {
|
||||
// _Out_ LPTSTR pszPath
|
||||
// );
|
||||
(
|
||||
source.getTarget().hasName("SHGetFolderPathAndSubDir") or
|
||||
source.getTarget().hasName("SHGetFolderPathAndSubDirA") or
|
||||
source.getTarget().hasName("SHGetFolderPathAndSubDirW")
|
||||
source.getTarget().hasGlobalName("SHGetFolderPathAndSubDir") or
|
||||
source.getTarget().hasGlobalName("SHGetFolderPathAndSubDirA") or
|
||||
source.getTarget().hasGlobalName("SHGetFolderPathAndSubDirW")
|
||||
) and
|
||||
use = source.getArgument(5)
|
||||
}
|
||||
@@ -273,9 +273,9 @@ class WindowsFolderPath extends SystemData {
|
||||
|
||||
private predicate logonUser(FunctionCall source, VariableAccess use) {
|
||||
(
|
||||
source.getTarget().hasName("LogonUser") or
|
||||
source.getTarget().hasName("LogonUserW") or
|
||||
source.getTarget().hasName("LogonUserA")
|
||||
source.getTarget().hasGlobalName("LogonUser") or
|
||||
source.getTarget().hasGlobalName("LogonUserW") or
|
||||
source.getTarget().hasGlobalName("LogonUserA")
|
||||
) and
|
||||
use = source.getAnArgument()
|
||||
}
|
||||
@@ -297,9 +297,9 @@ private predicate regQuery(FunctionCall source, VariableAccess use) {
|
||||
// _Inout_opt_ PLONG lpcbValue
|
||||
// );
|
||||
(
|
||||
source.getTarget().hasName("RegQueryValue") or
|
||||
source.getTarget().hasName("RegQueryValueA") or
|
||||
source.getTarget().hasName("RegQueryValueW")
|
||||
source.getTarget().hasGlobalName("RegQueryValue") or
|
||||
source.getTarget().hasGlobalName("RegQueryValueA") or
|
||||
source.getTarget().hasGlobalName("RegQueryValueW")
|
||||
) and
|
||||
use = source.getArgument(2)
|
||||
or
|
||||
@@ -311,9 +311,9 @@ private predicate regQuery(FunctionCall source, VariableAccess use) {
|
||||
// _Inout_opt_ LPDWORD ldwTotsize
|
||||
// );
|
||||
(
|
||||
source.getTarget().hasName("RegQueryMultipleValues") or
|
||||
source.getTarget().hasName("RegQueryMultipleValuesA") or
|
||||
source.getTarget().hasName("RegQueryMultipleValuesW")
|
||||
source.getTarget().hasGlobalName("RegQueryMultipleValues") or
|
||||
source.getTarget().hasGlobalName("RegQueryMultipleValuesA") or
|
||||
source.getTarget().hasGlobalName("RegQueryMultipleValuesW")
|
||||
) and
|
||||
use = source.getArgument(3)
|
||||
or
|
||||
@@ -326,9 +326,9 @@ private predicate regQuery(FunctionCall source, VariableAccess use) {
|
||||
// _Inout_opt_ LPDWORD lpcbData
|
||||
// );
|
||||
(
|
||||
source.getTarget().hasName("RegQueryValueEx") or
|
||||
source.getTarget().hasName("RegQueryValueExA") or
|
||||
source.getTarget().hasName("RegQueryValueExW")
|
||||
source.getTarget().hasGlobalName("RegQueryValueEx") or
|
||||
source.getTarget().hasGlobalName("RegQueryValueExA") or
|
||||
source.getTarget().hasGlobalName("RegQueryValueExW")
|
||||
) and
|
||||
use = source.getArgument(4)
|
||||
or
|
||||
@@ -342,9 +342,9 @@ private predicate regQuery(FunctionCall source, VariableAccess use) {
|
||||
// _Inout_opt_ LPDWORD pcbData
|
||||
// );
|
||||
(
|
||||
source.getTarget().hasName("RegGetValue") or
|
||||
source.getTarget().hasName("RegGetValueA") or
|
||||
source.getTarget().hasName("RegGetValueW")
|
||||
source.getTarget().hasGlobalName("RegGetValue") or
|
||||
source.getTarget().hasGlobalName("RegGetValueA") or
|
||||
source.getTarget().hasGlobalName("RegGetValueW")
|
||||
) and
|
||||
use = source.getArgument(5)
|
||||
}
|
||||
|
||||
@@ -15,5 +15,5 @@ import cpp
|
||||
from FunctionCall call, Function target
|
||||
where
|
||||
call.getTarget() = target and
|
||||
target.hasGlobalName("gets")
|
||||
target.hasGlobalOrStdName("gets")
|
||||
select call, "gets does not guard against buffer overflow"
|
||||
|
||||
@@ -22,7 +22,7 @@ predicate acquireExpr(Expr acquire, string kind) {
|
||||
exists(FunctionCall fc, Function f, string name |
|
||||
fc = acquire and
|
||||
f = fc.getTarget() and
|
||||
f.hasGlobalName(name) and
|
||||
f.hasGlobalOrStdName(name) and
|
||||
(
|
||||
name = "fopen" and
|
||||
kind = "file"
|
||||
@@ -46,7 +46,7 @@ predicate releaseExpr(Expr release, Expr resource, string kind) {
|
||||
exists(FunctionCall fc, Function f, string name |
|
||||
fc = release and
|
||||
f = fc.getTarget() and
|
||||
f.hasGlobalName(name) and
|
||||
f.hasGlobalOrStdName(name) and
|
||||
(
|
||||
name = "fclose" and
|
||||
resource = fc.getArgument(0) and
|
||||
|
||||
@@ -22,8 +22,8 @@ predicate containsArray(Type t) {
|
||||
or
|
||||
containsArray(t.getUnderlyingType()) and
|
||||
not exists(TypedefType allowed | allowed = t |
|
||||
allowed.hasGlobalName("jmp_buf") or
|
||||
allowed.hasGlobalName("va_list")
|
||||
allowed.hasGlobalOrStdName("jmp_buf") or
|
||||
allowed.hasGlobalOrStdName("va_list")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -605,15 +605,6 @@ class Class extends UserType {
|
||||
class_instantiation(underlyingElement(this), unresolveElement(c))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument used to instantiate this class from a
|
||||
* class template. When called on a class template, this will return the
|
||||
* `i`th template parameter.
|
||||
*/
|
||||
override Type getTemplateArgument(int i) {
|
||||
class_template_argument(underlyingElement(this), i, unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this class/struct is polymorphic (has a virtual function, or
|
||||
* inherits one).
|
||||
@@ -623,7 +614,7 @@ class Class extends UserType {
|
||||
}
|
||||
|
||||
override predicate involvesTemplateParameter() {
|
||||
getATemplateArgument().involvesTemplateParameter()
|
||||
getATemplateArgument().(Type).involvesTemplateParameter()
|
||||
}
|
||||
|
||||
/** Holds if this class, struct or union was declared 'final'. */
|
||||
|
||||
@@ -123,6 +123,13 @@ abstract class Declaration extends Locatable, @declaration {
|
||||
/** Holds if this declaration has the given name in the global namespace. */
|
||||
predicate hasGlobalName(string name) { this.hasQualifiedName("", "", name) }
|
||||
|
||||
/** Holds if this declaration has the given name in the global namespace or the `std` namespace. */
|
||||
predicate hasGlobalOrStdName(string name) {
|
||||
this.hasGlobalName(name)
|
||||
or
|
||||
this.hasQualifiedName("std", "", name)
|
||||
}
|
||||
|
||||
/** Gets a specifier of this declaration. */
|
||||
abstract Specifier getASpecifier();
|
||||
|
||||
@@ -193,20 +200,83 @@ abstract class Declaration extends Locatable, @declaration {
|
||||
|
||||
/**
|
||||
* Gets a template argument used to instantiate this declaration from a template.
|
||||
* When called on a template, this will return a template parameter.
|
||||
* When called on a template, this will return a template parameter type for
|
||||
* both typed and non-typed parameters.
|
||||
*/
|
||||
final Type getATemplateArgument() { result = getTemplateArgument(_) }
|
||||
final Locatable getATemplateArgument() { result = getTemplateArgument(_) }
|
||||
|
||||
/**
|
||||
* Gets a template argument used to instantiate this declaration from a template.
|
||||
* When called on a template, this will return a non-typed template
|
||||
* parameter value.
|
||||
*/
|
||||
final Locatable getATemplateArgumentKind() { result = getTemplateArgumentKind(_) }
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument used to instantiate this declaration from a
|
||||
* template. When called on a template, this will return the `i`th template parameter.
|
||||
* template.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* `template<typename T, T X> class Foo;`
|
||||
*
|
||||
* Will have `getTemplateArgument(0)` return `T`, and
|
||||
* `getTemplateArgument(1)` return `X`.
|
||||
*
|
||||
* `Foo<int, 1> bar;
|
||||
*
|
||||
* Will have `getTemplateArgument())` return `int`, and
|
||||
* `getTemplateArgument(1)` return `1`.
|
||||
*/
|
||||
Type getTemplateArgument(int index) { none() }
|
||||
final Locatable getTemplateArgument(int index) {
|
||||
if exists(getTemplateArgumentValue(index))
|
||||
then result = getTemplateArgumentValue(index)
|
||||
else result = getTemplateArgumentType(index)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument value used to instantiate this declaration
|
||||
* from a template. When called on a template, this will return the `i`th template
|
||||
* parameter value if it exists.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* `template<typename T, T X> class Foo;`
|
||||
*
|
||||
* Will have `getTemplateArgumentKind(1)` return `T`, and no result for
|
||||
* `getTemplateArgumentKind(0)`.
|
||||
*
|
||||
* `Foo<int, 10> bar;
|
||||
*
|
||||
* Will have `getTemplateArgumentKind(1)` return `int`, and no result for
|
||||
* `getTemplateArgumentKind(0)`.
|
||||
*/
|
||||
final Locatable getTemplateArgumentKind(int index) {
|
||||
if exists(getTemplateArgumentValue(index))
|
||||
then result = getTemplateArgumentType(index)
|
||||
else none()
|
||||
}
|
||||
|
||||
/** Gets the number of template arguments for this declaration. */
|
||||
final int getNumberOfTemplateArguments() {
|
||||
result = count(int i | exists(getTemplateArgument(i)))
|
||||
}
|
||||
|
||||
private Type getTemplateArgumentType(int index) {
|
||||
class_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
or
|
||||
function_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
or
|
||||
variable_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
}
|
||||
|
||||
private Expr getTemplateArgumentValue(int index) {
|
||||
class_template_argument_value(underlyingElement(this), index, unresolveElement(result))
|
||||
or
|
||||
function_template_argument_value(underlyingElement(this), index, unresolveElement(result))
|
||||
or
|
||||
variable_template_argument_value(underlyingElement(this), index, unresolveElement(result))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -343,15 +343,6 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
function_instantiation(underlyingElement(this), unresolveElement(f))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument used to instantiate this function from a
|
||||
* function template. When called on a function template, this will return the
|
||||
* `i`th template parameter.
|
||||
*/
|
||||
override Type getTemplateArgument(int index) {
|
||||
function_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this function is defined in several files. This is illegal in
|
||||
* C (though possible in some C++ compilers), and likely indicates that
|
||||
@@ -434,7 +425,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
// ... and likewise for destructors.
|
||||
this.(Destructor).getADestruction().mayBeGloballyImpure()
|
||||
else
|
||||
not exists(string name | this.hasGlobalName(name) |
|
||||
not exists(string name | this.hasGlobalOrStdName(name) |
|
||||
// Unless it's a function that we know is side-effect-free, it may
|
||||
// have side-effects.
|
||||
name = "strcmp" or
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Gets a `Field` that is nested within the given `Struct`.
|
||||
*
|
||||
* This identifies `Field`s which are located in the same memory
|
||||
* Gets a `Field` that is within the given `Struct`, either directly or nested
|
||||
* inside one or more levels of member structs.
|
||||
*/
|
||||
private Field getANestedField(Struct s) {
|
||||
result = s.getAField()
|
||||
@@ -15,7 +14,7 @@ private Field getANestedField(Struct s) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwraps a series of field accesses to determine the inner-most qualifier.
|
||||
* Unwraps a series of field accesses to determine the outer-most qualifier.
|
||||
*/
|
||||
private Expr getUltimateQualifier(FieldAccess fa) {
|
||||
exists(Expr qualifier | qualifier = fa.getQualifier() |
|
||||
|
||||
@@ -35,6 +35,14 @@ private string getParameterTypeString(Type parameterType) {
|
||||
else result = parameterType.(DumpType).getTypeIdentityString()
|
||||
}
|
||||
|
||||
private string getTemplateArgumentString(Declaration d, int i) {
|
||||
if exists(d.getTemplateArgumentKind(i))
|
||||
then
|
||||
result = d.getTemplateArgumentKind(i).(DumpType).getTypeIdentityString() + " " +
|
||||
d.getTemplateArgument(i)
|
||||
else result = d.getTemplateArgument(i).(DumpType).getTypeIdentityString()
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Declaration` extended to add methods for generating strings useful only for dumps and debugging.
|
||||
*/
|
||||
@@ -56,7 +64,7 @@ abstract private class DumpDeclaration extends Declaration {
|
||||
strictconcat(int i |
|
||||
exists(this.getTemplateArgument(i))
|
||||
|
|
||||
this.getTemplateArgument(i).(DumpType).getTypeIdentityString(), ", " order by i
|
||||
getTemplateArgumentString(this, i), ", " order by i
|
||||
) + ">"
|
||||
else result = ""
|
||||
}
|
||||
|
||||
@@ -7,3 +7,15 @@
|
||||
|
||||
import cpp
|
||||
import PrintAST
|
||||
|
||||
/**
|
||||
* Temporarily tweak this class or make a copy to control which functions are
|
||||
* printed.
|
||||
*/
|
||||
class Cfg extends PrintASTConfiguration {
|
||||
/**
|
||||
* TWEAK THIS PREDICATE AS NEEDED.
|
||||
* Holds if the AST for `func` should be printed.
|
||||
*/
|
||||
override predicate shouldPrintFunction(Function func) { any() }
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ class Type extends Locatable, @type {
|
||||
// A function call that provides an explicit template argument that refers to T uses T.
|
||||
// We exclude calls within instantiations, since they do not appear directly in the source.
|
||||
exists(FunctionCall c |
|
||||
c.getAnExplicitTemplateArgument().refersTo(this) and
|
||||
c.getAnExplicitTemplateArgument().(Type).refersTo(this) and
|
||||
result = c and
|
||||
not c.getEnclosingFunction().isConstructedFrom(_)
|
||||
)
|
||||
|
||||
@@ -155,15 +155,6 @@ class Variable extends Declaration, @variable {
|
||||
variable_instantiation(underlyingElement(this), unresolveElement(v))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument used to instantiate this variable from a
|
||||
* variable template. When called on a variable template, this will return the
|
||||
* `i`th template parameter.
|
||||
*/
|
||||
override Type getTemplateArgument(int index) {
|
||||
variable_template_argument(underlyingElement(this), index, unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this is a compiler-generated variable. For example, a
|
||||
* [range-based for loop](http://en.cppreference.com/w/cpp/language/range-for)
|
||||
|
||||
@@ -5,13 +5,17 @@ import cpp
|
||||
*/
|
||||
predicate allocationFunction(Function f) {
|
||||
exists(string name |
|
||||
f.hasGlobalName(name) and
|
||||
f.hasGlobalOrStdName(name) and
|
||||
(
|
||||
name = "malloc" or
|
||||
name = "calloc" or
|
||||
name = "realloc" or
|
||||
name = "strdup" or
|
||||
name = "wcsdup" or
|
||||
name = "wcsdup"
|
||||
)
|
||||
or
|
||||
f.hasGlobalName(name) and
|
||||
(
|
||||
name = "_strdup" or
|
||||
name = "_wcsdup" or
|
||||
name = "_mbsdup" or
|
||||
@@ -59,7 +63,7 @@ predicate allocationCall(FunctionCall fc) {
|
||||
allocationFunction(fc.getTarget()) and
|
||||
(
|
||||
// realloc(ptr, 0) only frees the pointer
|
||||
fc.getTarget().hasGlobalName("realloc") implies not fc.getArgument(1).getValue() = "0"
|
||||
fc.getTarget().hasGlobalOrStdName("realloc") implies not fc.getArgument(1).getValue() = "0"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -73,7 +77,10 @@ predicate freeFunction(Function f, int argNum) {
|
||||
name = "free" and argNum = 0
|
||||
or
|
||||
name = "realloc" and argNum = 0
|
||||
or
|
||||
)
|
||||
or
|
||||
f.hasGlobalOrStdName(name) and
|
||||
(
|
||||
name = "ExFreePoolWithTag" and argNum = 0
|
||||
or
|
||||
name = "ExFreeToLookasideListEx" and argNum = 1
|
||||
|
||||
@@ -28,7 +28,7 @@ class EnvironmentRead extends Expr {
|
||||
private predicate readsEnvironment(Expr read, string sourceDescription) {
|
||||
exists(FunctionCall call, string name |
|
||||
read = call and
|
||||
call.getTarget().hasGlobalName(name) and
|
||||
call.getTarget().hasGlobalOrStdName(name) and
|
||||
(name = "getenv" or name = "secure_getenv" or name = "_wgetenv") and
|
||||
sourceDescription = name
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ import cpp
|
||||
*/
|
||||
predicate fopenCall(FunctionCall fc) {
|
||||
exists(Function f | f = fc.getTarget() |
|
||||
f.hasGlobalName("fopen") or
|
||||
f.hasGlobalOrStdName("fopen") or
|
||||
f.hasGlobalName("open") or
|
||||
f.hasGlobalName("_open") or
|
||||
f.hasGlobalName("_wopen") or
|
||||
@@ -23,7 +23,7 @@ predicate fopenCall(FunctionCall fc) {
|
||||
*/
|
||||
predicate fcloseCall(FunctionCall fc, Expr closed) {
|
||||
exists(Function f | f = fc.getTarget() |
|
||||
f.hasGlobalName("fclose") and
|
||||
f.hasGlobalOrStdName("fclose") and
|
||||
closed = fc.getArgument(0)
|
||||
or
|
||||
f.hasGlobalName("close") and
|
||||
@@ -32,7 +32,7 @@ predicate fcloseCall(FunctionCall fc, Expr closed) {
|
||||
f.hasGlobalName("_close") and
|
||||
closed = fc.getArgument(0)
|
||||
or
|
||||
f.hasGlobalName("CloseHandle") and
|
||||
f.hasGlobalOrStdName("CloseHandle") and
|
||||
closed = fc.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,25 +8,32 @@ import semmle.code.cpp.commons.StringAnalysis
|
||||
import semmle.code.cpp.models.interfaces.FormattingFunction
|
||||
import semmle.code.cpp.models.implementations.Printf
|
||||
|
||||
class PrintfFormatAttribute extends FormatAttribute {
|
||||
PrintfFormatAttribute() {
|
||||
getArchetype() = "printf" or
|
||||
getArchetype() = "__printf__"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that can be identified as a `printf` style formatting
|
||||
* function by its use of the GNU `format` attribute.
|
||||
*/
|
||||
class AttributeFormattingFunction extends FormattingFunction {
|
||||
FormatAttribute printf_attrib;
|
||||
|
||||
override string getCanonicalQLClass() { result = "AttributeFormattingFunction" }
|
||||
|
||||
AttributeFormattingFunction() {
|
||||
printf_attrib = getAnAttribute() and
|
||||
(
|
||||
printf_attrib.getArchetype() = "printf" or
|
||||
printf_attrib.getArchetype() = "__printf__"
|
||||
) and
|
||||
exists(printf_attrib.getFirstFormatArgIndex()) // exclude `vprintf` style format functions
|
||||
exists(PrintfFormatAttribute printf_attrib |
|
||||
printf_attrib = getAnAttribute() and
|
||||
exists(printf_attrib.getFirstFormatArgIndex()) // exclude `vprintf` style format functions
|
||||
)
|
||||
}
|
||||
|
||||
override int getFormatParameterIndex() { result = printf_attrib.getFormatIndex() }
|
||||
override int getFormatParameterIndex() {
|
||||
forex(PrintfFormatAttribute printf_attrib | printf_attrib = getAnAttribute() |
|
||||
result = printf_attrib.getFormatIndex()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,15 +131,17 @@ class FormattingFunctionCall extends Expr {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument corresponding to the nth conversion specifier
|
||||
* Gets the argument corresponding to the nth conversion specifier.
|
||||
*/
|
||||
Expr getConversionArgument(int n) {
|
||||
exists(FormatLiteral fl, int b, int o |
|
||||
exists(FormatLiteral fl |
|
||||
fl = this.getFormat() and
|
||||
b = sum(int i, int toSum | i < n and toSum = fl.getNumArgNeeded(i) | toSum) and
|
||||
o = fl.getNumArgNeeded(n) and
|
||||
o > 0 and
|
||||
result = this.getFormatArgument(b + o - 1)
|
||||
(
|
||||
result = this.getFormatArgument(fl.getParameterFieldValue(n))
|
||||
or
|
||||
result = this.getFormatArgument(fl.getFormatArgumentIndexFor(n, 2)) and
|
||||
not exists(fl.getParameterFieldValue(n))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -142,11 +151,14 @@ class FormattingFunctionCall extends Expr {
|
||||
* an explicit minimum field width).
|
||||
*/
|
||||
Expr getMinFieldWidthArgument(int n) {
|
||||
exists(FormatLiteral fl, int b |
|
||||
exists(FormatLiteral fl |
|
||||
fl = this.getFormat() and
|
||||
b = sum(int i, int toSum | i < n and toSum = fl.getNumArgNeeded(i) | toSum) and
|
||||
fl.hasImplicitMinFieldWidth(n) and
|
||||
result = this.getFormatArgument(b)
|
||||
(
|
||||
result = this.getFormatArgument(fl.getMinFieldWidthParameterFieldValue(n))
|
||||
or
|
||||
result = this.getFormatArgument(fl.getFormatArgumentIndexFor(n, 0)) and
|
||||
not exists(fl.getMinFieldWidthParameterFieldValue(n))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -156,12 +168,14 @@ class FormattingFunctionCall extends Expr {
|
||||
* precision).
|
||||
*/
|
||||
Expr getPrecisionArgument(int n) {
|
||||
exists(FormatLiteral fl, int b, int o |
|
||||
exists(FormatLiteral fl |
|
||||
fl = this.getFormat() and
|
||||
b = sum(int i, int toSum | i < n and toSum = fl.getNumArgNeeded(i) | toSum) and
|
||||
(if fl.hasImplicitMinFieldWidth(n) then o = 1 else o = 0) and
|
||||
fl.hasImplicitPrecision(n) and
|
||||
result = this.getFormatArgument(b + o)
|
||||
(
|
||||
result = this.getFormatArgument(fl.getPrecisionParameterFieldValue(n))
|
||||
or
|
||||
result = this.getFormatArgument(fl.getFormatArgumentIndexFor(n, 1)) and
|
||||
not exists(fl.getPrecisionParameterFieldValue(n))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -361,6 +375,14 @@ class FormatLiteral extends Literal {
|
||||
*/
|
||||
string getParameterField(int n) { this.parseConvSpec(n, _, result, _, _, _, _, _) }
|
||||
|
||||
/**
|
||||
* Gets the parameter field of the nth conversion specifier (if it has one) as a
|
||||
* zero-based number.
|
||||
*/
|
||||
int getParameterFieldValue(int n) {
|
||||
result = this.getParameterField(n).regexpCapture("([0-9]*)\\$", 1).toInt() - 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the flags of the nth conversion specifier.
|
||||
*/
|
||||
@@ -430,6 +452,14 @@ class FormatLiteral extends Literal {
|
||||
*/
|
||||
int getMinFieldWidth(int n) { result = this.getMinFieldWidthOpt(n).toInt() }
|
||||
|
||||
/**
|
||||
* Gets the zero-based parameter number of the minimum field width of the nth
|
||||
* conversion specifier, if it is implicit and uses a parameter field (such as `*1$`).
|
||||
*/
|
||||
int getMinFieldWidthParameterFieldValue(int n) {
|
||||
result = this.getMinFieldWidthOpt(n).regexpCapture("\\*([0-9]*)\\$", 1).toInt() - 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the precision of the nth conversion specifier (empty string if none is given).
|
||||
*/
|
||||
@@ -460,6 +490,14 @@ class FormatLiteral extends Literal {
|
||||
else result = this.getPrecisionOpt(n).regexpCapture("\\.([0-9]*)", 1).toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the zero-based parameter number of the precision of the nth conversion
|
||||
* specifier, if it is implicit and uses a parameter field (such as `*1$`).
|
||||
*/
|
||||
int getPrecisionParameterFieldValue(int n) {
|
||||
result = this.getPrecisionOpt(n).regexpCapture("\\.\\*([0-9]*)\\$", 1).toInt() - 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the length flag of the nth conversion specifier.
|
||||
*/
|
||||
@@ -777,19 +815,49 @@ class FormatLiteral extends Literal {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the nth conversion specifier of this format string (if `mode = 2`), it's
|
||||
* minimum field width (if `mode = 0`) or it's precision (if `mode = 1`) requires a
|
||||
* format argument.
|
||||
*
|
||||
* Most conversion specifiers require a format argument, whereas minimum field width
|
||||
* and precision only require a format argument if they are present and a `*` was
|
||||
* used for it's value in the format string.
|
||||
*/
|
||||
private predicate hasFormatArgumentIndexFor(int n, int mode) {
|
||||
mode = 0 and
|
||||
this.hasImplicitMinFieldWidth(n)
|
||||
or
|
||||
mode = 1 and
|
||||
this.hasImplicitPrecision(n)
|
||||
or
|
||||
mode = 2 and
|
||||
exists(this.getConvSpecOffset(n)) and
|
||||
not this.getConversionChar(n) = "m"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the computed format argument index for the nth conversion specifier of this
|
||||
* format string (if `mode = 2`), it's minimum field width (if `mode = 0`) or it's
|
||||
* precision (if `mode = 1`). Has no result if that element is not present. Does
|
||||
* not account for positional arguments (`$`).
|
||||
*/
|
||||
int getFormatArgumentIndexFor(int n, int mode) {
|
||||
hasFormatArgumentIndexFor(n, mode) and
|
||||
(3 * n) + mode = rank[result + 1](int n2, int mode2 |
|
||||
hasFormatArgumentIndexFor(n2, mode2)
|
||||
|
|
||||
(3 * n2) + mode2
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of arguments required by the nth conversion specifier
|
||||
* of this format string.
|
||||
*/
|
||||
int getNumArgNeeded(int n) {
|
||||
exists(this.getConvSpecOffset(n)) and
|
||||
not this.getConversionChar(n) = "%" and
|
||||
exists(int n1, int n2, int n3 |
|
||||
(if this.hasImplicitMinFieldWidth(n) then n1 = 1 else n1 = 0) and
|
||||
(if this.hasImplicitPrecision(n) then n2 = 1 else n2 = 0) and
|
||||
(if this.getConversionChar(n) = "m" then n3 = 0 else n3 = 1) and
|
||||
result = n1 + n2 + n3
|
||||
)
|
||||
result = count(int mode | hasFormatArgumentIndexFor(n, mode))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -801,7 +869,7 @@ class FormatLiteral extends Literal {
|
||||
// At least one conversion specifier has a parameter field, in which case,
|
||||
// they all should have.
|
||||
result = max(string s | this.getParameterField(_) = s + "$" | s.toInt())
|
||||
else result = sum(int n, int toSum | toSum = this.getNumArgNeeded(n) | toSum)
|
||||
else result = count(int n, int mode | hasFormatArgumentIndexFor(n, mode))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -53,8 +53,8 @@ class AnalysedString extends Expr {
|
||||
*/
|
||||
class StrlenCall extends FunctionCall {
|
||||
StrlenCall() {
|
||||
this.getTarget().hasGlobalName("strlen") or
|
||||
this.getTarget().hasGlobalName("wcslen") or
|
||||
this.getTarget().hasGlobalOrStdName("strlen") or
|
||||
this.getTarget().hasGlobalOrStdName("wcslen") or
|
||||
this.getTarget().hasGlobalName("_mbslen") or
|
||||
this.getTarget().hasGlobalName("_mbslen_l") or
|
||||
this.getTarget().hasGlobalName("_mbstrlen") or
|
||||
|
||||
@@ -6,7 +6,7 @@ import Nullness
|
||||
*/
|
||||
predicate callDereferences(FunctionCall fc, int i) {
|
||||
exists(string name |
|
||||
fc.getTarget().hasGlobalName(name) and
|
||||
fc.getTarget().hasGlobalOrStdName(name) and
|
||||
(
|
||||
name = "bcopy" and i in [0 .. 1]
|
||||
or
|
||||
|
||||
@@ -264,9 +264,9 @@ predicate callMayReturnNull(Call call) {
|
||||
* Holds if `f` may, directly or indirectly, return a null literal.
|
||||
*/
|
||||
predicate mayReturnNull(Function f) {
|
||||
f.hasGlobalName("malloc")
|
||||
f.hasGlobalOrStdName("malloc")
|
||||
or
|
||||
f.hasGlobalName("calloc")
|
||||
f.hasGlobalOrStdName("calloc")
|
||||
or
|
||||
// f.hasGlobalName("strchr")
|
||||
// or
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.dataflow.internal.FlowVar
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.controlflow.Guards
|
||||
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
cached
|
||||
private newtype TNode =
|
||||
@@ -680,12 +682,16 @@ VariableAccess getAnAccessToAssignedVariable(Expr assign) {
|
||||
*
|
||||
* It is important that all extending classes in scope are disjoint.
|
||||
*/
|
||||
class BarrierGuard extends Expr {
|
||||
/** NOT YET SUPPORTED. Holds if this guard validates `e` upon evaluating to `branch`. */
|
||||
abstract deprecated predicate checks(Expr e, boolean branch);
|
||||
class BarrierGuard extends GuardCondition {
|
||||
/** Override this predicate to hold if this guard validates `e` upon evaluating to `b`. */
|
||||
abstract predicate checks(Expr e, boolean b);
|
||||
|
||||
/** Gets a node guarded by this guard. */
|
||||
final Node getAGuardedNode() {
|
||||
none() // stub
|
||||
final ExprNode getAGuardedNode() {
|
||||
exists(GVN value, boolean branch |
|
||||
result.getExpr() = value.getAnExpr() and
|
||||
this.checks(value.getAnExpr(), branch) and
|
||||
this.controls(result.getExpr().getBasicBlock(), branch)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,19 @@ module VirtualDispatch {
|
||||
not result.hasName("IUnknown")
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper predicate for `getAViableTarget`, which computes the viable targets for
|
||||
* virtual calls based on the qualifier type.
|
||||
*/
|
||||
private Function getAViableVirtualCallTarget(Class qualifierType, MemberFunction staticTarget) {
|
||||
exists(Class qualifierSubType |
|
||||
result = getAPossibleImplementation(staticTarget) and
|
||||
qualifierType = qualifierSubType.getABaseClass*() and
|
||||
mayInherit(qualifierSubType, result) and
|
||||
not cannotInherit(qualifierSubType, result)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable target for the given function call.
|
||||
*
|
||||
@@ -42,18 +55,9 @@ module VirtualDispatch {
|
||||
* If `c` is not a virtual call, the result will be `c.getTarget()`.
|
||||
*/
|
||||
Function getAViableTarget(Call c) {
|
||||
exists(Function staticTarget | staticTarget = c.getTarget() |
|
||||
if c.(FunctionCall).isVirtual() and staticTarget instanceof MemberFunction
|
||||
then
|
||||
exists(Class qualifierType, Class qualifierSubType |
|
||||
result = getAPossibleImplementation(staticTarget) and
|
||||
qualifierType = getCallQualifierType(c) and
|
||||
qualifierType = qualifierSubType.getABaseClass*() and
|
||||
mayInherit(qualifierSubType, result) and
|
||||
not cannotInherit(qualifierSubType, result)
|
||||
)
|
||||
else result = staticTarget
|
||||
)
|
||||
if c.(FunctionCall).isVirtual() and c.getTarget() instanceof MemberFunction
|
||||
then result = getAViableVirtualCallTarget(getCallQualifierType(c), c.getTarget())
|
||||
else result = c.getTarget()
|
||||
}
|
||||
|
||||
/** Holds if `f` is declared in `c` or a transitive base class of `c`. */
|
||||
@@ -63,7 +67,7 @@ module VirtualDispatch {
|
||||
|
||||
/**
|
||||
* Holds if `c` cannot inherit the member function `f`,
|
||||
* i.e. `c` or one of its supertypes overrides `f`.
|
||||
* that is, `c` or one of its supertypes overrides `f`.
|
||||
*/
|
||||
private predicate cannotInherit(Class c, MemberFunction f) {
|
||||
exists(Class overridingType, MemberFunction override |
|
||||
@@ -139,17 +139,29 @@ class FunctionCall extends Call, @funbindexpr {
|
||||
override string getCanonicalQLClass() { result = "FunctionCall" }
|
||||
|
||||
/** Gets an explicit template argument for this call. */
|
||||
Type getAnExplicitTemplateArgument() { result = getExplicitTemplateArgument(_) }
|
||||
Locatable getAnExplicitTemplateArgument() { result = getExplicitTemplateArgument(_) }
|
||||
|
||||
/** Gets an explicit template argument value for this call. */
|
||||
Locatable getAnExplicitTemplateArgumentKind() { result = getExplicitTemplateArgumentKind(_) }
|
||||
|
||||
/** Gets a template argument for this call. */
|
||||
Type getATemplateArgument() { result = getTarget().getATemplateArgument() }
|
||||
Locatable getATemplateArgument() { result = getTarget().getATemplateArgument() }
|
||||
|
||||
/** Gets a template argument value for this call. */
|
||||
Locatable getATemplateArgumentKind() { result = getTarget().getATemplateArgumentKind() }
|
||||
|
||||
/** Gets the nth explicit template argument for this call. */
|
||||
Type getExplicitTemplateArgument(int n) {
|
||||
Locatable getExplicitTemplateArgument(int n) {
|
||||
n < getNumberOfExplicitTemplateArguments() and
|
||||
result = getTemplateArgument(n)
|
||||
}
|
||||
|
||||
/** Gets the nth explicit template argument value for this call. */
|
||||
Locatable getExplicitTemplateArgumentKind(int n) {
|
||||
n < getNumberOfExplicitTemplateArguments() and
|
||||
result = getTemplateArgumentKind(n)
|
||||
}
|
||||
|
||||
/** Gets the number of explicit template arguments for this call. */
|
||||
int getNumberOfExplicitTemplateArguments() {
|
||||
if numtemplatearguments(underlyingElement(this), _)
|
||||
@@ -161,7 +173,10 @@ class FunctionCall extends Call, @funbindexpr {
|
||||
int getNumberOfTemplateArguments() { result = count(int i | exists(getTemplateArgument(i))) }
|
||||
|
||||
/** Gets the nth template argument for this call (indexed from 0). */
|
||||
Type getTemplateArgument(int n) { result = getTarget().getTemplateArgument(n) }
|
||||
Locatable getTemplateArgument(int n) { result = getTarget().getTemplateArgument(n) }
|
||||
|
||||
/** Gets the nth template argument value for this call (indexed from 0). */
|
||||
Locatable getTemplateArgumentKind(int n) { result = getTarget().getTemplateArgumentKind(n) }
|
||||
|
||||
/** Holds if any template arguments for this call are implicit / deduced. */
|
||||
predicate hasImplicitTemplateArguments() {
|
||||
|
||||
@@ -2,6 +2,7 @@ import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowDispatch as Dispatch
|
||||
|
||||
/**
|
||||
* A predictable instruction is one where an external user can predict
|
||||
@@ -37,7 +38,7 @@ private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
|
||||
}
|
||||
|
||||
private predicate accessesVariable(CopyInstruction copy, Variable var) {
|
||||
exists(VariableAddressInstruction va | va.getVariable().getAST() = var |
|
||||
exists(VariableAddressInstruction va | va.getASTVariable() = var |
|
||||
copy.(StoreInstruction).getDestinationAddress() = va
|
||||
or
|
||||
copy.(LoadInstruction).getSourceAddress() = va
|
||||
@@ -145,7 +146,8 @@ GlobalOrNamespaceVariable globalVarFromId(string id) {
|
||||
}
|
||||
|
||||
Function resolveCall(Call call) {
|
||||
// TODO: improve virtual dispatch. This will help in the test for
|
||||
// `UncontrolledProcessOperation.ql`.
|
||||
result = call.getTarget()
|
||||
exists(CallInstruction callInstruction |
|
||||
callInstruction.getAST() = call and
|
||||
result = Dispatch::viableCallable(callInstruction)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
|
||||
Function viableImpl(CallInstruction call) { result = viableCallable(call) }
|
||||
|
||||
@@ -20,6 +21,58 @@ Function viableCallable(CallInstruction call) {
|
||||
functionSignatureWithBody(qualifiedName, nparams, result) and
|
||||
strictcount(Function other | functionSignatureWithBody(qualifiedName, nparams, other)) = 1
|
||||
)
|
||||
or
|
||||
// Rudimentary virtual dispatch support. It's essentially local data flow
|
||||
// where the source is a derived-to-base conversion and the target is the
|
||||
// qualifier of a call.
|
||||
exists(Class derived, DataFlow::Node thisArgument |
|
||||
nodeMayHaveClass(derived, thisArgument) and
|
||||
overrideMayAffectCall(derived, thisArgument, _, result, call)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` is a virtual function call with qualifier `thisArgument` in
|
||||
* `enclosingFunction`, whose static target is overridden by
|
||||
* `overridingFunction` in `overridingClass`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate overrideMayAffectCall(
|
||||
Class overridingClass, DataFlow::Node thisArgument, Function enclosingFunction,
|
||||
MemberFunction overridingFunction, CallInstruction call
|
||||
) {
|
||||
call.getEnclosingFunction() = enclosingFunction and
|
||||
overridingFunction.getAnOverriddenFunction+() = call.getStaticCallTarget() and
|
||||
overridingFunction.getDeclaringType() = overridingClass and
|
||||
thisArgument = DataFlow::instructionNode(call.getThisArgument())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` may have dynamic class `derived`, where `derived` is a class
|
||||
* that may affect virtual dispatch within the enclosing function.
|
||||
*
|
||||
* For the sake of performance, this recursion is written out manually to make
|
||||
* it a relation on `Class x Node` rather than `Node x Node` or `MemberFunction
|
||||
* x Node`, both of which would be larger. It's a forward search since there
|
||||
* should usually be fewer classes than calls.
|
||||
*
|
||||
* If a value is cast several classes up in the hierarchy, that will be modeled
|
||||
* as a chain of `ConvertToBaseInstruction`s and will cause the search to start
|
||||
* from each of them and pass through subsequent ones. There might be
|
||||
* performance to gain by stopping before a second upcast and reconstructing
|
||||
* the full chain in a "big-step" recursion after this one.
|
||||
*/
|
||||
private predicate nodeMayHaveClass(Class derived, DataFlow::Node node) {
|
||||
exists(ConvertToBaseInstruction toBase |
|
||||
derived = toBase.getDerivedClass() and
|
||||
overrideMayAffectCall(derived, _, toBase.getEnclosingFunction(), _, _) and
|
||||
node.asInstruction() = toBase
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Node prev |
|
||||
nodeMayHaveClass(derived, prev) and
|
||||
DataFlow::localFlowStep(prev, node)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.controlflow.IRGuards
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
|
||||
/**
|
||||
* A newtype wrapper to prevent accidental casts between `Node` and
|
||||
@@ -204,7 +205,8 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
|
||||
iTo.(CopyInstruction).getSourceValue() = iFrom or
|
||||
iTo.(PhiInstruction).getAnOperand().getDef() = iFrom or
|
||||
// Treat all conversions as flow, even conversions between different numeric types.
|
||||
iTo.(ConvertInstruction).getUnary() = iFrom
|
||||
iTo.(ConvertInstruction).getUnary() = iFrom or
|
||||
iTo.(InheritanceConversionInstruction).getUnary() = iFrom
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,6 +215,14 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
|
||||
*/
|
||||
predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `i1` to `i2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
predicate localInstructionFlow(Instruction e1, Instruction e2) {
|
||||
localFlow(instructionNode(e1), instructionNode(e2))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `e1` to `e2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
@@ -220,7 +230,7 @@ predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
|
||||
predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)) }
|
||||
|
||||
/**
|
||||
* A guard that validates some expression.
|
||||
* A guard that validates some instruction.
|
||||
*
|
||||
* To use this in a configuration, extend the class and provide a
|
||||
* characteristic predicate precisely specifying the guard, and override
|
||||
@@ -229,11 +239,15 @@ predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)
|
||||
* It is important that all extending classes in scope are disjoint.
|
||||
*/
|
||||
class BarrierGuard extends IRGuardCondition {
|
||||
/** NOT YET SUPPORTED. Holds if this guard validates `e` upon evaluating to `b`. */
|
||||
abstract deprecated predicate checks(Instruction e, boolean b);
|
||||
/** Override this predicate to hold if this guard validates `instr` upon evaluating to `b`. */
|
||||
abstract predicate checks(Instruction instr, boolean b);
|
||||
|
||||
/** Gets a node guarded by this guard. */
|
||||
final Node getAGuardedNode() {
|
||||
none() // stub
|
||||
exists(ValueNumber value, boolean edge |
|
||||
result.asInstruction() = value.getAnInstruction() and
|
||||
this.checks(value.getAnInstruction(), edge) and
|
||||
this.controls(result.asInstruction().getBlock(), edge)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,14 @@ private predicate localInstructionTaintStep(Instruction nodeFrom, Instruction no
|
||||
*/
|
||||
predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) }
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `i1` to `i2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
predicate localInstructionTaint(Instruction i1, Instruction i2) {
|
||||
localTaint(DataFlow::instructionNode(i1), DataFlow::instructionNode(i2))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `e1` to `e2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
private import internal.IRTypeInternal
|
||||
|
||||
cached
|
||||
private newtype TIRType =
|
||||
TIRVoidType() or
|
||||
TIRUnknownType() or
|
||||
@@ -42,6 +43,10 @@ class IRType extends TIRType {
|
||||
*
|
||||
* This will hold for all `IRType` objects except `IRUnknownType`.
|
||||
*/
|
||||
// This predicate is overridden with `pragma[noinline]` in every leaf subclass.
|
||||
// This allows callers to ask for things like _the_ floating-point type of
|
||||
// size 4 without getting a join that first finds all types of size 4 and
|
||||
// _then_ restricts them to floating-point types.
|
||||
int getByteSize() { none() }
|
||||
|
||||
/**
|
||||
@@ -104,8 +109,6 @@ private class IRSizedType extends IRType {
|
||||
this = TIRFunctionAddressType(byteSize) or
|
||||
this = TIROpaqueType(_, byteSize)
|
||||
}
|
||||
|
||||
final override int getByteSize() { result = byteSize }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,6 +120,9 @@ class IRBooleanType extends IRSizedType, TIRBooleanType {
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalBooleanType(byteSize)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
final override int getByteSize() { result = byteSize }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,6 +147,9 @@ class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType {
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalSignedIntegerType(byteSize)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
final override int getByteSize() { result = byteSize }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,6 +162,9 @@ class IRUnsignedIntegerType extends IRNumericType, TIRUnsignedIntegerType {
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalUnsignedIntegerType(byteSize)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
final override int getByteSize() { result = byteSize }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,6 +176,9 @@ class IRFloatingPointType extends IRNumericType, TIRFloatingPointType {
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalFloatingPointType(byteSize)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
final override int getByteSize() { result = byteSize }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,6 +193,9 @@ class IRAddressType extends IRSizedType, TIRAddressType {
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalAddressType(byteSize)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
final override int getByteSize() { result = byteSize }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,6 +208,9 @@ class IRFunctionAddressType extends IRSizedType, TIRFunctionAddressType {
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalFunctionAddressType(byteSize)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
final override int getByteSize() { result = byteSize }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,6 +239,9 @@ class IROpaqueType extends IRSizedType, TIROpaqueType {
|
||||
* same size.
|
||||
*/
|
||||
final Language::OpaqueTypeTag getTag() { result = tag }
|
||||
|
||||
pragma[noinline]
|
||||
final override int getByteSize() { result = byteSize }
|
||||
}
|
||||
|
||||
module IRTypeSanity {
|
||||
|
||||
@@ -5,6 +5,7 @@ private newtype TMemoryAccessKind =
|
||||
TBufferMayMemoryAccess() or
|
||||
TEscapedMemoryAccess() or
|
||||
TEscapedMayMemoryAccess() or
|
||||
TNonLocalMayMemoryAccess() or
|
||||
TPhiMemoryAccess() or
|
||||
TUnmodeledMemoryAccess() or
|
||||
TChiTotalMemoryAccess() or
|
||||
@@ -80,6 +81,14 @@ class EscapedMayMemoryAccess extends MemoryAccessKind, TEscapedMayMemoryAccess {
|
||||
override string toString() { result = "escaped(may)" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand or result may access all memory whose address has escaped, other than data on the
|
||||
* stack frame of the current function.
|
||||
*/
|
||||
class NonLocalMayMemoryAccess extends MemoryAccessKind, TNonLocalMayMemoryAccess {
|
||||
override string toString() { result = "nonlocal(may)" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand is a Phi operand, which accesses the same memory as its
|
||||
* definition.
|
||||
|
||||
@@ -36,7 +36,7 @@ private newtype TOpcode =
|
||||
TPointerSub() or
|
||||
TPointerDiff() or
|
||||
TConvert() or
|
||||
TConvertToBase() or
|
||||
TConvertToNonVirtualBase() or
|
||||
TConvertToVirtualBase() or
|
||||
TConvertToDerived() or
|
||||
TCheckedConvertOrNull() or
|
||||
@@ -59,6 +59,7 @@ private newtype TOpcode =
|
||||
TUnmodeledDefinition() or
|
||||
TUnmodeledUse() or
|
||||
TAliasedDefinition() or
|
||||
TAliasedUse() or
|
||||
TPhi() or
|
||||
TBuiltIn() or
|
||||
TVarArgsStart() or
|
||||
@@ -111,6 +112,8 @@ abstract class RelationalOpcode extends CompareOpcode { }
|
||||
|
||||
abstract class CopyOpcode extends Opcode { }
|
||||
|
||||
abstract class ConvertToBaseOpcode extends UnaryOpcode { }
|
||||
|
||||
abstract class MemoryAccessOpcode extends Opcode { }
|
||||
|
||||
abstract class ReturnOpcode extends Opcode { }
|
||||
@@ -311,11 +314,11 @@ module Opcode {
|
||||
final override string toString() { result = "Convert" }
|
||||
}
|
||||
|
||||
class ConvertToBase extends UnaryOpcode, TConvertToBase {
|
||||
final override string toString() { result = "ConvertToBase" }
|
||||
class ConvertToNonVirtualBase extends ConvertToBaseOpcode, TConvertToNonVirtualBase {
|
||||
final override string toString() { result = "ConvertToNonVirtualBase" }
|
||||
}
|
||||
|
||||
class ConvertToVirtualBase extends UnaryOpcode, TConvertToVirtualBase {
|
||||
class ConvertToVirtualBase extends ConvertToBaseOpcode, TConvertToVirtualBase {
|
||||
final override string toString() { result = "ConvertToVirtualBase" }
|
||||
}
|
||||
|
||||
@@ -403,6 +406,10 @@ module Opcode {
|
||||
final override string toString() { result = "AliasedDefinition" }
|
||||
}
|
||||
|
||||
class AliasedUse extends Opcode, TAliasedUse {
|
||||
final override string toString() { result = "AliasedUse" }
|
||||
}
|
||||
|
||||
class Phi extends Opcode, TPhi {
|
||||
final override string toString() { result = "Phi" }
|
||||
}
|
||||
|
||||
@@ -22,6 +22,12 @@ abstract class IRVariable extends TIRVariable {
|
||||
|
||||
abstract string toString();
|
||||
|
||||
/**
|
||||
* Holds if this variable's value cannot be changed within a function. Currently used for string
|
||||
* literals, but could also apply to `const` global and static variables.
|
||||
*/
|
||||
predicate isReadOnly() { none() }
|
||||
|
||||
/**
|
||||
* Gets the type of the variable.
|
||||
*/
|
||||
@@ -113,34 +119,43 @@ class IRStaticUserVariable extends IRUserVariable {
|
||||
final override Language::StaticVariable getVariable() { result = var }
|
||||
}
|
||||
|
||||
abstract class IRGeneratedVariable extends IRVariable {
|
||||
Language::AST ast;
|
||||
Language::LanguageType type;
|
||||
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
|
||||
final override Language::AST getAST() { result = ast }
|
||||
|
||||
override string toString() { result = getBaseString() + getLocationString() }
|
||||
|
||||
override string getUniqueId() { none() }
|
||||
|
||||
final string getLocationString() {
|
||||
result = ast.getLocation().getStartLine().toString() + ":" +
|
||||
ast.getLocation().getStartColumn().toString()
|
||||
}
|
||||
|
||||
string getBaseString() { none() }
|
||||
}
|
||||
|
||||
IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) {
|
||||
result.getAST() = ast and
|
||||
result.getTag() = tag
|
||||
}
|
||||
|
||||
class IRTempVariable extends IRVariable, IRAutomaticVariable, TIRTempVariable {
|
||||
Language::AST ast;
|
||||
class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable {
|
||||
TempVariableTag tag;
|
||||
Language::LanguageType type;
|
||||
|
||||
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
|
||||
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
|
||||
final override Language::AST getAST() { result = ast }
|
||||
|
||||
final override string getUniqueId() {
|
||||
result = "Temp: " + Construction::getTempVariableUniqueId(this)
|
||||
}
|
||||
|
||||
final TempVariableTag getTag() { result = tag }
|
||||
|
||||
override string toString() {
|
||||
result = getBaseString() + ast.getLocation().getStartLine().toString() + ":" +
|
||||
ast.getLocation().getStartColumn().toString()
|
||||
}
|
||||
|
||||
string getBaseString() { result = "#temp" }
|
||||
override string getBaseString() { result = "#temp" }
|
||||
}
|
||||
|
||||
class IRReturnVariable extends IRTempVariable {
|
||||
@@ -154,3 +169,19 @@ class IRThrowVariable extends IRTempVariable {
|
||||
|
||||
override string getBaseString() { result = "#throw" }
|
||||
}
|
||||
|
||||
class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
||||
Language::StringLiteral literal;
|
||||
|
||||
IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) }
|
||||
|
||||
final override predicate isReadOnly() { any() }
|
||||
|
||||
final override string getUniqueId() {
|
||||
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
|
||||
}
|
||||
|
||||
override string getBaseString() { result = "#string" }
|
||||
|
||||
final Language::StringLiteral getLiteral() { result = literal }
|
||||
}
|
||||
|
||||
@@ -51,7 +51,8 @@ module InstructionSanity {
|
||||
opcode instanceof ReadSideEffectOpcode or
|
||||
opcode instanceof Opcode::InlineAsm or
|
||||
opcode instanceof Opcode::CallSideEffect or
|
||||
opcode instanceof Opcode::ReturnIndirection
|
||||
opcode instanceof Opcode::ReturnIndirection or
|
||||
opcode instanceof Opcode::AliasedUse
|
||||
) and
|
||||
tag instanceof SideEffectOperandTag
|
||||
)
|
||||
@@ -124,6 +125,16 @@ module InstructionSanity {
|
||||
)
|
||||
}
|
||||
|
||||
query predicate duplicateChiOperand(
|
||||
ChiInstruction chi, string message, IRFunction func, string funcText
|
||||
) {
|
||||
chi.getTotal() = chi.getPartial() and
|
||||
message = "Chi instruction for " + chi.getPartial().toString() +
|
||||
" has duplicate operands in function $@" and
|
||||
func = chi.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
|
||||
query predicate sideEffectWithoutPrimary(
|
||||
SideEffectInstruction instr, string message, IRFunction func, string funcText
|
||||
) {
|
||||
@@ -264,6 +275,7 @@ module InstructionSanity {
|
||||
) {
|
||||
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
|
||||
not useOperand.getUse() instanceof UnmodeledUseInstruction and
|
||||
not defInstr instanceof UnmodeledDefinitionInstruction and
|
||||
pointOfEvaluation(useOperand, useBlock, useIndex) and
|
||||
defInstr = useOperand.getAnyDef() and
|
||||
(
|
||||
@@ -827,14 +839,12 @@ class FloatConstantInstruction extends ConstantInstruction {
|
||||
FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
|
||||
}
|
||||
|
||||
class StringConstantInstruction extends Instruction {
|
||||
Language::StringLiteral value;
|
||||
class StringConstantInstruction extends VariableInstruction {
|
||||
override IRStringLiteral var;
|
||||
|
||||
StringConstantInstruction() { value = Construction::getInstructionStringLiteral(this) }
|
||||
final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) }
|
||||
|
||||
final override string getImmediateString() { result = Language::getStringLiteralText(value) }
|
||||
|
||||
final Language::StringLiteral getValue() { result = value }
|
||||
final Language::StringLiteral getValue() { result = var.getLiteral() }
|
||||
}
|
||||
|
||||
class BinaryInstruction extends Instruction {
|
||||
@@ -1002,14 +1012,22 @@ class InheritanceConversionInstruction extends UnaryInstruction {
|
||||
* to the address of a direct non-virtual base class.
|
||||
*/
|
||||
class ConvertToBaseInstruction extends InheritanceConversionInstruction {
|
||||
ConvertToBaseInstruction() { getOpcode() instanceof Opcode::ConvertToBase }
|
||||
ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an instruction that converts from the address of a derived class
|
||||
* to the address of a direct non-virtual base class.
|
||||
*/
|
||||
class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction {
|
||||
ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an instruction that converts from the address of a derived class
|
||||
* to the address of a virtual base class.
|
||||
*/
|
||||
class ConvertToVirtualBaseInstruction extends InheritanceConversionInstruction {
|
||||
class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction {
|
||||
ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase }
|
||||
}
|
||||
|
||||
@@ -1442,6 +1460,13 @@ class AliasedDefinitionInstruction extends Instruction {
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that consumes all escaped memory on exit from the function.
|
||||
*/
|
||||
class AliasedUseInstruction extends Instruction {
|
||||
AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse }
|
||||
}
|
||||
|
||||
class UnmodeledUseInstruction extends Instruction {
|
||||
UnmodeledUseInstruction() { getOpcode() instanceof Opcode::UnmodeledUse }
|
||||
|
||||
|
||||
@@ -396,6 +396,9 @@ class SideEffectOperand extends TypedOperand {
|
||||
override SideEffectOperandTag tag;
|
||||
|
||||
override MemoryAccessKind getMemoryAccess() {
|
||||
useInstr instanceof AliasedUseInstruction and
|
||||
result instanceof NonLocalMayMemoryAccess
|
||||
or
|
||||
useInstr instanceof CallSideEffectInstruction and
|
||||
result instanceof EscapedMayMemoryAccess
|
||||
or
|
||||
|
||||
@@ -110,7 +110,7 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
|
||||
instr = operand.getUse() and
|
||||
(
|
||||
// Converting to a non-virtual base class adds the offset of the base class.
|
||||
exists(ConvertToBaseInstruction convert |
|
||||
exists(ConvertToNonVirtualBaseInstruction convert |
|
||||
convert = instr and
|
||||
bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8)
|
||||
)
|
||||
@@ -309,6 +309,10 @@ predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset)
|
||||
instr.(VariableAddressInstruction).getIRVariable() = var and
|
||||
bitOffset = 0
|
||||
or
|
||||
// A string literal is just a special read-only global variable.
|
||||
instr.(StringConstantInstruction).getIRVariable() = var and
|
||||
bitOffset = 0
|
||||
or
|
||||
exists(Operand operand, IntValue originalBitOffset, IntValue propagatedBitOffset |
|
||||
operand = instr.getAnOperand() and
|
||||
// If an operand is propagated, then the result points to the same variable,
|
||||
|
||||
@@ -45,6 +45,7 @@ private newtype TMemoryLocation =
|
||||
languageType = type.getCanonicalLanguageType()
|
||||
} or
|
||||
TUnknownMemoryLocation(IRFunction irFunc) or
|
||||
TUnknownNonLocalMemoryLocation(IRFunction irFunc) or
|
||||
TUnknownVirtualVariable(IRFunction irFunc)
|
||||
|
||||
/**
|
||||
@@ -162,6 +163,26 @@ class UnknownMemoryLocation extends TUnknownMemoryLocation, MemoryLocation {
|
||||
final override string getUniqueId() { result = "{Unknown}" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An access to memory that is not known to be confined to a specific `IRVariable`, but is known to
|
||||
* not access memory on the current function's stack frame.
|
||||
*/
|
||||
class UnknownNonLocalMemoryLocation extends TUnknownNonLocalMemoryLocation, MemoryLocation {
|
||||
IRFunction irFunc;
|
||||
|
||||
UnknownNonLocalMemoryLocation() { this = TUnknownNonLocalMemoryLocation(irFunc) }
|
||||
|
||||
final override string toString() { result = "{UnknownNonLocal}" }
|
||||
|
||||
final override VirtualVariable getVirtualVariable() { result = TUnknownVirtualVariable(irFunc) }
|
||||
|
||||
final override Language::LanguageType getType() {
|
||||
result = any(IRUnknownType type).getCanonicalLanguageType()
|
||||
}
|
||||
|
||||
final override string getUniqueId() { result = "{UnknownNonLocal}" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An access to all aliased memory.
|
||||
*/
|
||||
@@ -189,10 +210,21 @@ Overlap getOverlap(MemoryLocation def, MemoryLocation use) {
|
||||
def instanceof UnknownVirtualVariable and
|
||||
result instanceof MustTotallyOverlap
|
||||
or
|
||||
// An UnknownMemoryLocation may partially overlap any Location within the same virtual variable.
|
||||
// An UnknownMemoryLocation may partially overlap any Location within the same virtual variable,
|
||||
// unless the location is read-only.
|
||||
def.getVirtualVariable() = use.getVirtualVariable() and
|
||||
def instanceof UnknownMemoryLocation and
|
||||
result instanceof MayPartiallyOverlap
|
||||
result instanceof MayPartiallyOverlap and
|
||||
not use.(VariableMemoryLocation).getVariable().isReadOnly()
|
||||
or
|
||||
// An UnknownNonLocalMemoryLocation may partially overlap any location within the same virtual
|
||||
// variable, except a local variable or read-only variable.
|
||||
def.getVirtualVariable() = use.getVirtualVariable() and
|
||||
def instanceof UnknownNonLocalMemoryLocation and
|
||||
result instanceof MayPartiallyOverlap and
|
||||
not exists(IRVariable var | var = use.(VariableMemoryLocation).getVariable() |
|
||||
var instanceof IRAutomaticVariable or var.isReadOnly()
|
||||
)
|
||||
or
|
||||
exists(VariableMemoryLocation defVariableLocation |
|
||||
defVariableLocation = def and
|
||||
@@ -202,6 +234,13 @@ Overlap getOverlap(MemoryLocation def, MemoryLocation use) {
|
||||
(use instanceof UnknownMemoryLocation or use instanceof UnknownVirtualVariable) and
|
||||
result instanceof MayPartiallyOverlap
|
||||
or
|
||||
// A VariableMemoryLocation that is not a local variable may partially overlap an unknown
|
||||
// non-local location within the same virtual variable.
|
||||
def.getVirtualVariable() = use.getVirtualVariable() and
|
||||
use instanceof UnknownNonLocalMemoryLocation and
|
||||
result instanceof MayPartiallyOverlap and
|
||||
not defVariableLocation.getVariable() instanceof IRAutomaticVariable
|
||||
or
|
||||
// A VariableMemoryLocation overlaps another location within the same variable based on the relationship
|
||||
// of the two offset intervals.
|
||||
exists(Overlap intervalOverlap |
|
||||
@@ -327,6 +366,9 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
|
||||
or
|
||||
kind instanceof EscapedMayMemoryAccess and
|
||||
result = TUnknownMemoryLocation(instr.getEnclosingIRFunction())
|
||||
or
|
||||
kind instanceof NonLocalMayMemoryAccess and
|
||||
result = TUnknownNonLocalMemoryLocation(instr.getEnclosingIRFunction())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -351,6 +393,9 @@ MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
|
||||
or
|
||||
kind instanceof EscapedMayMemoryAccess and
|
||||
result = TUnknownMemoryLocation(operand.getEnclosingIRFunction())
|
||||
or
|
||||
kind instanceof NonLocalMayMemoryAccess and
|
||||
result = TUnknownNonLocalMemoryLocation(operand.getEnclosingIRFunction())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -341,11 +341,6 @@ private module Cached {
|
||||
result = getOldInstruction(instruction).(OldIR::ConstantValueInstruction).getValue()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::StringLiteral getInstructionStringLiteral(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::StringConstantInstruction).getValue()
|
||||
}
|
||||
|
||||
cached
|
||||
Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
|
||||
result = getOldInstruction(instruction)
|
||||
@@ -401,7 +396,11 @@ private predicate hasChiNode(Alias::VirtualVariable vvar, OldInstruction def) {
|
||||
defLocation.getVirtualVariable() = vvar and
|
||||
// If the definition totally (or exactly) overlaps the virtual variable, then there's no need for a `Chi`
|
||||
// instruction.
|
||||
Alias::getOverlap(defLocation, vvar) instanceof MayPartiallyOverlap
|
||||
(
|
||||
Alias::getOverlap(defLocation, vvar) instanceof MayPartiallyOverlap or
|
||||
def.getResultMemoryAccess() instanceof IndirectMayMemoryAccess or
|
||||
def.getResultMemoryAccess() instanceof BufferMayMemoryAccess
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -714,7 +713,10 @@ module DefUse {
|
||||
defLocation = Alias::getResultMemoryLocation(def) and
|
||||
block.getInstruction(index) = def and
|
||||
overlap = Alias::getOverlap(defLocation, useLocation) and
|
||||
if overlap instanceof MayPartiallyOverlap
|
||||
if
|
||||
overlap instanceof MayPartiallyOverlap or
|
||||
def.getResultMemoryAccess() instanceof IndirectMayMemoryAccess or
|
||||
def.getResultMemoryAccess() instanceof BufferMayMemoryAccess
|
||||
then offset = (index * 2) + 1 // The use will be connected to the definition on the `Chi` instruction.
|
||||
else offset = index * 2 // The use will be connected to the definition on the original instruction.
|
||||
)
|
||||
|
||||
@@ -9,4 +9,10 @@ newtype TIRVariable =
|
||||
Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type
|
||||
) {
|
||||
Construction::hasTempVariable(func, ast, tag, type)
|
||||
} or
|
||||
TIRStringLiteral(
|
||||
Language::Function func, Language::AST ast, Language::LanguageType type,
|
||||
Language::StringLiteral literal
|
||||
) {
|
||||
Construction::hasStringLiteral(func, ast, type, literal)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,12 @@ abstract class IRVariable extends TIRVariable {
|
||||
|
||||
abstract string toString();
|
||||
|
||||
/**
|
||||
* Holds if this variable's value cannot be changed within a function. Currently used for string
|
||||
* literals, but could also apply to `const` global and static variables.
|
||||
*/
|
||||
predicate isReadOnly() { none() }
|
||||
|
||||
/**
|
||||
* Gets the type of the variable.
|
||||
*/
|
||||
@@ -113,34 +119,43 @@ class IRStaticUserVariable extends IRUserVariable {
|
||||
final override Language::StaticVariable getVariable() { result = var }
|
||||
}
|
||||
|
||||
abstract class IRGeneratedVariable extends IRVariable {
|
||||
Language::AST ast;
|
||||
Language::LanguageType type;
|
||||
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
|
||||
final override Language::AST getAST() { result = ast }
|
||||
|
||||
override string toString() { result = getBaseString() + getLocationString() }
|
||||
|
||||
override string getUniqueId() { none() }
|
||||
|
||||
final string getLocationString() {
|
||||
result = ast.getLocation().getStartLine().toString() + ":" +
|
||||
ast.getLocation().getStartColumn().toString()
|
||||
}
|
||||
|
||||
string getBaseString() { none() }
|
||||
}
|
||||
|
||||
IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) {
|
||||
result.getAST() = ast and
|
||||
result.getTag() = tag
|
||||
}
|
||||
|
||||
class IRTempVariable extends IRVariable, IRAutomaticVariable, TIRTempVariable {
|
||||
Language::AST ast;
|
||||
class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable {
|
||||
TempVariableTag tag;
|
||||
Language::LanguageType type;
|
||||
|
||||
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
|
||||
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
|
||||
final override Language::AST getAST() { result = ast }
|
||||
|
||||
final override string getUniqueId() {
|
||||
result = "Temp: " + Construction::getTempVariableUniqueId(this)
|
||||
}
|
||||
|
||||
final TempVariableTag getTag() { result = tag }
|
||||
|
||||
override string toString() {
|
||||
result = getBaseString() + ast.getLocation().getStartLine().toString() + ":" +
|
||||
ast.getLocation().getStartColumn().toString()
|
||||
}
|
||||
|
||||
string getBaseString() { result = "#temp" }
|
||||
override string getBaseString() { result = "#temp" }
|
||||
}
|
||||
|
||||
class IRReturnVariable extends IRTempVariable {
|
||||
@@ -154,3 +169,19 @@ class IRThrowVariable extends IRTempVariable {
|
||||
|
||||
override string getBaseString() { result = "#throw" }
|
||||
}
|
||||
|
||||
class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
||||
Language::StringLiteral literal;
|
||||
|
||||
IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) }
|
||||
|
||||
final override predicate isReadOnly() { any() }
|
||||
|
||||
final override string getUniqueId() {
|
||||
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
|
||||
}
|
||||
|
||||
override string getBaseString() { result = "#string" }
|
||||
|
||||
final Language::StringLiteral getLiteral() { result = literal }
|
||||
}
|
||||
|
||||
@@ -51,7 +51,8 @@ module InstructionSanity {
|
||||
opcode instanceof ReadSideEffectOpcode or
|
||||
opcode instanceof Opcode::InlineAsm or
|
||||
opcode instanceof Opcode::CallSideEffect or
|
||||
opcode instanceof Opcode::ReturnIndirection
|
||||
opcode instanceof Opcode::ReturnIndirection or
|
||||
opcode instanceof Opcode::AliasedUse
|
||||
) and
|
||||
tag instanceof SideEffectOperandTag
|
||||
)
|
||||
@@ -124,6 +125,16 @@ module InstructionSanity {
|
||||
)
|
||||
}
|
||||
|
||||
query predicate duplicateChiOperand(
|
||||
ChiInstruction chi, string message, IRFunction func, string funcText
|
||||
) {
|
||||
chi.getTotal() = chi.getPartial() and
|
||||
message = "Chi instruction for " + chi.getPartial().toString() +
|
||||
" has duplicate operands in function $@" and
|
||||
func = chi.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
|
||||
query predicate sideEffectWithoutPrimary(
|
||||
SideEffectInstruction instr, string message, IRFunction func, string funcText
|
||||
) {
|
||||
@@ -264,6 +275,7 @@ module InstructionSanity {
|
||||
) {
|
||||
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
|
||||
not useOperand.getUse() instanceof UnmodeledUseInstruction and
|
||||
not defInstr instanceof UnmodeledDefinitionInstruction and
|
||||
pointOfEvaluation(useOperand, useBlock, useIndex) and
|
||||
defInstr = useOperand.getAnyDef() and
|
||||
(
|
||||
@@ -827,14 +839,12 @@ class FloatConstantInstruction extends ConstantInstruction {
|
||||
FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
|
||||
}
|
||||
|
||||
class StringConstantInstruction extends Instruction {
|
||||
Language::StringLiteral value;
|
||||
class StringConstantInstruction extends VariableInstruction {
|
||||
override IRStringLiteral var;
|
||||
|
||||
StringConstantInstruction() { value = Construction::getInstructionStringLiteral(this) }
|
||||
final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) }
|
||||
|
||||
final override string getImmediateString() { result = Language::getStringLiteralText(value) }
|
||||
|
||||
final Language::StringLiteral getValue() { result = value }
|
||||
final Language::StringLiteral getValue() { result = var.getLiteral() }
|
||||
}
|
||||
|
||||
class BinaryInstruction extends Instruction {
|
||||
@@ -1002,14 +1012,22 @@ class InheritanceConversionInstruction extends UnaryInstruction {
|
||||
* to the address of a direct non-virtual base class.
|
||||
*/
|
||||
class ConvertToBaseInstruction extends InheritanceConversionInstruction {
|
||||
ConvertToBaseInstruction() { getOpcode() instanceof Opcode::ConvertToBase }
|
||||
ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an instruction that converts from the address of a derived class
|
||||
* to the address of a direct non-virtual base class.
|
||||
*/
|
||||
class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction {
|
||||
ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an instruction that converts from the address of a derived class
|
||||
* to the address of a virtual base class.
|
||||
*/
|
||||
class ConvertToVirtualBaseInstruction extends InheritanceConversionInstruction {
|
||||
class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction {
|
||||
ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase }
|
||||
}
|
||||
|
||||
@@ -1442,6 +1460,13 @@ class AliasedDefinitionInstruction extends Instruction {
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that consumes all escaped memory on exit from the function.
|
||||
*/
|
||||
class AliasedUseInstruction extends Instruction {
|
||||
AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse }
|
||||
}
|
||||
|
||||
class UnmodeledUseInstruction extends Instruction {
|
||||
UnmodeledUseInstruction() { getOpcode() instanceof Opcode::UnmodeledUse }
|
||||
|
||||
|
||||
@@ -396,6 +396,9 @@ class SideEffectOperand extends TypedOperand {
|
||||
override SideEffectOperandTag tag;
|
||||
|
||||
override MemoryAccessKind getMemoryAccess() {
|
||||
useInstr instanceof AliasedUseInstruction and
|
||||
result instanceof NonLocalMayMemoryAccess
|
||||
or
|
||||
useInstr instanceof CallSideEffectInstruction and
|
||||
result instanceof EscapedMayMemoryAccess
|
||||
or
|
||||
|
||||
@@ -44,6 +44,13 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasStringLiteral(Function func, Locatable ast, CppType type, StringLiteral literal) {
|
||||
literal = ast and
|
||||
literal.getEnclosingFunction() = func and
|
||||
getTypeForPRValue(literal.getType()) = type
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasModeledMemoryResult(Instruction instruction) { none() }
|
||||
|
||||
@@ -51,20 +58,23 @@ private module Cached {
|
||||
Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
exists(TranslatedExpr translatedExpr |
|
||||
translatedExpr = getTranslatedExpr(result) and
|
||||
instruction = translatedExpr.getResult()
|
||||
instruction = translatedExpr.getResult() and
|
||||
// Only associate `instruction` with this expression if the translated
|
||||
// expression actually produced the instruction; not if it merely
|
||||
// forwarded the result of another translated expression.
|
||||
instruction = translatedExpr.getInstruction(_)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
exists(Expr converted, TranslatedExpr translatedExpr |
|
||||
exists(Expr converted |
|
||||
result = converted.(Conversion).getExpr+()
|
||||
or
|
||||
result = converted
|
||||
|
|
||||
not result instanceof Conversion and
|
||||
translatedExpr = getTranslatedExpr(converted) and
|
||||
instruction = translatedExpr.getResult()
|
||||
converted = getInstructionConvertedResultExpression(instruction)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -231,8 +241,14 @@ private module Cached {
|
||||
|
||||
cached
|
||||
IRVariable getInstructionVariable(Instruction instruction) {
|
||||
result = getInstructionTranslatedElement(instruction)
|
||||
.getInstructionVariable(getInstructionTag(instruction))
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
element = getInstructionTranslatedElement(instruction) and
|
||||
tag = getInstructionTag(instruction) and
|
||||
(
|
||||
result = element.getInstructionVariable(tag) or
|
||||
result.(IRStringLiteral).getAST() = element.getInstructionStringLiteral(tag)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -263,12 +279,6 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
StringLiteral getInstructionStringLiteral(Instruction instruction) {
|
||||
result = getInstructionTranslatedElement(instruction)
|
||||
.getInstructionStringLiteral(getInstructionTag(instruction))
|
||||
}
|
||||
|
||||
cached
|
||||
BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
|
||||
result = getInstructionTranslatedElement(instruction)
|
||||
|
||||
@@ -28,6 +28,7 @@ newtype TInstructionTag =
|
||||
UnmodeledDefinitionTag() or
|
||||
UnmodeledUseTag() or
|
||||
AliasedDefinitionTag() or
|
||||
AliasedUseTag() or
|
||||
SwitchBranchTag() or
|
||||
CallTargetTag() or
|
||||
CallTag() or
|
||||
@@ -46,6 +47,7 @@ newtype TInstructionTag =
|
||||
ConditionValueResultLoadTag() or
|
||||
BoolConversionConstantTag() or
|
||||
BoolConversionCompareTag() or
|
||||
ResultCopyTag() or
|
||||
LoadTag() or // Implicit load due to lvalue-to-rvalue conversion
|
||||
CatchTag() or
|
||||
ThrowTag() or
|
||||
@@ -124,6 +126,8 @@ string getInstructionTagId(TInstructionTag tag) {
|
||||
or
|
||||
tag = AliasedDefinitionTag() and result = "AliasedDef"
|
||||
or
|
||||
tag = AliasedUseTag() and result = "AliasedUse"
|
||||
or
|
||||
tag = SwitchBranchTag() and result = "SwitchBranch"
|
||||
or
|
||||
tag = CallTargetTag() and result = "CallTarget"
|
||||
|
||||
@@ -352,6 +352,11 @@ class TranslatedSideEffects extends TranslatedElement, TTranslatedSideEffects {
|
||||
none()
|
||||
}
|
||||
|
||||
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
result = getTranslatedExpr(expr).getInstruction(CallTag())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedFunction` containing this expression.
|
||||
*/
|
||||
@@ -365,6 +370,39 @@ class TranslatedSideEffects extends TranslatedElement, TTranslatedSideEffects {
|
||||
override Function getFunction() { result = expr.getEnclosingFunction() }
|
||||
}
|
||||
|
||||
class TranslatedStructorCallSideEffects extends TranslatedSideEffects {
|
||||
TranslatedStructorCallSideEffects() { getParent().(TranslatedStructorCall).hasQualifier() }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType t) {
|
||||
opcode instanceof Opcode::IndirectMayWriteSideEffect and
|
||||
tag instanceof OnlyInstructionTag and
|
||||
t = getTypeForPRValue(expr.getTarget().getDeclaringType())
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
(
|
||||
if exists(getChild(0))
|
||||
then result = getChild(0).getFirstInstruction()
|
||||
else result = getParent().getChildSuccessor(this)
|
||||
) and
|
||||
tag = OnlyInstructionTag() and
|
||||
kind instanceof GotoEdge
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag instanceof OnlyInstructionTag and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getParent().(TranslatedStructorCall).getQualifierResult()
|
||||
}
|
||||
|
||||
final override int getInstructionIndex(InstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
result = -1
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEffect {
|
||||
Call call;
|
||||
Expr arg;
|
||||
@@ -447,20 +485,26 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff
|
||||
}
|
||||
|
||||
override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
||||
exists(Type operandType |
|
||||
if hasSpecificReadSideEffect(any(Opcode::BufferReadSideEffect op))
|
||||
then
|
||||
result = getUnknownType() and
|
||||
tag instanceof OnlyInstructionTag and
|
||||
operandType = arg.getType().getUnspecifiedType().(DerivedType).getBaseType() and
|
||||
operandTag instanceof SideEffectOperandTag
|
||||
or
|
||||
tag instanceof OnlyInstructionTag and
|
||||
operandType = arg.getType().getUnspecifiedType() and
|
||||
not operandType instanceof DerivedType and
|
||||
operandTag instanceof SideEffectOperandTag
|
||||
|
|
||||
// If the type we select is an incomplete type (e.g. a forward-declared `struct`), there will
|
||||
// not be a `CppType` that represents that type. In that case, fall back to `UnknownCppType`.
|
||||
result = getTypeForPRValueOrUnknown(operandType)
|
||||
)
|
||||
else
|
||||
exists(Type operandType |
|
||||
tag instanceof OnlyInstructionTag and
|
||||
operandType = arg.getType().getUnspecifiedType().(DerivedType).getBaseType() and
|
||||
operandTag instanceof SideEffectOperandTag
|
||||
or
|
||||
tag instanceof OnlyInstructionTag and
|
||||
operandType = arg.getType().getUnspecifiedType() and
|
||||
not operandType instanceof DerivedType and
|
||||
operandTag instanceof SideEffectOperandTag
|
||||
|
|
||||
// If the type we select is an incomplete type (e.g. a forward-declared `struct`), there will
|
||||
// not be a `CppType` that represents that type. In that case, fall back to `UnknownCppType`.
|
||||
result = getTypeForPRValueOrUnknown(operandType)
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasSpecificWriteSideEffect(Opcode op) {
|
||||
@@ -510,7 +554,7 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff
|
||||
)
|
||||
or
|
||||
not call.getTarget() instanceof SideEffectFunction and
|
||||
op instanceof Opcode::IndirectReadSideEffect
|
||||
op instanceof Opcode::BufferReadSideEffect
|
||||
}
|
||||
|
||||
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
|
||||
|
||||
@@ -120,7 +120,9 @@ abstract class TranslatedVariableDeclaration extends TranslatedElement, Initiali
|
||||
|
||||
private predicate hasUninitializedInstruction() {
|
||||
not exists(getInitialization()) or
|
||||
getInitialization() instanceof TranslatedListInitialization
|
||||
getInitialization() instanceof TranslatedListInitialization or
|
||||
getInitialization() instanceof TranslatedConstructorInitialization or
|
||||
getInitialization().(TranslatedStringLiteralInitialization).zeroInitRange(_, _)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ private import InstructionTag
|
||||
private import TranslatedCondition
|
||||
private import TranslatedFunction
|
||||
private import TranslatedStmt
|
||||
private import TranslatedExpr
|
||||
private import IRConstruction
|
||||
private import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
@@ -235,6 +236,15 @@ newtype TTranslatedElement =
|
||||
expr.hasLValueToRValueConversion() and
|
||||
not ignoreLoad(expr)
|
||||
} or
|
||||
TTranslatedResultCopy(Expr expr) {
|
||||
not ignoreExpr(expr) and
|
||||
exprNeedsCopyIfNotLoaded(expr) and
|
||||
// Doesn't have a TTranslatedLoad
|
||||
not (
|
||||
expr.hasLValueToRValueConversion() and
|
||||
not ignoreLoad(expr)
|
||||
)
|
||||
} or
|
||||
// An expression most naturally translated as control flow.
|
||||
TTranslatedNativeCondition(Expr expr) {
|
||||
not ignoreExpr(expr) and
|
||||
@@ -380,8 +390,10 @@ newtype TTranslatedElement =
|
||||
TTranslatedAllocationSize(NewOrNewArrayExpr newExpr) { not ignoreExpr(newExpr) } or
|
||||
// The declaration/initialization part of a `ConditionDeclExpr`
|
||||
TTranslatedConditionDecl(ConditionDeclExpr expr) { not ignoreExpr(expr) } or
|
||||
// The side effects of a `Call` {
|
||||
TTranslatedSideEffects(Call expr) { exists(TTranslatedArgumentSideEffect(expr, _, _, _)) } or // A precise side effect of an argument to a `Call` {
|
||||
// The side effects of a `Call`
|
||||
TTranslatedSideEffects(Call expr) {
|
||||
exists(TTranslatedArgumentSideEffect(expr, _, _, _)) or expr instanceof ConstructorCall
|
||||
} or // A precise side effect of an argument to a `Call`
|
||||
TTranslatedArgumentSideEffect(Call call, Expr expr, int n, boolean isWrite) {
|
||||
(
|
||||
expr = call.getArgument(n).getFullyConverted()
|
||||
|
||||
@@ -62,12 +62,11 @@ abstract class TranslatedExpr extends TranslatedElement {
|
||||
/**
|
||||
* Holds if the result of this `TranslatedExpr` is a glvalue.
|
||||
*/
|
||||
private predicate isResultGLValue() {
|
||||
predicate isResultGLValue() {
|
||||
// This implementation is overridden in `TranslatedCoreExpr` to mark them
|
||||
// as glvalues if they have loads on them. It's not overridden in
|
||||
// `TranslatedResultCopy` since result copies never have loads.
|
||||
expr.isGLValueCategory()
|
||||
or
|
||||
// If this TranslatedExpr doesn't produce the result, then it must represent
|
||||
// a glvalue that is then loaded by a TranslatedLoad.
|
||||
not producesExprResult()
|
||||
}
|
||||
|
||||
final override Locatable getAST() { result = expr }
|
||||
@@ -96,14 +95,28 @@ abstract class TranslatedExpr extends TranslatedElement {
|
||||
abstract class TranslatedCoreExpr extends TranslatedExpr {
|
||||
final override string toString() { result = expr.toString() }
|
||||
|
||||
/**
|
||||
* Holds if the result of this `TranslatedExpr` is a glvalue.
|
||||
*/
|
||||
override predicate isResultGLValue() {
|
||||
super.isResultGLValue()
|
||||
or
|
||||
// If this TranslatedExpr doesn't produce the result, then it must represent
|
||||
// a glvalue that is then loaded by a TranslatedLoad.
|
||||
hasLoad()
|
||||
}
|
||||
|
||||
final predicate hasLoad() {
|
||||
expr.hasLValueToRValueConversion() and
|
||||
not ignoreLoad(expr)
|
||||
}
|
||||
|
||||
final override predicate producesExprResult() {
|
||||
// If there's no load, then this is the only TranslatedExpr for this
|
||||
// expression.
|
||||
not expr.hasLValueToRValueConversion()
|
||||
or
|
||||
// If we're supposed to ignore the load on this expression, then this
|
||||
// is the only TranslatedExpr.
|
||||
ignoreLoad(expr)
|
||||
not hasLoad() and
|
||||
// If there's a result copy, then this expression's result is the copy.
|
||||
not exprNeedsCopyIfNotLoaded(expr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,6 +301,48 @@ class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
|
||||
private TranslatedCoreExpr getOperand() { result.getExpr() = expr }
|
||||
}
|
||||
|
||||
/**
|
||||
* IR translation of an implicit lvalue-to-rvalue conversion on the result of
|
||||
* an expression.
|
||||
*/
|
||||
class TranslatedResultCopy extends TranslatedExpr, TTranslatedResultCopy {
|
||||
TranslatedResultCopy() { this = TTranslatedResultCopy(expr) }
|
||||
|
||||
override string toString() { result = "Result of " + expr.toString() }
|
||||
|
||||
override Instruction getFirstInstruction() { result = getOperand().getFirstInstruction() }
|
||||
|
||||
override TranslatedElement getChild(int id) { id = 0 and result = getOperand() }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = ResultCopyTag() and
|
||||
opcode instanceof Opcode::CopyValue and
|
||||
resultType = getOperand().getResultType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = ResultCopyTag() and
|
||||
result = getParent().getChildSuccessor(this) and
|
||||
kind instanceof GotoEdge
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = getOperand() and result = getInstruction(ResultCopyTag())
|
||||
}
|
||||
|
||||
override Instruction getResult() { result = getInstruction(ResultCopyTag()) }
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = ResultCopyTag() and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = getOperand().getResult()
|
||||
}
|
||||
|
||||
final override predicate producesExprResult() { any() }
|
||||
|
||||
private TranslatedCoreExpr getOperand() { result.getExpr() = expr }
|
||||
}
|
||||
|
||||
class TranslatedCommaExpr extends TranslatedNonConstantExpr {
|
||||
override CommaExpr expr;
|
||||
|
||||
@@ -983,7 +1038,7 @@ class TranslatedInheritanceConversion extends TranslatedSingleInstructionConvers
|
||||
then
|
||||
if expr.(BaseClassConversion).isVirtual()
|
||||
then result instanceof Opcode::ConvertToVirtualBase
|
||||
else result instanceof Opcode::ConvertToBase
|
||||
else result instanceof Opcode::ConvertToNonVirtualBase
|
||||
else result instanceof Opcode::ConvertToDerived
|
||||
}
|
||||
}
|
||||
@@ -2403,6 +2458,58 @@ class TranslatedErrorExpr extends TranslatedSingleInstructionExpr {
|
||||
final override Opcode getOpcode() { result instanceof Opcode::Error }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the translation of `expr` will not directly generate any
|
||||
* `Instruction` for use as result. For such instructions we can synthesize a
|
||||
* `CopyValue` instruction to ensure that there is a 1-to-1 mapping between
|
||||
* expressions and result-bearing instructions.
|
||||
*/
|
||||
// This should ideally be a dispatch predicate on TranslatedNonConstantExpr,
|
||||
// but it doesn't look monotonic to QL.
|
||||
predicate exprNeedsCopyIfNotLoaded(Expr expr) {
|
||||
(
|
||||
expr instanceof AssignExpr
|
||||
or
|
||||
expr instanceof AssignOperation and
|
||||
not expr.isPRValueCategory() // is C++
|
||||
or
|
||||
expr instanceof PrefixCrementOperation and
|
||||
not expr.isPRValueCategory() // is C++
|
||||
or
|
||||
expr instanceof PointerDereferenceExpr
|
||||
or
|
||||
expr instanceof AddressOfExpr
|
||||
or
|
||||
expr instanceof BuiltInOperationBuiltInAddressOf
|
||||
or
|
||||
// No case for ParenthesisExpr to avoid getting too many instructions
|
||||
expr instanceof ReferenceDereferenceExpr
|
||||
or
|
||||
expr instanceof ReferenceToExpr
|
||||
or
|
||||
expr instanceof CommaExpr
|
||||
or
|
||||
expr instanceof ConditionDeclExpr
|
||||
// TODO: simplify TranslatedStmtExpr too
|
||||
) and
|
||||
not exprImmediatelyDiscarded(expr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expr` is immediately discarded. Such expressions do not need a
|
||||
* `CopyValue` because it's unlikely that anyone is interested in their value.
|
||||
*/
|
||||
private predicate exprImmediatelyDiscarded(Expr expr) {
|
||||
exists(ExprStmt s |
|
||||
s = expr.getParent() and
|
||||
not exists(StmtExpr se | s = se.getStmt().(Block).getLastStmt())
|
||||
)
|
||||
or
|
||||
exists(CommaExpr c | c.getLeftOperand() = expr)
|
||||
or
|
||||
exists(ForStmt for | for.getUpdate() = expr)
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of an `__assume` expression. We currently translate these as `NoOp`. In the
|
||||
* future, we will probably want to do something better. At a minimum, we can model `__assume(0)` as
|
||||
|
||||
@@ -100,6 +100,9 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
result = getInstruction(UnmodeledUseTag())
|
||||
or
|
||||
tag = UnmodeledUseTag() and
|
||||
result = getInstruction(AliasedUseTag())
|
||||
or
|
||||
tag = AliasedUseTag() and
|
||||
result = getInstruction(ExitFunctionTag())
|
||||
)
|
||||
}
|
||||
@@ -172,6 +175,10 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
opcode instanceof Opcode::UnmodeledUse and
|
||||
resultType = getVoidType()
|
||||
or
|
||||
tag = AliasedUseTag() and
|
||||
opcode instanceof Opcode::AliasedUse and
|
||||
resultType = getVoidType()
|
||||
or
|
||||
tag = ExitFunctionTag() and
|
||||
opcode instanceof Opcode::ExitFunction and
|
||||
resultType = getVoidType()
|
||||
@@ -192,6 +199,10 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
operandTag instanceof UnmodeledUseOperandTag and
|
||||
result = getUnmodeledDefinitionInstruction()
|
||||
or
|
||||
tag = AliasedUseTag() and
|
||||
operandTag instanceof SideEffectOperandTag and
|
||||
result = getUnmodeledDefinitionInstruction()
|
||||
or
|
||||
tag = ReturnTag() and
|
||||
hasReturnValue() and
|
||||
(
|
||||
@@ -208,6 +219,10 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
hasReturnValue() and
|
||||
operandTag instanceof LoadOperandTag and
|
||||
result = getTypeForPRValue(getReturnType())
|
||||
or
|
||||
tag = AliasedUseTag() and
|
||||
operandTag instanceof SideEffectOperandTag and
|
||||
result = getUnknownType()
|
||||
}
|
||||
|
||||
final override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
|
||||
@@ -340,7 +340,7 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
|
||||
* Holds if the `elementCount` array elements starting at `startIndex` must be
|
||||
* zero initialized.
|
||||
*/
|
||||
private predicate zeroInitRange(int startIndex, int elementCount) {
|
||||
predicate zeroInitRange(int startIndex, int elementCount) {
|
||||
exists(int targetCount |
|
||||
startIndex = expr.getUnspecifiedType().(ArrayType).getArraySize() and
|
||||
targetCount = getContext().getTargetType().getUnspecifiedType().(ArrayType).getArraySize() and
|
||||
@@ -752,7 +752,7 @@ abstract class TranslatedBaseStructorCall extends TranslatedStructorCallFromStru
|
||||
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::ConvertToBase and
|
||||
opcode instanceof Opcode::ConvertToNonVirtualBase and
|
||||
resultType = getTypeForGLValue(call.getTarget().getDeclaringType())
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,12 @@ abstract class IRVariable extends TIRVariable {
|
||||
|
||||
abstract string toString();
|
||||
|
||||
/**
|
||||
* Holds if this variable's value cannot be changed within a function. Currently used for string
|
||||
* literals, but could also apply to `const` global and static variables.
|
||||
*/
|
||||
predicate isReadOnly() { none() }
|
||||
|
||||
/**
|
||||
* Gets the type of the variable.
|
||||
*/
|
||||
@@ -113,34 +119,43 @@ class IRStaticUserVariable extends IRUserVariable {
|
||||
final override Language::StaticVariable getVariable() { result = var }
|
||||
}
|
||||
|
||||
abstract class IRGeneratedVariable extends IRVariable {
|
||||
Language::AST ast;
|
||||
Language::LanguageType type;
|
||||
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
|
||||
final override Language::AST getAST() { result = ast }
|
||||
|
||||
override string toString() { result = getBaseString() + getLocationString() }
|
||||
|
||||
override string getUniqueId() { none() }
|
||||
|
||||
final string getLocationString() {
|
||||
result = ast.getLocation().getStartLine().toString() + ":" +
|
||||
ast.getLocation().getStartColumn().toString()
|
||||
}
|
||||
|
||||
string getBaseString() { none() }
|
||||
}
|
||||
|
||||
IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) {
|
||||
result.getAST() = ast and
|
||||
result.getTag() = tag
|
||||
}
|
||||
|
||||
class IRTempVariable extends IRVariable, IRAutomaticVariable, TIRTempVariable {
|
||||
Language::AST ast;
|
||||
class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable {
|
||||
TempVariableTag tag;
|
||||
Language::LanguageType type;
|
||||
|
||||
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
|
||||
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
|
||||
final override Language::AST getAST() { result = ast }
|
||||
|
||||
final override string getUniqueId() {
|
||||
result = "Temp: " + Construction::getTempVariableUniqueId(this)
|
||||
}
|
||||
|
||||
final TempVariableTag getTag() { result = tag }
|
||||
|
||||
override string toString() {
|
||||
result = getBaseString() + ast.getLocation().getStartLine().toString() + ":" +
|
||||
ast.getLocation().getStartColumn().toString()
|
||||
}
|
||||
|
||||
string getBaseString() { result = "#temp" }
|
||||
override string getBaseString() { result = "#temp" }
|
||||
}
|
||||
|
||||
class IRReturnVariable extends IRTempVariable {
|
||||
@@ -154,3 +169,19 @@ class IRThrowVariable extends IRTempVariable {
|
||||
|
||||
override string getBaseString() { result = "#throw" }
|
||||
}
|
||||
|
||||
class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
||||
Language::StringLiteral literal;
|
||||
|
||||
IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) }
|
||||
|
||||
final override predicate isReadOnly() { any() }
|
||||
|
||||
final override string getUniqueId() {
|
||||
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
|
||||
}
|
||||
|
||||
override string getBaseString() { result = "#string" }
|
||||
|
||||
final Language::StringLiteral getLiteral() { result = literal }
|
||||
}
|
||||
|
||||
@@ -51,7 +51,8 @@ module InstructionSanity {
|
||||
opcode instanceof ReadSideEffectOpcode or
|
||||
opcode instanceof Opcode::InlineAsm or
|
||||
opcode instanceof Opcode::CallSideEffect or
|
||||
opcode instanceof Opcode::ReturnIndirection
|
||||
opcode instanceof Opcode::ReturnIndirection or
|
||||
opcode instanceof Opcode::AliasedUse
|
||||
) and
|
||||
tag instanceof SideEffectOperandTag
|
||||
)
|
||||
@@ -124,6 +125,16 @@ module InstructionSanity {
|
||||
)
|
||||
}
|
||||
|
||||
query predicate duplicateChiOperand(
|
||||
ChiInstruction chi, string message, IRFunction func, string funcText
|
||||
) {
|
||||
chi.getTotal() = chi.getPartial() and
|
||||
message = "Chi instruction for " + chi.getPartial().toString() +
|
||||
" has duplicate operands in function $@" and
|
||||
func = chi.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
|
||||
query predicate sideEffectWithoutPrimary(
|
||||
SideEffectInstruction instr, string message, IRFunction func, string funcText
|
||||
) {
|
||||
@@ -264,6 +275,7 @@ module InstructionSanity {
|
||||
) {
|
||||
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
|
||||
not useOperand.getUse() instanceof UnmodeledUseInstruction and
|
||||
not defInstr instanceof UnmodeledDefinitionInstruction and
|
||||
pointOfEvaluation(useOperand, useBlock, useIndex) and
|
||||
defInstr = useOperand.getAnyDef() and
|
||||
(
|
||||
@@ -827,14 +839,12 @@ class FloatConstantInstruction extends ConstantInstruction {
|
||||
FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
|
||||
}
|
||||
|
||||
class StringConstantInstruction extends Instruction {
|
||||
Language::StringLiteral value;
|
||||
class StringConstantInstruction extends VariableInstruction {
|
||||
override IRStringLiteral var;
|
||||
|
||||
StringConstantInstruction() { value = Construction::getInstructionStringLiteral(this) }
|
||||
final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) }
|
||||
|
||||
final override string getImmediateString() { result = Language::getStringLiteralText(value) }
|
||||
|
||||
final Language::StringLiteral getValue() { result = value }
|
||||
final Language::StringLiteral getValue() { result = var.getLiteral() }
|
||||
}
|
||||
|
||||
class BinaryInstruction extends Instruction {
|
||||
@@ -1002,14 +1012,22 @@ class InheritanceConversionInstruction extends UnaryInstruction {
|
||||
* to the address of a direct non-virtual base class.
|
||||
*/
|
||||
class ConvertToBaseInstruction extends InheritanceConversionInstruction {
|
||||
ConvertToBaseInstruction() { getOpcode() instanceof Opcode::ConvertToBase }
|
||||
ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an instruction that converts from the address of a derived class
|
||||
* to the address of a direct non-virtual base class.
|
||||
*/
|
||||
class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction {
|
||||
ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an instruction that converts from the address of a derived class
|
||||
* to the address of a virtual base class.
|
||||
*/
|
||||
class ConvertToVirtualBaseInstruction extends InheritanceConversionInstruction {
|
||||
class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction {
|
||||
ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase }
|
||||
}
|
||||
|
||||
@@ -1442,6 +1460,13 @@ class AliasedDefinitionInstruction extends Instruction {
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that consumes all escaped memory on exit from the function.
|
||||
*/
|
||||
class AliasedUseInstruction extends Instruction {
|
||||
AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse }
|
||||
}
|
||||
|
||||
class UnmodeledUseInstruction extends Instruction {
|
||||
UnmodeledUseInstruction() { getOpcode() instanceof Opcode::UnmodeledUse }
|
||||
|
||||
|
||||
@@ -396,6 +396,9 @@ class SideEffectOperand extends TypedOperand {
|
||||
override SideEffectOperandTag tag;
|
||||
|
||||
override MemoryAccessKind getMemoryAccess() {
|
||||
useInstr instanceof AliasedUseInstruction and
|
||||
result instanceof NonLocalMayMemoryAccess
|
||||
or
|
||||
useInstr instanceof CallSideEffectInstruction and
|
||||
result instanceof EscapedMayMemoryAccess
|
||||
or
|
||||
|
||||
@@ -110,7 +110,7 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
|
||||
instr = operand.getUse() and
|
||||
(
|
||||
// Converting to a non-virtual base class adds the offset of the base class.
|
||||
exists(ConvertToBaseInstruction convert |
|
||||
exists(ConvertToNonVirtualBaseInstruction convert |
|
||||
convert = instr and
|
||||
bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8)
|
||||
)
|
||||
@@ -309,6 +309,10 @@ predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset)
|
||||
instr.(VariableAddressInstruction).getIRVariable() = var and
|
||||
bitOffset = 0
|
||||
or
|
||||
// A string literal is just a special read-only global variable.
|
||||
instr.(StringConstantInstruction).getIRVariable() = var and
|
||||
bitOffset = 0
|
||||
or
|
||||
exists(Operand operand, IntValue originalBitOffset, IntValue propagatedBitOffset |
|
||||
operand = instr.getAnOperand() and
|
||||
// If an operand is propagated, then the result points to the same variable,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user