Merge remote-tracking branch 'upstream/master' into ir-dataflow-toString

Solved conflicts in `*.expected` by re-running the tests.
This commit is contained in:
Jonas Jensen
2019-11-19 14:27:27 +01:00
2068 changed files with 126638 additions and 59147 deletions

6
.codeqlmanifest.json Normal file
View File

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

14
.github/ISSUE_TEMPLATE/ql---general.md vendored Normal file
View File

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

3
.gitignore vendored
View File

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

View File

@@ -1,10 +1,11 @@
/cpp/ @Semmle/cpp-analysis
/csharp/ @Semmle/cs /csharp/ @Semmle/cs
/java/ @Semmle/java /java/ @Semmle/java
/javascript/ @Semmle/js /javascript/ @Semmle/js
/cpp/ @Semmle/cpp-analysis /python/ @Semmle/python
/cpp/**/*.qhelp @semmledocs-ac /cpp/**/*.qhelp @hubwriter
/csharp/**/*.qhelp @jf205 /csharp/**/*.qhelp @jf205
/java/**/*.qhelp @felicity-semmle /java/**/*.qhelp @felicitymay
/javascript/**/*.qhelp @mc-semmle /javascript/**/*.qhelp @mchammer01
/python/**/*.qhelp @felicity-semmle /python/**/*.qhelp @felicitymay
/docs/language/ @felicity-semmle @jf205 /docs/language/ @shati-patel @jf205

View File

@@ -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! 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. 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. 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** 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. 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. 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: 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 to contact you in relation to your contributions, as well as in the
normal course of software development. We also store records of your normal course of software development. We also store records of your
CLA agreements. Under GDPR legislation, we do this 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 Please do get in touch (privacy@semmle.com) if you have any questions about
this or our data protection policies. this or our data protection policies.

View File

@@ -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. 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. You can use the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com or the [CodeQL for Visual Studio Code](https://help.semmle.com/codeql/codeql-for-vscode.html) extension to try out your queries on any open source project that's currently being analyzed.
## Contributing ## 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 ## 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).

View File

@@ -36,6 +36,7 @@
| Shift out of range (`js/shift-out-of-range`| Fewer false positive results | This rule now correctly handles BigInt shift operands. | | Shift out of range (`js/shift-out-of-range`| Fewer false positive results | This rule now correctly handles BigInt shift operands. |
| Superfluous trailing arguments (`js/superfluous-trailing-arguments`) | Fewer false-positive results. | This rule no longer flags calls to placeholder functions that trivially throw an exception. | | Superfluous trailing arguments (`js/superfluous-trailing-arguments`) | Fewer false-positive results. | This rule no longer flags calls to placeholder functions that trivially throw an exception. |
| Undocumented parameter (`js/jsdoc/missing-parameter`) | No changes to results | This rule is now run on LGTM, although its results are still not shown by default. | | Undocumented parameter (`js/jsdoc/missing-parameter`) | No changes to results | This rule is now run on LGTM, although its results are still not shown by default. |
| Missing space in string concatenation (`js/missing-space-in-concatenation`) | Fewer false positive results | The rule now requires a word-like part exists in the string concatenation. |
## Changes to QL libraries ## Changes to QL libraries

View File

@@ -9,6 +9,8 @@ The following changes in version 1.23 affect C/C++ analysis in all applications.
| **Query** | **Tags** | **Purpose** | | **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------| |-----------------------------|-----------|--------------------------------------------------------------------|
| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | reliability, japanese-era | This query is a combination of two old queries that were identical in purpose but separate as an implementation detail. This new query replaces Hard-coded Japanese era start date in call (`cpp/japanese-era/constructor-or-method-with-exact-era-date`) and Hard-coded Japanese era start date in struct (`cpp/japanese-era/struct-with-exact-era-date`). | | Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | reliability, japanese-era | This query is a combination of two old queries that were identical in purpose but separate as an implementation detail. This new query replaces Hard-coded Japanese era start date in call (`cpp/japanese-era/constructor-or-method-with-exact-era-date`) and Hard-coded Japanese era start date in struct (`cpp/japanese-era/struct-with-exact-era-date`). |
| Signed overflow check (`cpp/signed-overflow-check`) | correctness, security | Finds overflow checks that rely on signed integer addition to overflow, which has undefined behavior. Example: `a + b < a`. |
| Pointer overflow check (`cpp/pointer-overflow-check`) | correctness, security | Finds overflow checks that rely on pointer addition to overflow, which has undefined behavior. Example: `ptr + a < ptr`. |
## Changes to existing queries ## Changes to existing queries
@@ -18,8 +20,15 @@ The following changes in version 1.23 affect C/C++ analysis in all applications.
| Hard-coded Japanese era start date in call (`cpp/japanese-era/constructor-or-method-with-exact-era-date`) | Deprecated | This query has been deprecated. Use the new combined query Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) instead. | | Hard-coded Japanese era start date in call (`cpp/japanese-era/constructor-or-method-with-exact-era-date`) | Deprecated | This query has been deprecated. Use the new combined query Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) instead. |
| Hard-coded Japanese era start date in struct (`cpp/japanese-era/struct-with-exact-era-date`) | Deprecated | This query has been deprecated. Use the new combined query Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) instead. | | Hard-coded Japanese era start date in struct (`cpp/japanese-era/struct-with-exact-era-date`) | Deprecated | This query has been deprecated. Use the new combined query Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) instead. |
| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | More correct results | This query now checks for the beginning date of the Reiwa era (1st May 2019). | | Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | More correct results | This query now checks for the beginning date of the Reiwa era (1st May 2019). |
| Sign check of bitwise operation (`cpp/bitwise-sign-check`) | Fewer false positive results | Results involving `>=` or `<=` are no longer reported. |
| Too few arguments to formatting function (`cpp/wrong-number-format-arguments`) | Fewer false positive results | Fixed false positives resulting from mistmatching declarations of a formatting function. |
| Too many arguments to formatting function (`cpp/too-many-format-arguments`) | Fewer false positive results | Fixed false positives resulting from mistmatching declarations of a formatting function. |
| Unclear comparison precedence (`cpp/comparison-precedence`) | Fewer false positive results | False positives involving template classes and functions have been fixed. |
| Comparison of narrow type with wide type in loop condition (`cpp/comparison-with-wider-type`) | Higher precision | The precision of this query has been increased to "high" as the alerts from this query have proved to be valuable on real-world projects. With this precision, results are now displayed by default in LGTM. |
| 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. * 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 Instead of specifying `isSink(Node n) { any() }` on a configuration to
@@ -28,10 +37,31 @@ The following changes in version 1.23 affect C/C++ analysis in all applications.
picture of the partial flow paths from a given source. The feature is picture of the partial flow paths from a given source. The feature is
disabled by default and can be enabled for individual configurations by disabled by default and can be enabled for individual configurations by
overriding `int explorationLimit()`. overriding `int explorationLimit()`.
* The data-flow library now supports flow out of C++ reference parameters.
* The data-flow library now allows flow through the address-of operator (`&`).
* The `DataFlow::DefinitionByReferenceNode` class now considers `f(x)` to be a * The `DataFlow::DefinitionByReferenceNode` class now considers `f(x)` to be a
definition of `x` when `x` is a variable of pointer type. It no longer 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 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. 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 * There is now a `DataFlow::localExprFlow` predicate and a
`TaintTracking::localExprTaint` predicate to make it easy to use the most `TaintTracking::localExprTaint` predicate to make it easy to use the most
common case of local data flow and taint: from one `Expr` to another. common case of local data flow and taint: from one `Expr` to another.
* The member predicates of the `FunctionInput` and `FunctionOutput` classes have been renamed for
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.

View File

@@ -8,13 +8,18 @@ The following changes in version 1.23 affect C# analysis in all applications.
| **Query** | **Tags** | **Purpose** | | **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 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. | | 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 ## Changes to existing queries
| **Query** | **Expected impact** | **Change** | | **Query** | **Expected impact** | **Change** |
|------------------------------|------------------------|-----------------------------------| |------------------------------|------------------------|-----------------------------------|
| Dereferenced variable may be null (`cs/dereferenced-value-may-be-null`) | Fewer false positive results | More `null` checks are now taken into account, including `null` checks for `dynamic` expressions and `null` checks such as `object alwaysNull = null; if (x != alwaysNull) ...`. |
| Missing Dispose call on local IDisposable (`cs/local-not-disposed`) | Fewer false positive results | The query has been rewritten in order to identify more dispose patterns. For example, a local `IDisposable` that is disposed of by passing through a fluent API is no longer reported. |
## Removal of old queries ## Removal of old queries
@@ -22,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. * `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 new class `NamespaceAccess` models accesses to namespaces, for example in `nameof` expressions.
* The data-flow library now makes it easier to specify barriers/sanitizers * The data-flow library now makes it easier to specify barriers/sanitizers
@@ -37,5 +42,11 @@ The following changes in version 1.23 affect C# analysis in all applications.
disabled by default and can be enabled for individual configurations by disabled by default and can be enabled for individual configurations by
overriding `int explorationLimit()`. overriding `int explorationLimit()`.
* `foreach` statements where the body is guaranteed to be executed at least once, such as `foreach (var x in new string[]{ "a", "b", "c" }) { ... }`, are now recognized by all analyses based on the control flow graph (such as SSA, data flow and taint tracking). * `foreach` statements where the body is guaranteed to be executed at least once, such as `foreach (var x in new string[]{ "a", "b", "c" }) { ... }`, are now recognized by all analyses based on the control flow graph (such as SSA, data flow and taint tracking).
* Fixed the control flow graph for `switch` statements where the `default` case was not the last case. This had caused the remaining cases to be unreachable. `SwitchStmt.getCase(int i)` now puts the `default` case last.
* There is now a `DataFlow::localExprFlow` predicate and a
`TaintTracking::localExprTaint` predicate to make it easy to use the most
common case of local data flow and taint: from one `Expr` to another.
* Data is now tracked through null-coalescing expressions (`??`).
* 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 ## Changes to autobuilder

View File

@@ -2,15 +2,24 @@
The following changes in version 1.23 affect Java analysis in all applications. The following changes in version 1.23 affect Java analysis in all applications.
## New queries
| **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------|
| Continue statement that does not continue (`java/continue-in-false-loop`) | correctness | Finds `continue` statements in `do { ... } while (false)` loops. |
## Changes to existing queries ## Changes to existing queries
| **Query** | **Expected impact** | **Change** | | **Query** | **Expected impact** | **Change** |
|------------------------------|------------------------|-----------------------------------| |------------------------------|------------------------|-----------------------------------|
| Dereferenced variable may be null (`java/dereferenced-value-may-be-null`) | Fewer false positives | Certain indirect null guards involving two auxiliary variables known to be equal can now be detected. |
| Non-synchronized override of synchronized method (`java/non-sync-override`) | Fewer false positives | Results are now only reported if the immediately overridden method is synchronized. |
| Query built from user-controlled sources (`java/sql-injection`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as SQL expressions sinks. | | Query built from user-controlled sources (`java/sql-injection`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as SQL expressions sinks. |
| Query built from local-user-controlled sources (`java/sql-injection-local`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as SQL expressions sinks. | | Query built from local-user-controlled sources (`java/sql-injection-local`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as SQL expressions sinks. |
| Query built without neutralizing special characters (`java/concatenated-sql-query`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as SQL expressions sinks. | | 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. * 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 Instead of specifying `isSink(Node n) { any() }` on a configuration to

View File

@@ -2,30 +2,81 @@
## General improvements ## General improvements
* 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: * Support for the following frameworks and libraries has been improved:
- [firebase](https://www.npmjs.com/package/firebase) - [firebase](https://www.npmjs.com/package/firebase)
- [get-them-args](https://www.npmjs.com/package/get-them-args)
- [minimist](https://www.npmjs.com/package/minimist)
- [mongodb](https://www.npmjs.com/package/mongodb) - [mongodb](https://www.npmjs.com/package/mongodb)
- [mongoose](https://www.npmjs.com/package/mongoose) - [mongoose](https://www.npmjs.com/package/mongoose)
- [optimist](https://www.npmjs.com/package/optimist)
- [parse-torrent](https://www.npmjs.com/package/parse-torrent)
- [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible)
- [yargs](https://www.npmjs.com/package/yargs)
* The call graph has been improved to resolve method calls in more cases. This may produce more security alerts. * The call graph has been improved to resolve method calls in more cases. This may produce more security alerts.
* TypeScript 3.6 and 3.7 features are now supported.
## New queries ## New queries
| **Query** | **Tags** | **Purpose** | | **Query** | **Tags** | **Purpose** |
|---------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |---------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Unused index variable (`js/unused-index-variable`) | correctness | Highlights loops that iterate over an array, but do not use the index variable to access array elements, indicating a possible typo or logic error. | | Ignoring result from pure array method (`js/ignore-array-result`) | maintainability, correctness | Highlights calls to array methods without side effects where the return value is ignored. Results are shown on LGTM by default. |
| Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | security, correctness, external/cwe/cwe-020 | Highlights checks for `javascript:` URLs that do not take `data:` or `vbscript:` URLs into account. Results are shown on LGTM by default. |
| Loop bound injection (`js/loop-bound-injection`) | security, external/cwe/cwe-834 | Highlights loops where a user-controlled object with an arbitrary .length value can trick the server to loop indefinitely. Results are shown on LGTM by default. |
| Shell command built from environment values (`js/shell-command-injection-from-environment`) | correctness, security, external/cwe/cwe-078, external/cwe/cwe-088 | Highlights shell commands that may change behavior inadvertently depending on the execution environment, indicating a possible violation of [CWE-78](https://cwe.mitre.org/data/definitions/78.html). Results are shown on LGTM by default.|
| Suspicious method name (`js/suspicious-method-name-declaration`) | correctness, typescript, methods | Highlights suspiciously named methods where the developer likely meant to write a constructor or function. Results are shown on LGTM by default. |
| 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. |
| Unused index variable (`js/unused-index-variable`) | correctness | Highlights loops that iterate over an array, but do not use the index variable to access array elements, indicating a possible typo or logic error. Results are shown on LGTM by default. |
| 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. |
## Changes to existing queries ## Changes to existing queries
| **Query** | **Expected impact** | **Change** | | **Query** | **Expected impact** | **Change** |
|--------------------------------|------------------------------|---------------------------------------------------------------------------| |--------------------------------|------------------------------|---------------------------------------------------------------------------|
| Incomplete string escaping or encoding (`js/incomplete-sanitization`) | Fewer false-positive results | This rule now recognizes additional ways delimiters can be stripped away. | | Incomplete string escaping or encoding (`js/incomplete-sanitization`) | Fewer false-positive results | This rule now recognizes additional ways delimiters can be stripped away. |
| Client-side cross-site scripting (`js/xss`) | More results | More potential vulnerabilities involving functions that manipulate DOM attributes are now recognized. | | Client-side cross-site scripting (`js/xss`) | More results, fewer false-positive results | More potential vulnerabilities involving functions that manipulate DOM attributes are now recognized, and more sanitizers are detected. |
| Code injection (`js/code-injection`) | More results | More potential vulnerabilities involving functions that manipulate DOM event handler attributes are now recognized. | | Code injection (`js/code-injection`) | More results | More potential vulnerabilities involving functions that manipulate DOM event handler attributes are now recognized. |
| Prototype pollution (`js/prototype-pollution`) | More results | The query now highlights vulnerable uses of jQuery and Angular, and the results are shown on LGTM by default. | | Hard-coded credentials (`js/hardcoded-credentials`) | Fewer false-positive results | This rule now flags fewer password examples. |
| Illegal invocation (`js/illegal-invocation`) | Fewer false-positive results | This rule now correctly handles methods named `call` and `apply`. |
| Incorrect suffix check (`js/incorrect-suffix-check`) | Fewer false-positive results | The query recognizes valid checks in more cases. | | Incorrect suffix check (`js/incorrect-suffix-check`) | Fewer false-positive results | The query recognizes valid checks in more cases. |
| Network data written to file (`js/http-to-file-access`) | Fewer false-positive results | This query has been renamed to better match its intended purpose, and now only considers network data untrusted. |
| Password in configuration file (`js/password-in-configuration-file`) | Fewer false-positive results | This rule now flags fewer password examples. |
| Prototype pollution (`js/prototype-pollution`) | More results | The query now highlights vulnerable uses of jQuery and Angular, and the results are shown on LGTM by default. |
| Reflected cross-site scripting (`js/reflected-xss`) | Fewer false-positive results | The query now recognizes more sanitizers. |
| Stored cross-site scripting (`js/stored-xss`) | Fewer false-positive results | The query now recognizes more sanitizers. |
| Uncontrolled command line (`js/command-line-injection`) | More results | This query now treats responses from servers as untrusted. |
| Uncontrolled data used in path expression (`js/path-injection`) | Fewer false-positive results | This query now recognizes calls to Express `sendFile` as safe in some cases. |
| Unknown directive (`js/unknown-directive`) | Fewer false positive results | This query no longer flags uses of ":", which is sometimes used like a directive. |
## Changes to QL libraries ## Changes to libraries
* `Expr.getDocumentation()` now handles chain assignments. * `Expr.getDocumentation()` now handles chain assignments.
* String literals are now parsed as regular expressions.
Consequently, a `RegExpTerm` may occur as part of a string literal or
as a regular expression literal. Queries that search for regular expressions may need to
use `RegExpTerm.isPartOfRegExpLiteral` or `RegExpTerm.isUsedAsRegExp` to restrict the search.
A regular expression AST can be obtained from a string literal using `StringLiteral.asRegExp`.
## Removal of deprecated queries
The following queries (deprecated since 1.17) are no longer available in the distribution:
* Builtin redefined (js/builtin-redefinition)
* Inefficient method definition (js/method-definition-in-constructor)
* Bad parity check (js/incomplete-parity-check)
* Potentially misspelled property or variable name (js/wrong-capitalization)
* Unknown JSDoc tag (js/jsdoc/unknown-tag-type)
* Invalid JSLint directive (js/jslint/invalid-directive)
* Malformed JSLint directive (js/jslint/malformed-directive)
* Use of HTML comments (js/html-comment)
* Multi-line string literal (js/multi-line-string)
* Octal literal (js/octal-literal)
* Reserved word used as variable name (js/use-of-reserved-word)
* Trailing comma in array or object expressions (js/trailing-comma-in-array-or-object)
* Call to parseInt without radix (js/parseint-without-radix)

View File

@@ -11,4 +11,17 @@
|-----------|----------|-------------| |-----------|----------|-------------|
| Clear-text logging of sensitive information (`py/clear-text-logging-sensitive-data`) | security, external/cwe/cwe-312 | Finds instances where sensitive information is logged without encryption or hashing. Results are shown on LGTM by default. | | Clear-text logging of sensitive information (`py/clear-text-logging-sensitive-data`) | security, external/cwe/cwe-312 | Finds instances where sensitive information is logged without encryption or hashing. Results are shown on LGTM by default. |
| Clear-text storage of sensitive information (`py/clear-text-storage-sensitive-data`) | security, external/cwe/cwe-312 | Finds instances where sensitive information is stored without encryption or hashing. Results are shown on LGTM by default. | | Clear-text storage of sensitive information (`py/clear-text-storage-sensitive-data`) | security, external/cwe/cwe-312 | Finds instances where sensitive information is stored without encryption or hashing. Results are shown on LGTM by default. |
| Binding a socket to all network interfaces (`py/bind-socket-all-network-interfaces`) | security | Finds instances where a socket is bound to all network interfaces. Results are shown on LGTM by default. |
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
|----------------------------|------------------------|------------|
| Unreachable code | Fewer false positives | Analysis now accounts for uses of `contextlib.suppress` to suppress exceptions. |
| `__iter__` method returns a non-iterator | Better alert message | Alert now highlights which class is expected to be an iterator. |
## Changes to QL libraries
* Django library now recognizes positional arguments from a `django.conf.urls.url` regex (Django version 1.x)

View File

@@ -5,3 +5,19 @@
## Changes to code extraction ## Changes to code extraction
* Asynchronous generator methods are now parsed correctly and no longer cause a spurious syntax error. * Asynchronous generator methods are now parsed correctly and no longer cause a spurious syntax error.
* Files in `node_modules` and `bower_components` folders are no longer extracted by default. If you still want to extract files from these folders, you can add the following filters to your `lgtm.yml` file (or add them to existing filters):
```yaml
extraction:
javascript:
index:
filters:
- include: "**/node_modules"
- include: "**/bower_components"
```
* Recognition of CommonJS modules has improved. As a result, some files that were previously extracted as
global scripts are now extracted as modules.
* Top-level `await` is now supported.
* A bug was fixed in how the TypeScript extractor handles default-exported anonymous classes.
* A bug was fixed in how the TypeScript extractor handles computed instance field names.

View File

@@ -1,6 +1,6 @@
# Files moved to ``docs`` directory # 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. 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: They're now stored as part of the Sphinx ``support`` project with the other documentation:
``docs/ql-documentation/support``. ``docs/language/support``.

View File

@@ -47,31 +47,40 @@
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll" "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll"
], ],
"IR IRBlock": [ "IR IRBlock": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRBlock.qll" "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRBlock.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRBlock.qll"
], ],
"IR IRVariable": [ "IR IRVariable": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll" "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll"
], ],
"IR IRFunction": [ "IR IRFunction": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRFunction.qll" "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRFunction.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRFunction.qll"
], ],
"IR Operand": [ "IR Operand": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Operand.qll" "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Operand.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Operand.qll"
],
"IR IRType": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/IRType.qll"
], ],
"IR Operand Tag": [ "IR Operand Tag": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
@@ -85,19 +94,22 @@
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IR.qll" "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IR.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IR.qll"
], ],
"IR IRSanity": [ "IR IRSanity": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRSanity.qll" "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRSanity.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRSanity.qll"
], ],
"IR PrintIR": [ "IR PrintIR": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/PrintIR.qll" "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/PrintIR.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/PrintIR.qll"
], ],
"IR IntegerConstant": [ "IR IntegerConstant": [
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll", "cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll",
@@ -161,22 +173,39 @@
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintIRImports.qll" "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintIRImports.qll"
], ],
"C++ SSA SSAConstructionImports": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll"
],
"C++ SSA AliasAnalysis": [ "C++ SSA AliasAnalysis": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll" "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll"
], ],
"C++ SSA SSAConstruction": [ "C++ IR ValueNumberingImports": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingImports.qll"
],
"IR SSA SimpleSSA": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll"
],
"IR SSA SSAConstruction": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll" "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll"
], ],
"C++ SSA PrintSSA": [ "IR SSA PrintSSA": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll" "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll"
], ],
"C++ IR ValueNumber": [ "IR ValueNumber": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll" "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/ValueNumbering.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll"
], ],
"C++ IR ConstantAnalysis": [ "C++ IR ConstantAnalysis": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll",
@@ -205,21 +234,31 @@
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll" "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
], ],
"C# IR InstructionImports": [ "C# IR InstructionImports": [
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionImports.qll" "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionImports.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll"
], ],
"C# IR IRImports": [ "C# IR IRImports": [
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRImports.qll" "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRImports.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRImports.qll"
], ],
"C# IR IRBlockImports": [ "C# IR IRBlockImports": [
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRBlockImports.qll" "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRBlockImports.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll"
], ],
"C# IR IRVariableImports": [ "C# IR IRVariableImports": [
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRVariableImports.qll" "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRVariableImports.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll"
], ],
"C# IR OperandImports": [ "C# IR OperandImports": [
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/OperandImports.qll" "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/OperandImports.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/OperandImports.qll"
], ],
"C# IR PrintIRImports": [ "C# IR PrintIRImports": [
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/PrintIRImports.qll" "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/PrintIRImports.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll"
],
"C# IR ValueNumberingImports": [
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll"
] ]
} }

View File

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

View File

@@ -13,7 +13,7 @@ import semmle.code.cpp.pointsto.PointsTo
import Negativity import Negativity
predicate closeCall(FunctionCall fc, Variable v) { 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 or
exists(FunctionCall midcall, Function mid, int arg | exists(FunctionCall midcall, Function mid, int arg |
fc.getArgument(arg) = v.getAnAccess() and fc.getArgument(arg) = v.getAnAccess() and

View File

@@ -13,7 +13,7 @@ import semmle.code.cpp.pointsto.PointsTo
predicate closed(Expr e) { predicate closed(Expr e) {
exists(FunctionCall fc | exists(FunctionCall fc |
fc.getTarget().hasGlobalName("close") and fc.getTarget().hasGlobalOrStdName("close") and
fc.getArgument(0) = e fc.getArgument(0) = e
) )
} }

View File

@@ -53,7 +53,7 @@ predicate allocCallOrIndirect(Expr e) {
* can cause memory leaks. * can cause memory leaks.
*/ */
predicate verifiedRealloc(FunctionCall reallocCall, Variable v, ControlFlowNode verified) { predicate verifiedRealloc(FunctionCall reallocCall, Variable v, ControlFlowNode verified) {
reallocCall.getTarget().hasGlobalName("realloc") and reallocCall.getTarget().hasGlobalOrStdName("realloc") and
reallocCall.getArgument(0) = v.getAnAccess() and reallocCall.getArgument(0) = v.getAnAccess() and
( (
exists(Variable newV, ControlFlowNode node | exists(Variable newV, ControlFlowNode node |
@@ -79,7 +79,7 @@ predicate verifiedRealloc(FunctionCall reallocCall, Variable v, ControlFlowNode
predicate freeCallOrIndirect(ControlFlowNode n, Variable v) { predicate freeCallOrIndirect(ControlFlowNode n, Variable v) {
// direct free call // direct free call
freeCall(n, v.getAnAccess()) and freeCall(n, v.getAnAccess()) and
not n.(FunctionCall).getTarget().hasGlobalName("realloc") not n.(FunctionCall).getTarget().hasGlobalOrStdName("realloc")
or or
// verified realloc call // verified realloc call
verifiedRealloc(_, v, n) verifiedRealloc(_, v, n)

View File

@@ -13,10 +13,7 @@
import cpp import cpp
class MallocCall extends FunctionCall { class MallocCall extends FunctionCall {
MallocCall() { MallocCall() { this.getTarget().hasGlobalOrStdName("malloc") }
this.getTarget().hasGlobalName("malloc") or
this.getTarget().hasQualifiedName("std", "malloc")
}
Expr getAllocatedSize() { Expr getAllocatedSize() {
if this.getArgument(0) instanceof VariableAccess if this.getArgument(0) instanceof VariableAccess
@@ -36,12 +33,12 @@ predicate spaceProblem(FunctionCall append, string msg) {
malloc.getAllocatedSize() = add and malloc.getAllocatedSize() = add and
buffer.getAnAccess() = strlen.getStringExpr() and buffer.getAnAccess() = strlen.getStringExpr() and
( (
insert.getTarget().hasGlobalName("strcpy") or insert.getTarget().hasGlobalOrStdName("strcpy") or
insert.getTarget().hasGlobalName("strncpy") insert.getTarget().hasGlobalOrStdName("strncpy")
) and ) and
( (
append.getTarget().hasGlobalName("strcat") or append.getTarget().hasGlobalOrStdName("strcat") or
append.getTarget().hasGlobalName("strncat") append.getTarget().hasGlobalOrStdName("strncat")
) and ) and
malloc.getASuccessor+() = insert and malloc.getASuccessor+() = insert and
insert.getArgument(1) = buffer.getAnAccess() and insert.getArgument(1) = buffer.getAnAccess() and

View File

@@ -25,7 +25,7 @@ import semmle.code.cpp.security.TaintTracking
predicate sourceSized(FunctionCall fc, Expr src) { predicate sourceSized(FunctionCall fc, Expr src) {
exists(string name | exists(string name |
(name = "strncpy" or name = "strncat" or name = "memcpy" or name = "memmove") and (name = "strncpy" or name = "strncat" or name = "memcpy" or name = "memmove") and
fc.getTarget().hasGlobalName(name) fc.getTarget().hasGlobalOrStdName(name)
) and ) and
exists(Expr dest, Expr size, Variable v | exists(Expr dest, Expr size, Variable v |
fc.getArgument(0) = dest and fc.getArgument(0) = dest and

View File

@@ -60,19 +60,19 @@ predicate overflowOffsetInLoop(BufferAccess bufaccess, string msg) {
predicate bufferAndSizeFunction(Function f, int buf, int size) { predicate bufferAndSizeFunction(Function f, int buf, int size) {
f.hasGlobalName("read") and buf = 1 and size = 2 f.hasGlobalName("read") and buf = 1 and size = 2
or or
f.hasGlobalName("fgets") and buf = 0 and size = 1 f.hasGlobalOrStdName("fgets") and buf = 0 and size = 1
or or
f.hasGlobalName("strncpy") and buf = 0 and size = 2 f.hasGlobalOrStdName("strncpy") and buf = 0 and size = 2
or or
f.hasGlobalName("strncat") and buf = 0 and size = 2 f.hasGlobalOrStdName("strncat") and buf = 0 and size = 2
or or
f.hasGlobalName("memcpy") and buf = 0 and size = 2 f.hasGlobalOrStdName("memcpy") and buf = 0 and size = 2
or or
f.hasGlobalName("memmove") and buf = 0 and size = 2 f.hasGlobalOrStdName("memmove") and buf = 0 and size = 2
or or
f.hasGlobalName("snprintf") and buf = 0 and size = 1 f.hasGlobalOrStdName("snprintf") and buf = 0 and size = 1
or or
f.hasGlobalName("vsnprintf") and buf = 0 and size = 1 f.hasGlobalOrStdName("vsnprintf") and buf = 0 and size = 1
} }
class CallWithBufferSize extends FunctionCall { class CallWithBufferSize extends FunctionCall {

View File

@@ -17,12 +17,12 @@ import cpp
class Allocation extends FunctionCall { class Allocation extends FunctionCall {
Allocation() { Allocation() {
exists(string name | exists(string name |
this.getTarget().hasGlobalName(name) and this.getTarget().hasGlobalOrStdName(name) and
(name = "malloc" or name = "calloc" or name = "realloc") (name = "malloc" or name = "calloc" or name = "realloc")
) )
} }
private string getName() { this.getTarget().hasGlobalName(result) } private string getName() { this.getTarget().hasGlobalOrStdName(result) }
int getSize() { int getSize() {
this.getName() = "malloc" and this.getName() = "malloc" and

View File

@@ -17,12 +17,12 @@ import cpp
class Allocation extends FunctionCall { class Allocation extends FunctionCall {
Allocation() { Allocation() {
exists(string name | exists(string name |
this.getTarget().hasGlobalName(name) and this.getTarget().hasGlobalOrStdName(name) and
(name = "malloc" or name = "calloc" or name = "realloc") (name = "malloc" or name = "calloc" or name = "realloc")
) )
} }
private string getName() { this.getTarget().hasGlobalName(result) } private string getName() { this.getTarget().hasGlobalOrStdName(result) }
int getSize() { int getSize() {
this.getName() = "malloc" and this.getName() = "malloc" and

View File

@@ -16,7 +16,7 @@ import semmle.code.cpp.controlflow.LocalScopeVariableReachability
predicate isFreeExpr(Expr e, LocalScopeVariable v) { predicate isFreeExpr(Expr e, LocalScopeVariable v) {
exists(VariableAccess va | va.getTarget() = v | exists(VariableAccess va | va.getTarget() = v |
exists(FunctionCall fc | fc = e | exists(FunctionCall fc | fc = e |
fc.getTarget().hasGlobalName("free") and fc.getTarget().hasGlobalOrStdName("free") and
va = fc.getArgument(0) va = fc.getArgument(0)
) )
or or

View File

@@ -59,7 +59,7 @@ class Options extends string {
predicate exits(Function f) { predicate exits(Function f) {
f.getAnAttribute().hasName("noreturn") f.getAnAttribute().hasName("noreturn")
or or
exists(string name | f.hasGlobalName(name) | exists(string name | f.hasGlobalOrStdName(name) |
name = "exit" or name = "exit" or
name = "_exit" or name = "_exit" or
name = "abort" or name = "abort" or
@@ -91,7 +91,7 @@ class Options extends string {
* By default holds only for `fgets`. * By default holds only for `fgets`.
*/ */
predicate alwaysCheckReturnValue(Function f) { predicate alwaysCheckReturnValue(Function f) {
f.hasGlobalName("fgets") or f.hasGlobalOrStdName("fgets") or
CustomOptions::alwaysCheckReturnValue(f) // old Options.qll CustomOptions::alwaysCheckReturnValue(f) // old Options.qll
} }

View File

@@ -28,7 +28,8 @@ where
not bf.getType().hasName("BOOL") and not bf.getType().hasName("BOOL") and
// If this is true, then there cannot be unsigned sign extension or overflow. // If this is true, then there cannot be unsigned sign extension or overflow.
not bf.getDeclaredNumBits() = bf.getType().getSize() * 8 and not bf.getDeclaredNumBits() = bf.getType().getSize() * 8 and
not bf.isAnonymous() not bf.isAnonymous() and
not bf.isFromUninstantiatedTemplate(_)
select bf, select bf,
"Bit field " + bf.getName() + " of type " + bf.getUnderlyingType().getName() + "Bit field " + bf.getName() + " of type " + bf.getUnderlyingType().getName() +
" should have explicitly unsigned integral, explicitly signed integral, or enumeration type." " should have explicitly unsigned integral, explicitly signed integral, or enumeration type."

View File

@@ -2,36 +2,39 @@
"-//Semmle//qhelp//EN" "-//Semmle//qhelp//EN"
"qhelp.dtd"> "qhelp.dtd">
<qhelp> <qhelp>
<overview>
<p> <overview>
Checking for overflow of integer addition needs to be done with <p>
care, because automatic type promotion can prevent the check Checking for overflow of integer addition needs to be done with
from working correctly. care, because automatic type promotion can prevent the check
</p> from working as intended, with the same value (<code>true</code>
</overview> or <code>false</code>) always being returned.
<recommendation> </p>
<p> </overview>
Use an explicit cast to make sure that the result of the addition is <recommendation>
not implicitly converted to a larger type. <p>
</p> Use an explicit cast to make sure that the result of the addition is
</recommendation> not implicitly converted to a larger type.
<example> </p>
<sample src="BadAdditionOverflowCheckExample1.cpp" /> </recommendation>
<p> <example>
On a typical architecture where <tt>short</tt> is 16 bits <sample src="BadAdditionOverflowCheckExample1.cpp" />
and <tt>int</tt> is 32 bits, the operands of the addition are <p>
automatically promoted to <tt>int</tt>, so it cannot overflow On a typical architecture where <code>short</code> is 16 bits
and the result of the comparison is always false. and <code>int</code> is 32 bits, the operands of the addition are
</p> automatically promoted to <code>int</code>, so it cannot overflow
<p> and the result of the comparison is always false.
The code below implements the check correctly, by using an </p>
explicit cast to make sure that the result of the addition <p>
is <tt>unsigned short</tt>. The code below implements the check correctly, by using an
</p> explicit cast to make sure that the result of the addition
<sample src="BadAdditionOverflowCheckExample2.cpp" /> is <code>unsigned short</code> (which may overflow, in which case
</example> the comparison would evaluate to <code>true</code>).
<references> </p>
<li><a href="http://c-faq.com/expr/preservingrules.html">Preserving Rules</a></li> <sample src="BadAdditionOverflowCheckExample2.cpp" />
<li><a href="https://www.securecoding.cert.org/confluence/plugins/servlet/mobile#content/view/20086942">Understand integer conversion rules</a></li> </example>
</references> <references>
<li><a href="http://c-faq.com/expr/preservingrules.html">Preserving Rules</a></li>
<li><a href="https://www.securecoding.cert.org/confluence/plugins/servlet/mobile#content/view/20086942">Understand integer conversion rules</a></li>
</references>
</qhelp> </qhelp>

View File

@@ -1,3 +1,4 @@
bool checkOverflow(unsigned short x, unsigned short y) { bool checkOverflow(unsigned short x, unsigned short y) {
return (x + y < x); // BAD: x and y are automatically promoted to int. // BAD: comparison is always false due to type promotion
return (x + y < x);
} }

View File

@@ -14,9 +14,16 @@ import cpp
from RelationalOperation e, BinaryBitwiseOperation lhs from RelationalOperation e, BinaryBitwiseOperation lhs
where where
lhs = e.getGreaterOperand() and // `lhs > 0` (or `0 < lhs`)
lhs.getActualType().(IntegralType).isSigned() and // (note that `lhs < 0`, `lhs >= 0` or `lhs <= 0` all imply that the signedness of
forall(int op | op = lhs.(BitwiseAndExpr).getAnOperand().getValue().toInt() | op < 0) and // `lhs` is understood, so should not be flagged).
(e instanceof GTExpr or e instanceof LTExpr) and
e.getGreaterOperand() = lhs and
e.getLesserOperand().getValue() = "0" and e.getLesserOperand().getValue() = "0" and
// lhs is signed
lhs.getActualType().(IntegralType).isSigned() and
// if `lhs` has the form `x & c`, with constant `c`, `c` is negative
forall(int op | op = lhs.(BitwiseAndExpr).getAnOperand().getValue().toInt() | op < 0) and
// exception for cases involving macros
not e.isAffectedByMacro() not e.isAffectedByMacro()
select e, "Potential unsafe sign check of a bitwise operation." select e, "Potential unsafe sign check of a bitwise operation."

View File

@@ -14,5 +14,8 @@
import cpp import cpp
from ComparisonOperation co, ComparisonOperation chco from ComparisonOperation co, ComparisonOperation chco
where co.getAChild() = chco and not chco.isParenthesised() where
select co, "Check the comparison operator precedence." co.getAChild() = chco and
not chco.isParenthesised() and
not co.isFromUninstantiatedTemplate(_)
select co, "Comparison as an operand to another comparison."

View File

@@ -13,17 +13,13 @@
import cpp import cpp
import PointlessSelfComparison import PointlessSelfComparison
import semmle.code.cpp.commons.Exclusions
from ComparisonOperation cmp from ComparisonOperation cmp
where where
pointlessSelfComparison(cmp) and pointlessSelfComparison(cmp) and
not nanTest(cmp) and not nanTest(cmp) and
not overflowTest(cmp) and not overflowTest(cmp) and
not exists(MacroInvocation mi | not cmp.isFromTemplateInstantiation(_) and
// cmp is in mi not isFromMacroDefinition(cmp)
mi.getAnExpandedElement() = cmp and
// and cmp was apparently not passed in as a macro parameter
cmp.getLocation().getStartLine() = mi.getLocation().getStartLine() and
cmp.getLocation().getStartColumn() = mi.getLocation().getStartColumn()
)
select cmp, "Self comparison." select cmp, "Self comparison."

View File

@@ -0,0 +1,3 @@
bool foo(int n1, unsigned short delta) {
return n1 + delta < n1; // BAD
}

View File

@@ -0,0 +1,4 @@
bool bar(unsigned short n1, unsigned short delta) {
// NB: Comparison is always false
return n1 + delta < n1; // GOOD (but misleading)
}

View File

@@ -0,0 +1,4 @@
#include <limits.h>
bool foo(int n1, unsigned short delta) {
return n1 > INT_MAX - delta; // GOOD
}

View File

@@ -0,0 +1,3 @@
bool bar(unsigned short n1, unsigned short delta) {
return (unsigned short)(n1 + delta) < n1; // GOOD
}

View 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 &lt; 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 &lt; i</code>,
it is possible to rewrite it as <code>(unsigned short)(i + delta)&nbsp;&lt;&nbsp;i</code>.
Note that <code>i + delta</code>does not actually overflow, due to <code>int</code> promotion
</p>
<p>
Given <code>unsigned short i, delta</code> and <code>i + delta &lt; i</code>,
it is also possible to rewrite it as <code>USHORT_MAX - delta</code>. It must be true
that <code>delta &gt; 0</code> and the <code>limits.h</code> or <code>climits</code>
header has been included.
</p>
<p>
Given <code>int i, delta</code> and <code>i + delta &lt; i</code>,
it is possible to rewrite it as <code>INT_MAX - delta</code>. It must be true
that <code>delta &gt; 0</code> and the <code>limits.h</code> or <code>climits</code>
header has been included.
</p>
<p>
Given <code>int i, delta</code> and <code>i + delta &lt; i</code>,
it is also possible to rewrite it as <code>(unsigned)i + delta &lt; i</code>.
Note that program semantics are affected by this change.
</p>
<p>
Given <code>int i, delta</code> and <code>i + delta &lt; i</code>,
it is also possible to rewrite it as <code>unsigned int i, delta</code> and
<code>i + delta &lt; i</code>. Note that program semantics are
affected by this change.
</p>
</recommendation>
<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 &lt; 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>

View File

@@ -0,0 +1,31 @@
/**
* @name Signed overflow check
* @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 correctness
* 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."

View File

@@ -157,7 +157,8 @@ where
formatOtherArgType(ffc, n, expected, arg, actual) and formatOtherArgType(ffc, n, expected, arg, actual) and
not actual.getUnspecifiedType().(IntegralType).getSize() = sizeof_IntType() not actual.getUnspecifiedType().(IntegralType).getSize() = sizeof_IntType()
) and ) and
not arg.isAffectedByMacro() not arg.isAffectedByMacro() and
not arg.isFromUninstantiatedTemplate(_)
select arg, select arg,
"This argument should be of type '" + expected.getName() + "' but is of type '" + "This argument should be of type '" + expected.getName() + "' but is of type '" +
actual.getUnspecifiedType().getName() + "'" actual.getUnspecifiedType().getName() + "'"

View File

@@ -30,7 +30,7 @@ private predicate additionalLogicalCheck(Expr e, string operation, int valueToCh
/** /**
* An `Operation` that seems to be checking for leap year. * An `Operation` that seems to be checking for leap year.
*/ */
class CheckForLeapYearOperation extends Operation { class CheckForLeapYearOperation extends Expr {
CheckForLeapYearOperation() { CheckForLeapYearOperation() {
exists(BinaryArithmeticOperation bo | bo = this | exists(BinaryArithmeticOperation bo | bo = this |
bo.getAnOperand().getValue().toInt() = 4 and bo.getAnOperand().getValue().toInt() = 4 and
@@ -39,8 +39,6 @@ class CheckForLeapYearOperation extends Operation {
additionalLogicalCheck(this.getEnclosingElement(), "%", 400) additionalLogicalCheck(this.getEnclosingElement(), "%", 400)
) )
} }
override string getOperator() { result = "LeapYearCheck" }
} }
/** /**

View File

@@ -0,0 +1,6 @@
int get_number_from_network();
int process_network(int[] buff, int buffSize) {
int i = ntohl(get_number_from_network());
return buff[i];
}

View File

@@ -0,0 +1,10 @@
uint32_t get_number_from_network();
int process_network(int[] buff, uint32_t buffSize) {
uint32_t i = ntohl(get_number_from_network());
if (i < buffSize) {
return buff[i];
} else {
return -1;
}
}

View File

@@ -0,0 +1,54 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Data received over a network connection may be received in a different byte order than
the byte order used by the local host, making the data difficult to process. To address this,
data received over the wire is usually converted to host byte order by a call to a network-to-host
byte order function, such as <code>ntohl</code>.
</p>
<p>
The use of a network-to-host byte order function is therefore a good indicator that the returned
value is unvalidated data retrieved from the network, and should not be used without further
validation. In particular, the returned value should not be used as an array index or array length
value without validation, as this could result in a buffer overflow vulnerability.
</p>
</overview>
<recommendation>
<p>
Validate data returned by network-to-host byte order functions before use and especially before
using the value as an array index or bound.
</p>
</recommendation>
<example>
<p>In the example below, network data is retrieved and passed to <code>ntohl</code> to convert
it to host byte order. The data is then used as an index in an array access expression. However,
there is no validation that the data returned by <code>ntohl</code> is within the bounds of the array,
which could lead to reading outside the bounds of the buffer.
</p>
<sample src="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="NtohlArrayNoBound-good.cpp" />
</example>
<references>
<li>
<a href="https://docs.microsoft.com/en-us/windows/desktop/api/winsock/nf-winsock-ntohl">
ntohl - winsock reference
</a>
</li>
<li>
<a href="https://linux.die.net/man/3/ntohl">
ntohl - Linux man page
</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,17 @@
/**
* @id cpp/network-to-host-function-as-array-bound
* @name Untrusted network-to-host usage
* @description Using the result of a network-to-host byte order function, such as ntohl, as an
* array bound or length value without checking it may result in buffer overflows or
* other vulnerabilties.
* @kind problem
* @problem.severity error
*/
import cpp
import NtohlArrayNoBound
import semmle.code.cpp.dataflow.DataFlow
from NetworkToBufferSizeConfiguration bufConfig, DataFlow::Node source, DataFlow::Node sink
where bufConfig.hasFlow(source, sink)
select sink, "Unchecked use of data from network function $@", source, source.toString()

View File

@@ -0,0 +1,154 @@
import cpp
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
* a pointer to the buffer and the length of data to be read or written.
* Extend this class to support different kinds of buffer access.
*/
abstract class BufferAccess extends Locatable {
/** Gets the pointer to the buffer being accessed. */
abstract Expr getPointer();
/** Gets the length of the data being read or written by this buffer access. */
abstract Expr getAccessedLength();
}
/**
* A buffer access through an array expression.
*/
class ArrayBufferAccess extends BufferAccess, ArrayExpr {
override Expr getPointer() { result = this.getArrayBase() }
override Expr getAccessedLength() { result = this.getArrayOffset() }
}
/**
* A buffer access through an overloaded array expression.
*/
class OverloadedArrayBufferAccess extends BufferAccess, OverloadedArrayExpr {
override Expr getPointer() { result = this.getQualifier() }
override Expr getAccessedLength() { result = this.getAnArgument() }
}
/**
* A buffer access through pointer arithmetic.
*/
class PointerArithmeticAccess extends BufferAccess, Expr {
PointerArithmeticOperation p;
PointerArithmeticAccess() {
this = p and
p.getAnOperand().getType().getUnspecifiedType() instanceof IntegralType and
not p.getParent() instanceof ComparisonOperation
}
override Expr getPointer() {
result = p.getAnOperand() and
result.getType().getUnspecifiedType() instanceof PointerType
}
override Expr getAccessedLength() {
result = p.getAnOperand() and
result.getType().getUnspecifiedType() instanceof IntegralType
}
}
/**
* A pair of buffer accesses through a call to memcpy.
*/
class MemCpy extends BufferAccess, FunctionCall {
MemCpy() { getTarget().hasName("memcpy") }
override Expr getPointer() {
result = getArgument(0) or
result = getArgument(1)
}
override Expr getAccessedLength() { result = getArgument(2) }
}
class StrncpySizeExpr extends BufferAccess, FunctionCall {
StrncpySizeExpr() { getTarget().hasName("strncpy") }
override Expr getPointer() {
result = getArgument(0) or
result = getArgument(1)
}
override Expr getAccessedLength() { result = getArgument(2) }
}
class RecvSizeExpr extends BufferAccess, FunctionCall {
RecvSizeExpr() { getTarget().hasName("recv") }
override Expr getPointer() { result = getArgument(1) }
override Expr getAccessedLength() { result = getArgument(2) }
}
class SendSizeExpr extends BufferAccess, FunctionCall {
SendSizeExpr() { getTarget().hasName("send") }
override Expr getPointer() { result = getArgument(1) }
override Expr getAccessedLength() { result = getArgument(2) }
}
class SnprintfSizeExpr extends BufferAccess, FunctionCall {
SnprintfSizeExpr() { getTarget().hasName("snprintf") }
override Expr getPointer() { result = getArgument(0) }
override Expr getAccessedLength() { result = getArgument(1) }
}
class MemcmpSizeExpr extends BufferAccess, FunctionCall {
MemcmpSizeExpr() { getTarget().hasName("Memcmp") }
override Expr getPointer() {
result = getArgument(0) or
result = getArgument(1)
}
override Expr getAccessedLength() { result = getArgument(2) }
}
class MallocSizeExpr extends BufferAccess, FunctionCall {
MallocSizeExpr() { getTarget().hasName("malloc") }
override Expr getPointer() { none() }
override Expr getAccessedLength() { result = getArgument(1) }
}
class NetworkFunctionCall extends FunctionCall {
NetworkFunctionCall() {
getTarget().hasName("ntohd") or
getTarget().hasName("ntohf") or
getTarget().hasName("ntohl") or
getTarget().hasName("ntohll") or
getTarget().hasName("ntohs")
}
}
class NetworkToBufferSizeConfiguration extends DataFlow::Configuration {
NetworkToBufferSizeConfiguration() { this = "NetworkToBufferSizeConfiguration" }
override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof NetworkFunctionCall }
override predicate isSink(DataFlow::Node node) {
node.asExpr() = any(BufferAccess ba).getAccessedLength()
}
override predicate isBarrier(DataFlow::Node node) {
exists(GuardCondition gc, GVN gvn |
gc.getAChild*() = gvn.getAnExpr() and
globalValueNumber(node.asExpr()) = gvn and
gc.controls(node.asExpr().getBasicBlock(), _)
)
}
}

View File

@@ -0,0 +1,3 @@
bool not_in_range(T *ptr, T *ptr_end, size_t i) {
return ptr + i >= ptr_end || ptr + i < ptr; // BAD
}

View File

@@ -0,0 +1,3 @@
bool not_in_range(T *ptr, T *ptr_end, size_t i) {
return i >= ptr_end - ptr; // GOOD
}

View File

@@ -0,0 +1,67 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
When checking for integer overflow, you may often write tests like
<code>p + i &lt; p</code>. This works fine if <code>p</code> and
<code>i</code> are unsigned integers, since any overflow in the addition
will cause the value to simply "wrap around." However, using this pattern when
<code>p</code> is a pointer is problematic because pointer 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>
To check whether an index <code>i</code> is less than the length of an array,
simply compare these two numbers as unsigned integers: <code>i &lt; ARRAY_LENGTH</code>.
If the length of the array is defined as the difference between two pointers
<code>ptr</code> and <code>p_end</code>, write <code>i &lt; p_end - ptr</code>.
If <code>i</code> is signed, cast it to unsigned
in order to guard against negative <code>i</code>. For example, write
<code>(size_t)i &lt; p_end - ptr</code>.
</p>
</recommendation>
<example>
<p>
An invalid check for pointer overflow is most often seen as part of checking
whether a number <code>a</code> is too large by checking first if adding the
number to <code>ptr</code> goes past the end of an allocation and then
checking if adding it to <code>ptr</code> creates a pointer so large that it
overflows and wraps around.
</p>
<sample src="PointerOverflow-bad.cpp" />
<p>
In both of these checks, the operations are performed in the wrong order.
First, an expression that may cause undefined behavior is evaluated
(<code>ptr + i</code>), and then the result is checked for being in range.
But once undefined behavior has happened in the pointer addition, it cannot
be recovered from: it's too late to perform the range check after a possible
pointer overflow.
</p>
<p>
While it's not the subject of this query, the expression <code>ptr + i &lt;
ptr_end</code> is also an invalid range check. It's undefined behavor in
C/C++ to create a pointer that points more than one past the end of an
allocation.
</p>
<p>
The next example shows how to portably check whether an unsigned number is outside the
range of an allocation between <code>ptr</code> and <code>ptr_end</code>.
</p>
<sample src="PointerOverflow-good.cpp" />
</example>
<references>
<li>Embedded in Academia: <a href="https://blog.regehr.org/archives/1395">Pointer Overflow Checking</a>.</li>
<li>LWN: <a href="https://lwn.net/Articles/278137/">GCC and pointer overflows</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,31 @@
/**
* @name Pointer overflow check
* @description Adding a value to a pointer to check if it overflows relies
* on undefined behavior and may lead to memory corruption.
* @kind problem
* @problem.severity error
* @precision high
* @id cpp/pointer-overflow-check
* @tags reliability
* security
*/
import cpp
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
private import semmle.code.cpp.commons.Exclusions
from RelationalOperation ro, PointerAddExpr add, Expr expr1, Expr expr2
where
ro.getAnOperand() = add and
add.getAnOperand() = expr1 and
ro.getAnOperand() = expr2 and
globalValueNumber(expr1) = globalValueNumber(expr2) and
// Exclude macros but not their arguments
not isFromMacroDefinition(ro) and
// There must be a compilation of this file without a flag that makes pointer
// overflow well defined.
exists(Compilation c | c.getAFileCompiled() = ro.getFile() |
not c.getAnArgument() = "-fwrapv-pointer" and
not c.getAnArgument() = "-fno-strict-overflow"
)
select ro, "Range check relying on pointer overflow."

View File

@@ -0,0 +1,15 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Using the TLS or SSLv23 protocol from the boost::asio library, but not disabling deprecated protocols may expose the software to known vulnerabilities or permit weak encryption algorithms to be used. Disabling the minimum-recommended protocols is also flagged.</p>
</overview>
<references>
<li>
<a href="https://www.boost.org/doc/libs/1_71_0/doc/html/boost_asio.html">Boost.Asio documentation</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,94 @@
/**
* @name Boost_asio TLS Settings Misconfiguration
* @description Using the TLS or SSLv23 protocol from the boost::asio library, but not disabling deprecated protocols, or disabling minimum-recommended protocols.
* @kind problem
* @problem.severity error
* @id cpp/boost/tls_settings_misconfiguration
* @tags security
*/
import cpp
import semmle.code.cpp.security.boostorg.asio.protocols
class ExistsAnyFlowConfig extends DataFlow::Configuration {
ExistsAnyFlowConfig() { this = "ExistsAnyFlowConfig" }
override predicate isSource(DataFlow::Node source) {
exists(BoostorgAsio::SslContextClass c | c.getAContructorCall() = source.asExpr())
}
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(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
)
)
)
}
bindingset[flag]
predicate isOptionNotSet(ConstructorCall cc, int flag) {
not exists(FunctionCall fcSetOptions | isOptionSet(cc, flag, fcSetOptions))
}
from
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 (
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoSsl3(), _) and
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1(), _) and
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1(), _) and
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2())
)
or
BoostorgAsio::isExprTlsBoostProtocol(protocolSource) and
not BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
not (
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1(), _) and
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1(), _) and
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2())
)
) and
(
BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoSsl3()) and
e = cc and
msg = "no_sslv3 has not been set"
or
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1()) and
e = cc and
msg = "no_tlsv1 has not been set"
or
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1()) and
e = cc and
msg = "no_tlsv1_1 has not been set"
or
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2(), e) and
msg = "no_tlsv1_2 was set"
)
select cc, "Usage of $@ with protocol $@ is not configured correctly: The option $@.", cc,
"boost::asio::ssl::context::context", protocolSource, protocolSource.toString(), e, msg

View File

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

View File

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

View File

@@ -7,6 +7,10 @@
* @id cpp/ignore-return-value-sal * @id cpp/ignore-return-value-sal
* @problem.severity warning * @problem.severity warning
* @tags reliability * @tags reliability
* external/cwe/cwe-573
* external/cwe/cwe-252
* @opaque-id SM02344
* @microsoft.severity Important
*/ */
import SAL import SAL

View File

@@ -2,29 +2,45 @@ import cpp
class SALMacro extends Macro { class SALMacro extends Macro {
SALMacro() { SALMacro() {
this.getFile().getBaseName() = "sal.h" or exists(string filename | filename = this.getFile().getBaseName() |
this.getFile().getBaseName() = "specstrings_strict.h" or filename = "sal.h" or
this.getFile().getBaseName() = "specstrings.h" filename = "specstrings_strict.h" or
filename = "specstrings.h" or
filename = "w32p.h" or
filename = "minwindef.h"
) and
(
// Dialect for Windows 8 and above
this.getName().matches("\\_%\\_")
or
// Dialect for Windows 7
this.getName().matches("\\_\\_%")
)
} }
} }
pragma[noinline]
predicate isTopLevelMacroAccess(MacroAccess ma) { not exists(ma.getParentInvocation()) }
class SALAnnotation extends MacroInvocation { class SALAnnotation extends MacroInvocation {
SALAnnotation() { SALAnnotation() {
this.getMacro() instanceof SALMacro and this.getMacro() instanceof SALMacro and
not exists(this.getParentInvocation()) isTopLevelMacroAccess(this)
} }
/** Returns the `Declaration` annotated by `this`. */ /** Returns the `Declaration` annotated by `this`. */
Declaration getDeclaration() { annotatesAt(this, result.getADeclarationEntry(), _, _) } Declaration getDeclaration() {
annotatesAt(this, result.getADeclarationEntry(), _, _) and
not result instanceof Type // exclude typedefs
}
/** Returns the `DeclarationEntry` annotated by `this`. */ /** Returns the `DeclarationEntry` annotated by `this`. */
DeclarationEntry getDeclarationEntry() { annotatesAt(this, result, _, _) } DeclarationEntry getDeclarationEntry() {
annotatesAt(this, result, _, _) and
not result instanceof TypeDeclarationEntry // exclude typedefs
}
} }
/*
* Particular SAL annotations of interest
*/
class SALCheckReturn extends SALAnnotation { class SALCheckReturn extends SALAnnotation {
SALCheckReturn() { SALCheckReturn() {
exists(SALMacro m | m = this.getMacro() | exists(SALMacro m | m = this.getMacro() |
@@ -39,8 +55,8 @@ class SALNotNull extends SALAnnotation {
exists(SALMacro m | m = this.getMacro() | exists(SALMacro m | m = this.getMacro() |
not m.getName().matches("%\\_opt\\_%") and not m.getName().matches("%\\_opt\\_%") and
( (
m.getName().matches("\\_In%") or m.getName().matches("_In%") or
m.getName().matches("\\_Out%") or m.getName().matches("_Out%") or
m.getName() = "_Ret_notnull_" m.getName() = "_Ret_notnull_"
) )
) and ) and
@@ -63,42 +79,124 @@ class SALMaybeNull extends SALAnnotation {
} }
} }
/* ///////////////////////////////////////////////////////////////////////////////
* Implementation details // Implementation details
/**
* Holds if `a` annotates the declaration entry `d` and
* its start position is the `idx`th position in `file` that holds a SAL element.
*/ */
predicate annotatesAt(SALAnnotation a, DeclarationEntry d, File file, int idx) {
private predicate annotatesAt(SALAnnotation a, DeclarationEntry e, File file, int idx) { annotatesAtPosition(a.(SALElement).getStartPosition(), d, file, idx)
a = salElementAt(file, idx) and
(
// Base case: `a` right before `e`
e = salElementAt(file, idx + 1)
or
// Recursive case: `a` right before some annotation on `e`
annotatesAt(_, e, file, idx + 1)
)
}
library class SALElement extends Element {
SALElement() {
this instanceof DeclarationEntry or
this instanceof SALAnnotation
}
}
/** Gets the `idx`th `SALElement` in `file`. */
private SALElement salElementAt(File file, int idx) {
interestingLoc(file, result, interestingStartPos(file, idx))
} }
/** /**
* Holds if an SALElement element at character `result` comes at * Holds if `pos` is the `idx`th position in `file` that holds a SAL element,
* position `idx` in `file`. * which annotates the declaration entry `d` (by occurring before it without
* any other declaration entries in between).
*/ */
private int interestingStartPos(File file, int idx) { // For performance reasons, do not mention the annotation itself here,
result = rank[idx](int otherStart | interestingLoc(file, _, otherStart)) // but compute with positions instead. This performs better on databases
// with many annotations at the same position.
private predicate annotatesAtPosition(SALPosition pos, DeclarationEntry d, File file, int idx) {
pos = salRelevantPositionAt(file, idx) and
salAnnotationPos(pos) and
(
// Base case: `pos` right before `d`
d.(SALElement).getStartPosition() = salRelevantPositionAt(file, idx + 1)
or
// Recursive case: `pos` right before some annotation on `d`
annotatesAtPosition(_, d, file, idx + 1)
)
} }
/** Holds if `element` in `file` is at character `startPos`. */ /**
private predicate interestingLoc(File file, SALElement element, int startPos) { * A parameter annotated by one or more SAL annotations.
element.getLocation().charLoc(file, startPos, _) */
class SALParameter extends Parameter {
/** One of this parameter's annotations. */
SALAnnotation a;
SALParameter() { annotatesAt(a, this.getADeclarationEntry(), _, _) }
predicate isIn() { a.getMacroName().toLowerCase().matches("%\\_in%") }
predicate isOut() { a.getMacroName().toLowerCase().matches("%\\_out%") }
predicate isInOut() { a.getMacroName().toLowerCase().matches("%\\_inout%") }
}
/**
* A SAL element, that is, a SAL annotation or a declaration entry
* that may have SAL annotations.
*/
library class SALElement extends Element {
SALElement() {
containsSALAnnotation(this.(DeclarationEntry).getFile()) or
this instanceof SALAnnotation
}
predicate hasStartPosition(File file, int line, int col) {
exists(Location loc | loc = this.getLocation() |
file = loc.getFile() and
line = loc.getStartLine() and
col = loc.getStartColumn()
)
}
predicate hasEndPosition(File file, int line, int col) {
exists(Location loc |
loc = this.(FunctionDeclarationEntry).getBlock().getLocation()
or
this = any(VariableDeclarationEntry vde |
vde.isDefinition() and
loc = vde.getVariable().getInitializer().getLocation()
)
|
file = loc.getFile() and
line = loc.getEndLine() and
col = loc.getEndColumn()
)
}
SALPosition getStartPosition() {
exists(File file, int line, int col |
this.hasStartPosition(file, line, col) and
result = MkSALPosition(file, line, col)
)
}
}
/** Holds if `file` contains a SAL annotation. */
pragma[noinline]
private predicate containsSALAnnotation(File file) { any(SALAnnotation a).getFile() = file }
/**
* A source-file position of a `SALElement`. Unlike location, this denotes a
* point in the file rather than a range.
*/
private newtype SALPosition =
MkSALPosition(File file, int line, int col) {
exists(SALElement e |
e.hasStartPosition(file, line, col)
or
e.hasEndPosition(file, line, col)
)
}
/** Holds if `pos` is the start position of a SAL annotation. */
pragma[noinline]
private predicate salAnnotationPos(SALPosition pos) {
any(SALAnnotation a).(SALElement).getStartPosition() = pos
}
/**
* Gets the `idx`th position in `file` that holds a SAL element,
* ordering positions lexicographically by their start line and start column.
*/
private SALPosition salRelevantPositionAt(File file, int idx) {
result = rank[idx](SALPosition pos, int line, int col |
pos = MkSALPosition(file, line, col)
|
pos order by line, col
)
} }

View File

@@ -34,8 +34,10 @@ class FileFunction extends FunctionWithWrappers {
nme.matches("CreateFile%") nme.matches("CreateFile%")
) )
or or
this.hasQualifiedName("std", "fopen")
or
// on any of the fstream classes, or filebuf // 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_fstream" or
nme = "basic_ifstream" or nme = "basic_ifstream" or
nme = "basic_ofstream" or nme = "basic_ofstream" or

View File

@@ -34,7 +34,7 @@ characters before writing to the HTML page.</p>
<li> <li>
OWASP: 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>. (Cross Site Scripting) Prevention Cheat Sheet</a>.
</li> </li>
<li> <li>

View File

@@ -17,8 +17,8 @@ import semmle.code.cpp.security.TaintTracking
/** A call that prints its arguments to `stdout`. */ /** A call that prints its arguments to `stdout`. */
class PrintStdoutCall extends FunctionCall { class PrintStdoutCall extends FunctionCall {
PrintStdoutCall() { PrintStdoutCall() {
getTarget().hasGlobalName("puts") or getTarget().hasGlobalOrStdName("puts") or
getTarget().hasGlobalName("printf") getTarget().hasGlobalOrStdName("printf")
} }
} }

View File

@@ -19,10 +19,7 @@ import semmle.code.cpp.dataflow.DataFlow
import semmle.code.cpp.models.implementations.Memcpy import semmle.code.cpp.models.implementations.Memcpy
class MallocCall extends FunctionCall { class MallocCall extends FunctionCall {
MallocCall() { MallocCall() { this.getTarget().hasGlobalOrStdName("malloc") }
this.getTarget().hasGlobalName("malloc") or
this.getTarget().hasQualifiedName("std", "malloc")
}
Expr getAllocatedSize() { Expr getAllocatedSize() {
if this.getArgument(0) instanceof VariableAccess if this.getArgument(0) instanceof VariableAccess

View File

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

View File

@@ -0,0 +1,59 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>A common pattern is to initialize a local variable by calling another function (an
"initialization" function) with the address of the local variable as a pointer argument. That
function is then responsible for writing to the memory location referenced by the pointer.</p>
<p>In some cases, the called function may not always write to the memory pointed to by the
pointer argument. In such cases, the function will typically return a "status" code, informing the
caller as to whether the initialization succeeded or not. If the caller does not check the status
code before reading the local variable, it may read unitialized memory, which can result in
unexpected behavior.</p>
</overview>
<recommendation>
<p>When using a initialization function that does not guarantee to initialize the memory pointed to
by the passed pointer, and returns a status code to indicate whether such initialization occurred,
the status code should be checked before reading from the local variable.</p>
</recommendation>
<example>
<p>In this hypothetical example we have code for managing a series of devices. The code
includes a <code>DeviceConfig</code> struct that can represent properties about each device.
The <code>initDeviceConfig</code> function can be called to initialize one of these structures, by
providing a "device number", which can be used to look up the appropriate properties in some data
store. If an invalid device number is provided, the function returns a status code of
<code>-1</code>, and does not initialize the provided pointer.</p>
<p>In the first code sample below, the <code>notify</code> function calls the
<code>initDeviceConfig</code> function with a pointer to the local variable <code>config</code>,
which is then subsequently accessed to fetch properties of the device. However, the code does not
check the return value from the function call to <code>initDeviceConfig</code>. If the
device number passed to the <code>notify</code> function was invalid, the
<code>initDeviceConfig</code> function will leave the <code>config</code> variable uninitialized,
which will result in the <code>notify</code> function accessing uninitialized memory.</p>
<sample src="ConditionallyUninitializedVariableBad.c" />
<p>To fix this, the code needs to check that the return value of the call to
<code>initDeviceConfig</code> is zero. If that is true, then the calling code can safely assume
that the local variable has been initialized.</p>
<sample src="ConditionallyUninitializedVariableGood.c" />
</example>
<references>
<li>
Wikipedia:
<a href="https://en.wikipedia.org/wiki/Uninitialized_variable">Uninitialized variable</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,33 @@
/**
* @name Conditionally uninitialized variable
* @description When an initialization function is used to initialize a local variable, but the
* returned status code is not checked, the variable may be left in an uninitialized
* state, and reading the variable may result in undefined behavior.
* @kind problem
* @problem.severity warning
* @opaque-id SM02313
* @id cpp/conditionally-uninitialized-variable
* @tags security
* external/cwe/cwe-457
*/
import cpp
import semmle.code.cpp.controlflow.SSA
private import UninitializedVariables
from
ConditionallyInitializedVariable v, ConditionalInitializationFunction f,
ConditionalInitializationCall call, string defined, Evidence e
where
exists(v.getARiskyAccess(f, call, e)) and
(
if e = DefinitionInSnapshot()
then defined = ""
else
if e = SuggestiveSALAnnotation()
then defined = "externally defined (SAL) "
else defined = "externally defined (CSV) "
)
select call,
"The status of this call to " + defined +
"$@ is not checked, potentially leaving $@ uninitialized.", f, f.getName(), v, v.getName()

View File

@@ -0,0 +1,25 @@
struct DeviceConfig {
bool isEnabled;
int channel;
};
int initDeviceConfig(DeviceConfig *ref, int deviceNumber) {
if (deviceNumber >= getMaxDevices()) {
// No device with that number, return -1 to indicate failure
return -1;
}
// Device with that number, fetch parameters and initialize struct
ref->isEnabled = fetchIsDeviceEnabled(deviceNumber);
ref->channel = fetchDeviceChannel(deviceNumber);
// Return 0 to indicate success
return 0;
}
int notify(int deviceNumber) {
DeviceConfig config;
initDeviceConfig(&config, deviceNumber);
// BAD: Using config without checking the status code that is returned
if (config.isEnabled) {
notifyChannel(config.channel);
}
}

View File

@@ -0,0 +1,27 @@
struct DeviceConfig {
bool isEnabled;
int channel;
};
int initDeviceConfig(DeviceConfig *ref, int deviceNumber) {
if (deviceNumber >= getMaxDevices()) {
// No device with that number, return -1 to indicate failure
return -1;
}
// Device with that number, fetch parameters and initialize struct
ref->isEnabled = fetchIsDeviceEnabled(deviceNumber);
ref->channel = fetchDeviceChannel(deviceNumber);
// Return 0 to indicate success
return 0;
}
void notify(int deviceNumber) {
DeviceConfig config;
int statusCode = initDeviceConfig(&config, deviceNumber);
if (statusCode == 0) {
// GOOD: Status code returned by initialization function is checked, so this is safe
if (config.isEnabled) {
notifyChannel(config.channel);
}
}
}

View File

@@ -0,0 +1,705 @@
/**
* Provides classes and predicates for identifying functions that initialize their arguments.
*/
import cpp
import external.ExternalArtifact
private import semmle.code.cpp.dispatch.VirtualDispatchPrototype
import semmle.code.cpp.NestedFields
import Microsoft.SAL
import semmle.code.cpp.controlflow.Guards
/** A context under which a function may be called. */
private newtype TContext =
/** No specific call context. */
NoContext() or
/**
* The call context is that the given other parameter is null.
*
* This context is created for all parameters that are null checked in the body of the function.
*/
ParamNull(Parameter p) { p = any(ParameterNullCheck pnc).getParameter() } or
/**
* The call context is that the given other parameter is not null.
*
* This context is created for all parameters that are null checked in the body of the function.
*/
ParamNotNull(Parameter p) { p = any(ParameterNullCheck pnc).getParameter() }
/**
* A context under which a function may be called.
*
* Some functions may conditionally initialize a parameter depending on the value of another
* parameter. Consider:
* ```
* int MyInitFunction(int* paramToBeInitialized, int* paramToCheck) {
* if (!paramToCheck) {
* // fail!
* return -1;
* }
* paramToBeInitialized = 0;
* }
* ```
* In this case, whether `paramToBeInitialized` is initialized when this function call completes
* depends on whether `paramToCheck` is or is not null. A call-context insensitive analysis will
* determine that any call to this function may leave the parameter uninitialized, even if the
* argument to paramToCheck is guaranteed to be non-null (`&foo`, for example).
*
* This class models call contexts that can be considered when calculating whether a given parameter
* initializes or not. The supported contexts are:
* - `ParamNull(otherParam)` - the given `otherParam` is considered to be null. Applies when
* exactly one parameter other than this one is null checked.
* - `ParamNotNull(otherParam)` - the given `otherParam` is considered to be not null. Applies when
* exactly one parameter other than this one is null checked.
* - `NoContext()` - applies in all other circumstances.
*/
class Context extends TContext {
string toString() {
this = NoContext() and result = "NoContext"
or
this = ParamNull(any(Parameter p | result = "ParamNull(" + p.getName() + ")"))
or
this = ParamNotNull(any(Parameter p | result = "ParamNotNull(" + p.getName() + ")"))
}
}
/**
* A check against a parameter.
*/
abstract class ParameterCheck extends Expr {
/**
* Gets a successor of this check that should be ignored for the given context.
*/
abstract ControlFlowNode getIgnoredSuccessorForContext(Context c);
}
/** A null-check expression on a parameter. */
class ParameterNullCheck extends ParameterCheck {
Parameter p;
ControlFlowNode nullSuccessor;
ControlFlowNode notNullSuccessor;
ParameterNullCheck() {
this.isCondition() and
p.getFunction() instanceof InitializationFunction and
p.getType().getUnspecifiedType() instanceof PointerType and
exists(VariableAccess va | va = p.getAnAccess() |
nullSuccessor = getATrueSuccessor() and
notNullSuccessor = getAFalseSuccessor() and
(
va = this.(NotExpr).getOperand() or
va = any(EQExpr eq | eq = this and eq.getAnOperand().getValue() = "0").getAnOperand() or
va = getCheckedFalseCondition(this) or
va = any(NEExpr eq |
eq = getCheckedFalseCondition(this) and eq.getAnOperand().getValue() = "0"
).getAnOperand()
)
or
nullSuccessor = getAFalseSuccessor() and
notNullSuccessor = getATrueSuccessor() and
(
va = this or
va = any(NEExpr eq | eq = this and eq.getAnOperand().getValue() = "0").getAnOperand() or
va = any(EQExpr eq |
eq = getCheckedFalseCondition(this) and eq.getAnOperand().getValue() = "0"
).getAnOperand()
)
)
}
/** The parameter being null-checked. */
Parameter getParameter() { result = p }
override ControlFlowNode getIgnoredSuccessorForContext(Context c) {
c = ParamNull(p) and result = notNullSuccessor
or
c = ParamNotNull(p) and result = nullSuccessor
}
/** The successor at which the parameter is confirmed to be null. */
ControlFlowNode getNullSuccessor() { result = nullSuccessor }
/** The successor at which the parameter is confirmed to be not-null. */
ControlFlowNode getNotNullSuccessor() { result = notNullSuccessor }
}
/**
* An entry in a CSV file in cond-init that contains externally defined functions that are
* conditional initializers. These files are typically produced by running the
* ConditionallyInitializedFunction companion query.
*/
class ValidatedExternalCondInitFunction extends ExternalData {
ValidatedExternalCondInitFunction() { this.getDataPath().matches("%cond-init%.csv") }
predicate isExternallyVerified(Function f, int param) {
functionSignature(f, getField(1), getField(2)) and param = getFieldAsInt(3)
}
}
/**
* The type of evidence used to determine whether a function initializes a parameter.
*/
newtype Evidence =
/**
* The function is defined in the snapshot, and the CFG has been analyzed to determine that the
* parameter is not initialized on at least one path to the exit.
*/
DefinitionInSnapshot() or
/**
* The function is externally defined, but the parameter has an `_out` SAL annotation which
* suggests that it is initialized in the function.
*/
SuggestiveSALAnnotation() or
/**
* We have been given a CSV file which indicates this parameter is conditionally initialized.
*/
ExternalEvidence()
/**
* A call to an function which initializes one or more of its parameters.
*/
class InitializationFunctionCall extends FunctionCall {
Expr initializedArgument;
InitializationFunctionCall() { initializedArgument = getAnInitializedArgument(this) }
/** Gets a parameter that is initialized by this call. */
Parameter getAnInitParameter() { result.getAnAccess() = initializedArgument }
}
/**
* A variable access which is dereferenced then assigned to.
*/
private predicate isPointerDereferenceAssignmentTarget(VariableAccess target) {
target.getParent().(PointerDereferenceExpr) = any(Assignment e).getLValue()
}
/**
* A function which initializes one or more of its parameters.
*/
class InitializationFunction extends Function {
int i;
Evidence evidence;
InitializationFunction() {
evidence = DefinitionInSnapshot() and
(
// Assignment by pointer dereferencing the parameter
isPointerDereferenceAssignmentTarget(this.getParameter(i).getAnAccess()) or
// Field wise assignment to the parameter
any(Assignment e).getLValue() = getAFieldAccess(this.getParameter(i)) or
i = this
.(MemberFunction)
.getAnOverridingFunction+()
.(InitializationFunction)
.initializedParameter() or
getParameter(i) = any(InitializationFunctionCall c).getAnInitParameter()
)
or
// If we have no definition, we look at SAL annotations
not this.isDefined() and
this.getParameter(i).(SALParameter).isOut() and
evidence = SuggestiveSALAnnotation()
or
// We have some external information that this function conditionally initializes
not this.isDefined() and
any(ValidatedExternalCondInitFunction vc).isExternallyVerified(this, i) and
evidence = ExternalEvidence()
}
/** Gets a parameter index which is initialized by this function. */
int initializedParameter() { result = i }
/** Gets a `ControlFlowNode` which assigns a new value to the parameter with the given index. */
ControlFlowNode paramReassignment(int index) {
index = i and
(
result = this.getParameter(i).getAnAccess() and
(
result = any(Assignment a).getLValue().(PointerDereferenceExpr).getOperand()
or
// Field wise assignment to the parameter
result = any(Assignment a).getLValue().(FieldAccess).getQualifier()
or
// Assignment to a nested field of the parameter
result = any(Assignment a).getLValue().(NestedFieldAccess).getUltimateQualifier()
or
result = getAnInitializedArgument(any(Call c))
or
exists(IfStmt check | result = check.getCondition().getAChild*() |
paramReassignmentCondition(check)
)
)
or
result = any(AssumeExpr e |
e.getEnclosingFunction() = this and e.getAChild().(Literal).getValue() = "0"
)
)
}
/**
* Helper predicate: holds if the `if` statement `check` contains a
* reassignment to the `i`th parameter within its `then` statement.
*/
pragma[noinline]
private predicate paramReassignmentCondition(IfStmt check) {
this.paramReassignment(i).getEnclosingStmt().getParentStmt*() = check.getThen()
}
/** Holds if `n` can be reached without the parameter at `index` being reassigned. */
predicate paramNotReassignedAt(ControlFlowNode n, int index, Context c) {
c = getAContext(index) and
(
not exists(this.getEntryPoint()) and index = i and n = this
or
n = this.getEntryPoint() and index = i
or
exists(ControlFlowNode mid | paramNotReassignedAt(mid, index, c) |
n = mid.getASuccessor() and
not n = paramReassignment(index) and
/*
* Ignore successor edges where the parameter is null, because it is then confirmed to be
* initialized.
*/
not exists(ParameterNullCheck nullCheck |
nullCheck = mid and
nullCheck = getANullCheck(index) and
n = nullCheck.getNullSuccessor()
) and
/*
* Ignore successor edges which are excluded by the given context
*/
not exists(ParameterCheck paramCheck | paramCheck = mid |
n = paramCheck.getIgnoredSuccessorForContext(c)
)
)
)
}
/** Gets a null-check on the parameter at `index`. */
private ParameterNullCheck getANullCheck(int index) {
getParameter(index) = result.getParameter()
}
/** Gets a parameter which is not at the given index. */
private Parameter getOtherParameter(int index) {
index = i and
result = getAParameter() and
not result.getIndex() = index
}
/**
* Gets a call `Context` that is applicable when considering whether parameter at the `index` can
* be conditionally initialized.
*/
Context getAContext(int index) {
index = i and
/*
* If there is one and only one other parameter which is null checked in the body of the method,
* then we have two contexts to consider - that the other param is null, or that the other param
* is not null.
*/
if
strictcount(Parameter p |
exists(Context c | c = ParamNull(p) or c = ParamNotNull(p)) and
p = getOtherParameter(index)
) = 1
then
exists(Parameter p | p = getOtherParameter(index) |
result = ParamNull(p) or result = ParamNotNull(p)
)
else
// Otherwise, only consider NoContext.
result = NoContext()
}
/**
* Holds if this function should be whitelisted - that is, not considered as conditionally
* initializing its parameters.
*/
predicate whitelisted() {
exists(string name | this.hasName(name) |
// Return value is not a success code but the output functions never fail.
name.matches("_Interlocked%")
or
// Functions that never fail, according to MSDN.
name = "QueryPerformanceCounter"
or
name = "QueryPerformanceFrequency"
or
// Functions that never fail post-Vista, according to MSDN.
name = "InitializeCriticalSectionAndSpinCount"
or
// `rand_s` writes 0 to a non-null argument if it fails, according to MSDN.
name = "rand_s"
or
// IntersectRect initializes the argument regardless of whether the input intersects
name = "IntersectRect"
or
name = "SetRect"
or
name = "UnionRect"
or
// These functions appears to have an incorrect CFG, which leads to false positives
name = "PhysicalToLogicalDPIPoint"
or
name = "LogicalToPhysicalDPIPoint"
or
// Sets NtProductType to default on error
name = "RtlGetNtProductType"
or
// Our CFG is not sophisticated enough to detect that the argument is always initialized
name = "StringCchLengthA"
or
// All paths init the argument, and always returns SUCCESS.
name = "RtlUnicodeToMultiByteSize"
or
// All paths init the argument, and always returns SUCCESS.
name = "RtlMultiByteToUnicodeSize"
or
// All paths init the argument, and always returns SUCCESS.
name = "RtlUnicodeToMultiByteN"
or
// Always initializes argument
name = "RtlGetFirstRange"
or
// Destination range is zeroed out on failure, assuming first two parameters are valid
name = "memcpy_s"
or
// This zeroes the memory unconditionally
name = "SeCreateAccessState"
)
}
}
/**
* A function which initializes one or more of its parameters, but not on all paths.
*/
class ConditionalInitializationFunction extends InitializationFunction {
Context c;
ConditionalInitializationFunction() {
c = this.getAContext(i) and
not this.whitelisted() and
exists(Type status | status = this.getType().getUnspecifiedType() |
status instanceof IntegralType or
status instanceof Enum
) and
not this.getType().getName().toLowerCase() = "size_t" and
(
/*
* If there is no definition, consider this to be conditionally initializing (based on either
* SAL or external data).
*/
not evidence = DefinitionInSnapshot()
or
/*
* If this function is defined in this snapshot, then it conditionally initializes if there
* is at least one path through the function which doesn't initialize the parameter.
*
* Explicitly ignore pure virtual functions.
*/
this.isDefined() and
this.paramNotReassignedAt(this, i, c) and
not this instanceof PureVirtualFunction
)
}
/** Gets the evidence associated with the given parameter. */
Evidence getEvidence(int param) {
/*
* Note: due to the way the predicate dispatch interacts with fields, this needs to be
* implemented on this class, not `InitializationFunction`. If implemented on the latter it
* can return evidence that does not result in conditional initialization.
*/
param = i and evidence = result
}
/** Gets the index of a parameter which is conditionally initialized. */
int conditionallyInitializedParameter(Context context) { result = i and context = c }
}
/**
* More elaborate tracking, flagging cases where the status is checked after
* the potentially uninitialized variable has been used, and ignoring cases
* where the status is not checked but there is no use of the potentially
* uninitialized variable, may be obtained via `getARiskyAccess`.
*/
class ConditionalInitializationCall extends FunctionCall {
ConditionalInitializationFunction target;
ConditionalInitializationCall() { target = getTarget(this) }
/** Gets the argument passed for the given parameter to this call. */
Expr getArgumentForParameter(Parameter p) {
p = getTarget().getAParameter() and
result = getArgument(p.getIndex())
}
/**
* Gets an argument conditionally initialized by this call.
*/
Expr getAConditionallyInitializedArgument(ConditionalInitializationFunction condTarget, Evidence e) {
condTarget = target and
exists(Context context |
result = getAConditionallyInitializedArgument(this, condTarget, context, e)
|
context = NoContext()
or
exists(Parameter otherP, Expr otherArg |
context = ParamNotNull(otherP) or
context = ParamNull(otherP)
|
otherArg = getArgumentForParameter(otherP) and
(otherArg instanceof AddressOfExpr implies context = ParamNotNull(otherP)) and
(otherArg.getType() instanceof ArrayType implies context = ParamNotNull(otherP)) and
(otherArg.getValue() = "0" implies context = ParamNull(otherP))
)
)
}
VariableAccess getAConditionallyInitializedVariable() {
not result.getTarget().getAnAssignedValue().getASuccessor+() = result and
// Should not be assigned field-wise prior to the call.
not exists(Assignment a, FieldAccess fa |
fa.getQualifier() = result.getTarget().getAnAccess() and
a.getLValue() = fa and
fa.getASuccessor+() = result
) and
result = this
.getArgument(getTarget(this)
.(ConditionalInitializationFunction)
.conditionallyInitializedParameter(_))
.(AddressOfExpr)
.getOperand()
}
Variable getStatusVariable() {
exists(AssignExpr a | a.getLValue() = result.getAnAccess() | a.getRValue() = this)
or
result.getInitializer().getExpr() = this
}
Expr getSuccessCheck() {
exists(this.getAFalseSuccessor()) and result = this
or
result = this.getParent() and
(
result instanceof NotExpr or
result.(EQExpr).getAnOperand().getValue() = "0" or
result.(GEExpr).getLesserOperand().getValue() = "0"
)
}
Expr getFailureCheck() {
result = this.getParent() and
(
result instanceof NotExpr or
result.(NEExpr).getAnOperand().getValue() = "0" or
result.(LTExpr).getLesserOperand().getValue() = "0"
)
}
private predicate inCheckedContext() {
exists(Call parent | this = parent.getAnArgument() |
parent.getTarget() instanceof Operator or
parent.getTarget().hasName("VerifyOkCatastrophic")
)
}
ControlFlowNode uncheckedReaches(LocalVariable var) {
(
not exists(var.getInitializer()) and
var = this.getAConditionallyInitializedVariable().getTarget() and
if exists(this.getFailureCheck())
then result = this.getFailureCheck().getATrueSuccessor()
else
if exists(this.getSuccessCheck())
then result = this.getSuccessCheck().getAFalseSuccessor()
else (
result = this.getASuccessor() and not this.inCheckedContext()
)
)
or
exists(ControlFlowNode mid | mid = uncheckedReaches(var) |
not mid = getStatusVariable().getAnAccess() and
not mid = var.getAnAccess() and
not exists(VariableAccess write | result = write and write = var.getAnAccess() |
write = any(AssignExpr a).getLValue() or
write = any(AddressOfExpr a).getOperand()
) and
result = mid.getASuccessor()
)
}
VariableAccess getARiskyRead(Function f) {
f = this.getTarget() and
exists(this.getFile().getRelativePath()) and
result = this.uncheckedReaches(result.getTarget()) and
not this.(GuardCondition).controls(result.getBasicBlock(), _)
}
}
/**
* Gets the position of an argument to the call which is initialized by the call.
*/
pragma[nomagic]
int initializedArgument(Call call) {
exists(InitializationFunction target |
target = getTarget(call) and
result = target.initializedParameter()
)
}
/**
* Gets an argument which is initialized by the call.
*/
Expr getAnInitializedArgument(Call call) { result = call.getArgument(initializedArgument(call)) }
/**
* Gets the position of an argument to the call to the target which is conditionally initialized by
* the call, under the given context and evidence.
*/
pragma[nomagic]
private int conditionallyInitializedArgument(
Call call, ConditionalInitializationFunction target, Context c, Evidence e
) {
target = getTarget(call) and
c = target.getAContext(result) and
e = target.getEvidence(result) and
result = target.conditionallyInitializedParameter(c)
}
/**
* Gets an argument which is conditionally initialized by the call to the given target under the given context and evidence.
*/
Expr getAConditionallyInitializedArgument(
Call call, ConditionalInitializationFunction target, Context c, Evidence e
) {
result = call.getArgument(conditionallyInitializedArgument(call, target, c, e))
}
/**
* Gets the type signature for the functions parameters.
*/
private string typeSig(Function f) {
result = concat(int i, Type pt |
pt = f.getParameter(i).getType()
|
pt.getUnspecifiedType().toString(), "," order by i
)
}
/**
* Holds where qualifiedName and typeSig make up the signature for the function.
*/
private predicate functionSignature(Function f, string qualifiedName, string typeSig) {
qualifiedName = f.getQualifiedName() and
typeSig = typeSig(f)
}
/**
* Gets a possible definition for the undefined function by matching the undefined function name
* and parameter arity with a defined function.
*
* This is useful for identifying call to target dependencies across libraries, where the libraries
* are never statically linked together.
*/
private Function getAPossibleDefinition(Function undefinedFunction) {
not undefinedFunction.isDefined() and
exists(string qn, string typeSig |
functionSignature(undefinedFunction, qn, typeSig) and functionSignature(result, qn, typeSig)
) and
result.isDefined()
}
/**
* Helper predicate for `getTarget`, that computes possible targets of a `Call`.
*
* If there is at least one defined target after performing some simple virtual dispatch
* resolution, then the result is all the defined targets.
*/
private Function getTarget1(Call c) {
result = VirtualDispatch::getAViableTarget(c) and
result.isDefined()
}
/**
* Helper predicate for `getTarget`, that computes possible targets of a `Call`.
*
* If we can use the heuristic matching of functions to find definitions for some of the viable
* targets, return those.
*/
private Function getTarget2(Call c) {
not exists(getTarget1(c)) and
result = getAPossibleDefinition(VirtualDispatch::getAViableTarget(c))
}
/**
* Helper predicate for `getTarget`, that computes possible targets of a `Call`.
*
* Otherwise, the result is the undefined `Function` instances.
*/
private Function getTarget3(Call c) {
not exists(getTarget1(c)) and
not exists(getTarget2(c)) and
result = VirtualDispatch::getAViableTarget(c)
}
/**
* Gets a possible target for the `Call`, using the name and parameter matching if we did not associate
* this call with a specific definition at link or compile time, and performing simple virtual
* dispatch resolution.
*/
Function getTarget(Call c) {
result = getTarget1(c) or
result = getTarget2(c) or
result = getTarget3(c)
}
/**
* Get an access of a field on `Variable` v.
*/
FieldAccess getAFieldAccess(Variable v) {
exists(VariableAccess va, Expr qualifierExpr |
// Find an access of the variable, or an AddressOfExpr that has the access
va = v.getAnAccess() and
(
qualifierExpr = va or
qualifierExpr.(AddressOfExpr).getOperand() = va
)
|
// Direct field access
qualifierExpr = result.getQualifier()
or
// Nested field access
qualifierExpr = result.(NestedFieldAccess).getUltimateQualifier()
)
}
/**
* Gets a condition which is checked to be false by the given `ne` expression, according to this pattern:
* ```
* int a = !!result;
* if (!a) { // <- ne
* ....
* }
* ```
*/
private Expr getCheckedFalseCondition(NotExpr ne) {
exists(LocalVariable v |
result = v.getInitializer().getExpr().(NotExpr).getOperand().(NotExpr).getOperand() and
ne.getOperand() = v.getAnAccess() and
nonAssignedVariable(v)
// and not passed by val?
)
}
pragma[noinline]
private predicate nonAssignedVariable(Variable v) { not exists(v.getAnAssignment()) }

View File

@@ -0,0 +1,190 @@
/**
* A module for identifying conditionally initialized variables.
*/
import cpp
import InitializationFunctions
// Optimised reachability predicates
private predicate reaches(ControlFlowNode a, ControlFlowNode b) = fastTC(successor/2)(a, b)
private predicate successor(ControlFlowNode a, ControlFlowNode b) { b = a.getASuccessor() }
class WhitelistedCallsConfig extends string {
WhitelistedCallsConfig() { this = "config" }
abstract predicate isWhitelisted(Call c);
}
abstract class WhitelistedCall extends Call {
override Function getTarget() { none() }
}
private predicate hasConditionalInitialization(
ConditionalInitializationFunction f, ConditionalInitializationCall call, LocalVariable v,
VariableAccess initAccess, Evidence e
) {
// Ignore whitelisted calls
not call instanceof WhitelistedCall and
f = getTarget(call) and
initAccess = v.getAnAccess() and
initAccess = call.getAConditionallyInitializedArgument(f, e).(AddressOfExpr).getOperand()
}
/**
* A variable that can be conditionally initialized by a call.
*/
class ConditionallyInitializedVariable extends LocalVariable {
ConditionalInitializationCall call;
ConditionalInitializationFunction f;
VariableAccess initAccess;
Evidence e;
ConditionallyInitializedVariable() {
// Find a call that conditionally initializes this variable
hasConditionalInitialization(f, call, this, initAccess, e) and
// Ignore cases where the variable is assigned prior to the call
not reaches(getAnAssignedValue(), initAccess) and
// Ignore cases where the variable is assigned field-wise prior to the call.
not exists(FieldAccess fa |
exists(Assignment a |
fa = getAFieldAccess(this) and
a.getLValue() = fa
)
|
reaches(fa, initAccess)
) and
// Ignore cases where the variable is assigned by a prior call to an initialization function
not exists(Call c |
getAnAccess() = getAnInitializedArgument(c).(AddressOfExpr).getOperand() and
reaches(c, initAccess)
) and
/*
* Static local variables with constant initializers do not have the initializer expr as part of
* the CFG, but should always be considered as initialized, so exclude them.
*/
not exists(getInitializer().getExpr())
}
/**
* Gets an access of the variable `v` which is not used as an lvalue, and not used as an argument
* to an initialization function.
*/
private VariableAccess getAReadAccess() {
result = this.getAnAccess() and
// Not used as an lvalue
not result = any(AssignExpr a).getLValue() and
// Not passed to another initialization function
not exists(Call c, int j | j = c.getTarget().(InitializationFunction).initializedParameter() |
result = c.getArgument(j).(AddressOfExpr).getOperand()
) and
// Not a pointless read
not result = any(ExprStmt es).getExpr()
}
/**
* Gets a read access of variable `v` that occurs after the `initializingCall`.
*/
private VariableAccess getAReadAccessAfterCall(ConditionalInitializationCall initializingCall) {
// Variable associated with this particular call
call = initializingCall and
// Access is a meaningful read access
result = getAReadAccess() and
// Which occurs after the call
reaches(call, result) and
/*
* Ignore risky accesses which are arguments to calls which also include another parameter to
* the original call. This is an attempt to eliminate results where the "status" can be checked
* through another parameter that assigned as part of the original call.
*/
not exists(Call c |
c.getAnArgument() = result or
c.getAnArgument().(AddressOfExpr).getOperand() = result
|
exists(LocalVariable lv |
call.getAnArgument().(AddressOfExpr).getOperand() = lv.getAnAccess() and
not lv = this
|
c.getAnArgument() = lv.getAnAccess()
)
)
}
/**
* Gets an access to the variable that is risky because the variable may not be initialized after
* the `call`, and the status of the call is never checked.
*/
VariableAccess getARiskyAccessWithNoStatusCheck(
ConditionalInitializationFunction initializingFunction,
ConditionalInitializationCall initializingCall, Evidence evidence
) {
// Variable associated with this particular call
call = initializingCall and
initializingFunction = f and
e = evidence and
result = getAReadAccessAfterCall(initializingCall) and
(
// Access is risky because status return code ignored completely
call instanceof ExprInVoidContext
or
// Access is risky because status return code ignored completely
exists(LocalVariable status | call = status.getAnAssignedValue() |
not exists(status.getAnAccess())
)
)
}
/**
* Gets an access to the variable that is risky because the variable may not be initialized after
* the `call`, and the status of the call is only checked after the risky access.
*/
VariableAccess getARiskyAccessBeforeStatusCheck(
ConditionalInitializationFunction initializingFunction,
ConditionalInitializationCall initializingCall, Evidence evidence
) {
// Variable associated with this particular call
call = initializingCall and
initializingFunction = f and
e = evidence and
result = getAReadAccessAfterCall(initializingCall) and
exists(LocalVariable status, Assignment a |
a.getRValue() = call and
call = status.getAnAssignedValue() and
// There exists a check of the status code
definitionUsePair(status, a, _) and
// And the check of the status code does not occur before the risky access
not exists(VariableAccess statusAccess |
definitionUsePair(status, a, statusAccess) and
reaches(statusAccess, result)
) and
// Ignore cases where the assignment to the status code is used directly
a instanceof ExprInVoidContext and
/*
* Ignore risky accesses which are arguments to calls which also include the status code.
* If both the risky value and status code are passed to a different function, that
* function is responsible for checking the status code.
*/
not exists(Call c |
c.getAnArgument() = result or
c.getAnArgument().(AddressOfExpr).getOperand() = result
|
definitionUsePair(status, a, c.getAnArgument())
)
)
}
/**
* Gets an access to the variable that is risky because the variable may not be initialized after
* the `call`.
*/
VariableAccess getARiskyAccess(
ConditionalInitializationFunction initializingFunction,
ConditionalInitializationCall initializingCall, Evidence evidence
) {
result = getARiskyAccessBeforeStatusCheck(initializingFunction, initializingCall, evidence) or
result = getARiskyAccessWithNoStatusCheck(initializingFunction, initializingCall, evidence)
}
}

View File

@@ -190,11 +190,11 @@ private predicate windowsSystemInfo(FunctionCall source, Element use) {
// void WINAPI GetSystemInfo(_Out_ LPSYSTEM_INFO lpSystemInfo); // void WINAPI GetSystemInfo(_Out_ LPSYSTEM_INFO lpSystemInfo);
// void WINAPI GetNativeSystemInfo(_Out_ LPSYSTEM_INFO lpSystemInfo); // void WINAPI GetNativeSystemInfo(_Out_ LPSYSTEM_INFO lpSystemInfo);
( (
source.getTarget().hasName("GetVersionEx") or source.getTarget().hasGlobalName("GetVersionEx") or
source.getTarget().hasName("GetVersionExA") or source.getTarget().hasGlobalName("GetVersionExA") or
source.getTarget().hasName("GetVersionExW") or source.getTarget().hasGlobalName("GetVersionExW") or
source.getTarget().hasName("GetSystemInfo") or source.getTarget().hasGlobalName("GetSystemInfo") or
source.getTarget().hasName("GetNativeSystemInfo") source.getTarget().hasGlobalName("GetNativeSystemInfo")
) and ) and
use = source.getArgument(0) use = source.getArgument(0)
} }
@@ -216,9 +216,9 @@ private predicate windowsFolderPath(FunctionCall source, Element use) {
// _In_ BOOL fCreate // _In_ BOOL fCreate
// ); // );
( (
source.getTarget().hasName("SHGetSpecialFolderPath") or source.getTarget().hasGlobalName("SHGetSpecialFolderPath") or
source.getTarget().hasName("SHGetSpecialFolderPathA") or source.getTarget().hasGlobalName("SHGetSpecialFolderPathA") or
source.getTarget().hasName("SHGetSpecialFolderPathW") source.getTarget().hasGlobalName("SHGetSpecialFolderPathW")
) and ) and
use = source.getArgument(1) use = source.getArgument(1)
or or
@@ -228,7 +228,7 @@ private predicate windowsFolderPath(FunctionCall source, Element use) {
// _In_opt_ HANDLE hToken, // _In_opt_ HANDLE hToken,
// _Out_ PWSTR *ppszPath // _Out_ PWSTR *ppszPath
// ); // );
source.getTarget().hasName("SHGetKnownFolderPath") and source.getTarget().hasGlobalName("SHGetKnownFolderPath") and
use = source.getArgument(3) use = source.getArgument(3)
or or
// HRESULT SHGetFolderPath( // HRESULT SHGetFolderPath(
@@ -239,9 +239,9 @@ private predicate windowsFolderPath(FunctionCall source, Element use) {
// _Out_ LPTSTR pszPath // _Out_ LPTSTR pszPath
// ); // );
( (
source.getTarget().hasName("SHGetFolderPath") or source.getTarget().hasGlobalName("SHGetFolderPath") or
source.getTarget().hasName("SHGetFolderPathA") or source.getTarget().hasGlobalName("SHGetFolderPathA") or
source.getTarget().hasName("SHGetFolderPathW") source.getTarget().hasGlobalName("SHGetFolderPathW")
) and ) and
use = source.getArgument(4) use = source.getArgument(4)
or or
@@ -254,9 +254,9 @@ private predicate windowsFolderPath(FunctionCall source, Element use) {
// _Out_ LPTSTR pszPath // _Out_ LPTSTR pszPath
// ); // );
( (
source.getTarget().hasName("SHGetFolderPathAndSubDir") or source.getTarget().hasGlobalName("SHGetFolderPathAndSubDir") or
source.getTarget().hasName("SHGetFolderPathAndSubDirA") or source.getTarget().hasGlobalName("SHGetFolderPathAndSubDirA") or
source.getTarget().hasName("SHGetFolderPathAndSubDirW") source.getTarget().hasGlobalName("SHGetFolderPathAndSubDirW")
) and ) and
use = source.getArgument(5) use = source.getArgument(5)
} }
@@ -273,9 +273,9 @@ class WindowsFolderPath extends SystemData {
private predicate logonUser(FunctionCall source, VariableAccess use) { private predicate logonUser(FunctionCall source, VariableAccess use) {
( (
source.getTarget().hasName("LogonUser") or source.getTarget().hasGlobalName("LogonUser") or
source.getTarget().hasName("LogonUserW") or source.getTarget().hasGlobalName("LogonUserW") or
source.getTarget().hasName("LogonUserA") source.getTarget().hasGlobalName("LogonUserA")
) and ) and
use = source.getAnArgument() use = source.getAnArgument()
} }
@@ -297,9 +297,9 @@ private predicate regQuery(FunctionCall source, VariableAccess use) {
// _Inout_opt_ PLONG lpcbValue // _Inout_opt_ PLONG lpcbValue
// ); // );
( (
source.getTarget().hasName("RegQueryValue") or source.getTarget().hasGlobalName("RegQueryValue") or
source.getTarget().hasName("RegQueryValueA") or source.getTarget().hasGlobalName("RegQueryValueA") or
source.getTarget().hasName("RegQueryValueW") source.getTarget().hasGlobalName("RegQueryValueW")
) and ) and
use = source.getArgument(2) use = source.getArgument(2)
or or
@@ -311,9 +311,9 @@ private predicate regQuery(FunctionCall source, VariableAccess use) {
// _Inout_opt_ LPDWORD ldwTotsize // _Inout_opt_ LPDWORD ldwTotsize
// ); // );
( (
source.getTarget().hasName("RegQueryMultipleValues") or source.getTarget().hasGlobalName("RegQueryMultipleValues") or
source.getTarget().hasName("RegQueryMultipleValuesA") or source.getTarget().hasGlobalName("RegQueryMultipleValuesA") or
source.getTarget().hasName("RegQueryMultipleValuesW") source.getTarget().hasGlobalName("RegQueryMultipleValuesW")
) and ) and
use = source.getArgument(3) use = source.getArgument(3)
or or
@@ -326,9 +326,9 @@ private predicate regQuery(FunctionCall source, VariableAccess use) {
// _Inout_opt_ LPDWORD lpcbData // _Inout_opt_ LPDWORD lpcbData
// ); // );
( (
source.getTarget().hasName("RegQueryValueEx") or source.getTarget().hasGlobalName("RegQueryValueEx") or
source.getTarget().hasName("RegQueryValueExA") or source.getTarget().hasGlobalName("RegQueryValueExA") or
source.getTarget().hasName("RegQueryValueExW") source.getTarget().hasGlobalName("RegQueryValueExW")
) and ) and
use = source.getArgument(4) use = source.getArgument(4)
or or
@@ -342,9 +342,9 @@ private predicate regQuery(FunctionCall source, VariableAccess use) {
// _Inout_opt_ LPDWORD pcbData // _Inout_opt_ LPDWORD pcbData
// ); // );
( (
source.getTarget().hasName("RegGetValue") or source.getTarget().hasGlobalName("RegGetValue") or
source.getTarget().hasName("RegGetValueA") or source.getTarget().hasGlobalName("RegGetValueA") or
source.getTarget().hasName("RegGetValueW") source.getTarget().hasGlobalName("RegGetValueW")
) and ) and
use = source.getArgument(5) use = source.getArgument(5)
} }

View File

@@ -15,5 +15,5 @@ import cpp
from FunctionCall call, Function target from FunctionCall call, Function target
where where
call.getTarget() = target and call.getTarget() = target and
target.hasGlobalName("gets") target.hasGlobalOrStdName("gets")
select call, "gets does not guard against buffer overflow" select call, "gets does not guard against buffer overflow"

View File

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

View File

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

View File

@@ -22,7 +22,7 @@ predicate acquireExpr(Expr acquire, string kind) {
exists(FunctionCall fc, Function f, string name | exists(FunctionCall fc, Function f, string name |
fc = acquire and fc = acquire and
f = fc.getTarget() and f = fc.getTarget() and
f.hasGlobalName(name) and f.hasGlobalOrStdName(name) and
( (
name = "fopen" and name = "fopen" and
kind = "file" kind = "file"
@@ -46,7 +46,7 @@ predicate releaseExpr(Expr release, Expr resource, string kind) {
exists(FunctionCall fc, Function f, string name | exists(FunctionCall fc, Function f, string name |
fc = release and fc = release and
f = fc.getTarget() and f = fc.getTarget() and
f.hasGlobalName(name) and f.hasGlobalOrStdName(name) and
( (
name = "fclose" and name = "fclose" and
resource = fc.getArgument(0) and resource = fc.getArgument(0) and

View File

@@ -51,7 +51,7 @@ class ReferenceCopyAssignmentOperator extends MemberFunction {
/** /**
* A call to a function called swap. Note: could be a member, * A call to a function called swap. Note: could be a member,
* std::swap or a function overloading std::swap (not in std::) * `std::swap` or a function overloading `std::swap` (not in `std::`)
* so keep it simple * so keep it simple
*/ */
FunctionCall getASwapCall() { FunctionCall getASwapCall() {

View File

@@ -22,8 +22,8 @@ predicate containsArray(Type t) {
or or
containsArray(t.getUnderlyingType()) and containsArray(t.getUnderlyingType()) and
not exists(TypedefType allowed | allowed = t | not exists(TypedefType allowed | allowed = t |
allowed.hasGlobalName("jmp_buf") or allowed.hasGlobalOrStdName("jmp_buf") or
allowed.hasGlobalName("va_list") allowed.hasGlobalOrStdName("va_list")
) )
} }

View File

@@ -10,7 +10,7 @@
/* /*
* See More Effective C++ item 7. * See More Effective C++ item 7.
* Note: Meyers allows unary & to be overloaded but not comma * Note: Meyers allows unary `&` to be overloaded but not comma.
*/ */
import cpp import cpp

View File

@@ -15,11 +15,11 @@ import cpp
/* /*
* Interpretation and deviations: * Interpretation and deviations:
* - if the higher operator has precedence > arithmetic then it is fine * - if the higher operator has precedence > arithmetic then it is fine
* RATIONALE: exprs like f(), *x, &x are easily understood to bind tightly * RATIONALE: exprs like `f()`, `*x`, `&x` are easily understood to bind tightly
* - if the higher operator is the RHS of an assign then it is fine * - if the higher operator is the RHS of an assign then it is fine
* RATIONALE: cf. MISRA, too many cases excluded otherwise * RATIONALE: cf. MISRA, too many cases excluded otherwise
* - comparison operators can be mixed with arithmetic * - comparison operators can be mixed with arithmetic
* RATIONALE: x==y+z is common and unambiguous * RATIONALE: `x==y+z` is common and unambiguous
*/ */
predicate arithmeticPrecedence(int p) { p = 12 or p = 13 } predicate arithmeticPrecedence(int p) { p = 12 or p = 13 }

4
cpp/ql/src/qlpack.yml Normal file
View File

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

View File

@@ -605,15 +605,6 @@ class Class extends UserType {
class_instantiation(underlyingElement(this), unresolveElement(c)) 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 * Holds if this class/struct is polymorphic (has a virtual function, or
* inherits one). * inherits one).
@@ -623,7 +614,7 @@ class Class extends UserType {
} }
override predicate involvesTemplateParameter() { override predicate involvesTemplateParameter() {
getATemplateArgument().involvesTemplateParameter() getATemplateArgument().(Type).involvesTemplateParameter()
} }
/** Holds if this class, struct or union was declared 'final'. */ /** Holds if this class, struct or union was declared 'final'. */

View File

@@ -14,8 +14,12 @@ private import semmle.code.cpp.internal.QualifiedName as Q
* ``` * ```
* extern int myglobal; * extern int myglobal;
* ``` * ```
* Each of these declarations is given its own distinct `DeclarationEntry`, * and defined in one:
* but they all share the same `Declaration`. * ```
* int myglobal;
* ```
* Each of these declarations (including the definition) is given its own
* distinct `DeclarationEntry`, but they all share the same `Declaration`.
* *
* Some derived class of `Declaration` do not have a corresponding * Some derived class of `Declaration` do not have a corresponding
* `DeclarationEntry`, because they always have a unique source location. * `DeclarationEntry`, because they always have a unique source location.
@@ -119,6 +123,13 @@ abstract class Declaration extends Locatable, @declaration {
/** Holds if this declaration has the given name in the global namespace. */ /** Holds if this declaration has the given name in the global namespace. */
predicate hasGlobalName(string name) { this.hasQualifiedName("", "", name) } 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. */ /** Gets a specifier of this declaration. */
abstract Specifier getASpecifier(); abstract Specifier getASpecifier();
@@ -189,26 +200,99 @@ abstract class Declaration extends Locatable, @declaration {
/** /**
* Gets a template argument used to instantiate this declaration from a template. * 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 * 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. */ /** Gets the number of template arguments for this declaration. */
final int getNumberOfTemplateArguments() { final int getNumberOfTemplateArguments() {
result = count(int i | exists(getTemplateArgument(i))) result = count(int i | exists(getTemplateArgument(i)))
} }
private Type getTemplateArgumentType(int index) {
class_template_argument(underlyingElement(this), index, unresolveElement(result))
or
function_template_argument(underlyingElement(this), index, unresolveElement(result))
or
variable_template_argument(underlyingElement(this), index, unresolveElement(result))
}
private Expr getTemplateArgumentValue(int index) {
class_template_argument_value(underlyingElement(this), index, unresolveElement(result))
or
function_template_argument_value(underlyingElement(this), index, unresolveElement(result))
or
variable_template_argument_value(underlyingElement(this), index, unresolveElement(result))
}
} }
/** /**
* A C/C++ declaration entry. See the comment above `Declaration` for an * A C/C++ declaration entry. For example the following code contains five
* explanation of the relationship between `Declaration` and * declaration entries:
* `DeclarationEntry`. * ```
* extern int myGlobal;
* int myVariable;
* typedef char MyChar;
* void myFunction();
* void myFunction() {
* // ...
* }
* ```
* See the comment above `Declaration` for an explanation of the relationship
* between `Declaration` and `DeclarationEntry`.
*/ */
abstract class DeclarationEntry extends Locatable { abstract class DeclarationEntry extends Locatable {
/** Gets a specifier associated with this declaration entry. */ /** Gets a specifier associated with this declaration entry. */
@@ -281,8 +365,19 @@ abstract class DeclarationEntry extends Locatable {
* A declaration that can potentially have more C++ access rights than its * A declaration that can potentially have more C++ access rights than its
* enclosing element. This comprises `Class` (they have access to their own * enclosing element. This comprises `Class` (they have access to their own
* private members) along with other `UserType`s and `Function` (they can be * private members) along with other `UserType`s and `Function` (they can be
* the target of `friend` declarations). * the target of `friend` declarations). For example `MyClass` and
* `myFunction` in the following code:
* ```
* class MyClass
* {
* public:
* ...
* };
* *
* void myFunction() {
* // ...
* }
* ```
* In the C++ standard (N4140 11.2), rules for access control revolve around * In the C++ standard (N4140 11.2), rules for access control revolve around
* the informal phrase "_R_ occurs in a member or friend of class C", where * the informal phrase "_R_ occurs in a member or friend of class C", where
* `AccessHolder` corresponds to this _R_. * `AccessHolder` corresponds to this _R_.
@@ -416,8 +511,19 @@ abstract class AccessHolder extends Declaration {
/** /**
* A declaration that very likely has more C++ access rights than its * A declaration that very likely has more C++ access rights than its
* enclosing element. This comprises `Class` (they have access to their own * enclosing element. This comprises `Class` (they have access to their own
* private members) along with any target of a `friend` declaration. * private members) along with any target of a `friend` declaration. For
* example `MyClass` and `friendFunction` in the following code:
* ```
* class MyClass
* {
* public:
* friend void friendFunction();
* };
* *
* void friendFunction() {
* // ...
* }
* ```
* Most access rights are computed for `DirectAccessHolder` instead of * Most access rights are computed for `DirectAccessHolder` instead of
* `AccessHolder` -- that's more efficient because there are fewer * `AccessHolder` -- that's more efficient because there are fewer
* `DirectAccessHolder`s. If a `DirectAccessHolder` contains an `AccessHolder`, * `DirectAccessHolder`s. If a `DirectAccessHolder` contains an `AccessHolder`,

View File

@@ -343,15 +343,6 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
function_instantiation(underlyingElement(this), unresolveElement(f)) 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 * Holds if this function is defined in several files. This is illegal in
* C (though possible in some C++ compilers), and likely indicates that * 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. // ... and likewise for destructors.
this.(Destructor).getADestruction().mayBeGloballyImpure() this.(Destructor).getADestruction().mayBeGloballyImpure()
else 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 // Unless it's a function that we know is side-effect-free, it may
// have side-effects. // have side-effects.
name = "strcmp" or name = "strcmp" or

View File

@@ -0,0 +1,40 @@
import cpp
/**
* 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()
or
exists(NestedStruct ns |
s = ns.getDeclaringType() and
result = getANestedField(ns)
)
}
/**
* Unwraps a series of field accesses to determine the outer-most qualifier.
*/
private Expr getUltimateQualifier(FieldAccess fa) {
exists(Expr qualifier | qualifier = fa.getQualifier() |
result = getUltimateQualifier(qualifier)
or
not qualifier instanceof FieldAccess and result = qualifier
)
}
/**
* Accesses to nested fields.
*/
class NestedFieldAccess extends FieldAccess {
Expr ultimateQualifier;
NestedFieldAccess() {
ultimateQualifier = getUltimateQualifier(this) and
getTarget() = getANestedField(ultimateQualifier.getType().stripType())
}
/** Gets the ultimate qualifier of this nested field access. */
Expr getUltimateQualifier() { result = ultimateQualifier }
}

View File

@@ -158,3 +158,10 @@ class Parameter extends LocalScopeVariable, @parameter {
) )
} }
} }
/**
* An `int` that is a parameter index for some function. This is needed for binding in certain cases.
*/
class ParameterIndex extends int {
ParameterIndex() { exists(Parameter p | this = p.getIndex()) }
}

View File

@@ -35,6 +35,14 @@ private string getParameterTypeString(Type parameterType) {
else result = parameterType.(DumpType).getTypeIdentityString() 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. * 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 | strictconcat(int i |
exists(this.getTemplateArgument(i)) exists(this.getTemplateArgument(i))
| |
this.getTemplateArgument(i).(DumpType).getTypeIdentityString(), ", " order by i getTemplateArgumentString(this, i), ", " order by i
) + ">" ) + ">"
else result = "" else result = ""
} }

View File

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

View File

@@ -5,6 +5,8 @@ private import semmle.code.cpp.internal.ResolveClass
/** /**
* A C/C++ type. * A C/C++ type.
*
* This QL class represents the root of the C/C++ type hierarchy.
*/ */
class Type extends Locatable, @type { class Type extends Locatable, @type {
Type() { isType(underlyingElement(this)) } Type() { isType(underlyingElement(this)) }
@@ -210,7 +212,7 @@ class Type extends Locatable, @type {
// A function call that provides an explicit template argument that refers to T uses T. // 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. // We exclude calls within instantiations, since they do not appear directly in the source.
exists(FunctionCall c | exists(FunctionCall c |
c.getAnExplicitTemplateArgument().refersTo(this) and c.getAnExplicitTemplateArgument().(Type).refersTo(this) and
result = c and result = c and
not c.getEnclosingFunction().isConstructedFrom(_) not c.getEnclosingFunction().isConstructedFrom(_)
) )
@@ -289,6 +291,13 @@ class Type extends Locatable, @type {
/** /**
* A C/C++ built-in primitive type (int, float, void, and so on). See 4.1.1. * A C/C++ built-in primitive type (int, float, void, and so on). See 4.1.1.
* In the following example, `unsigned int` and `double` denote primitive
* built-in types:
* ```
* double a;
* unsigned int ua[40];
* typedef double LargeFloat;
* ```
*/ */
class BuiltInType extends Type, @builtintype { class BuiltInType extends Type, @builtintype {
override string toString() { result = this.getName() } override string toString() { result = this.getName() }
@@ -301,7 +310,14 @@ class BuiltInType extends Type, @builtintype {
} }
/** /**
* An erroneous type. * An erroneous type. This type has no corresponding C/C++ syntax.
*
* `ErroneousType` is the type of `ErrorExpr`, which in turn refers to an illegal
* language construct. In the example below, a temporary (`0`) cannot be bound
* to an lvalue reference (`int &`):
* ```
* int &intref = 0;
* ```
*/ */
class ErroneousType extends BuiltInType { class ErroneousType extends BuiltInType {
ErroneousType() { builtintypes(underlyingElement(this), _, 1, _, _, _) } ErroneousType() { builtintypes(underlyingElement(this), _, 1, _, _, _) }
@@ -310,7 +326,18 @@ class ErroneousType extends BuiltInType {
} }
/** /**
* The unknown type. * The unknown type. This type has no corresponding C/C++ syntax.
*
* Unknown types usually occur inside _uninstantiated_ template functions.
* In the example below, the expressions `x.a` and `x.b` have unknown type
* in the _uninstantiated_ template.
* ```
* template<typename T>
* bool check(T x) {
* if (x.a == x.b)
* abort();
* }
* ```
*/ */
class UnknownType extends BuiltInType { class UnknownType extends BuiltInType {
UnknownType() { builtintypes(underlyingElement(this), _, 2, _, _, _) } UnknownType() { builtintypes(underlyingElement(this), _, 2, _, _, _) }
@@ -326,6 +353,10 @@ private predicate isArithmeticType(@builtintype type, int kind) {
/** /**
* The C/C++ arithmetic types. See 4.1.1. * The C/C++ arithmetic types. See 4.1.1.
*
* This includes primitive types on which arithmetic, bitwise or logical
* operations may be performed. Examples of arithmetic types include
* `char`, `int`, `float`, and `bool`.
*/ */
class ArithmeticType extends BuiltInType { class ArithmeticType extends BuiltInType {
ArithmeticType() { isArithmeticType(underlyingElement(this), _) } ArithmeticType() { isArithmeticType(underlyingElement(this), _) }
@@ -349,11 +380,20 @@ private predicate isIntegralType(@builtintype type, int kind) {
} }
/** /**
* A C/C++ integral or enum type. * A C/C++ integral or `enum` type.
* The definition of "integral type" in the C++ Standard excludes enum types, *
* but because an enum type holds a value of its underlying integral type, * The definition of "integral type" in the C++ standard excludes `enum` types,
* but because an `enum` type holds a value of its underlying integral type,
* it is often useful to have a common category that includes both integral * it is often useful to have a common category that includes both integral
* and enum types. * and `enum` types.
*
* In the following example, `a`, `b` and `c` are all declared with an
* integral or `enum` type:
* ```
* unsigned long a;
* enum e1 { val1, val2 } b;
* enum class e2: short { val3, val4 } c;
* ```
*/ */
class IntegralOrEnumType extends Type { class IntegralOrEnumType extends Type {
IntegralOrEnumType() { IntegralOrEnumType() {
@@ -426,7 +466,17 @@ private predicate integralTypeMapping(int original, int canonical, int unsigned,
} }
/** /**
* The C/C++ integral types. See 4.1.1. * The C/C++ integral types. See 4.1.1. These are types that are represented
* as integers of varying sizes. Both `enum` types and floating-point types
* are excluded.
*
* In the following examples, `a`, `b` and `c` are declared using integral
* types:
* ```
* unsigned int a;
* long long b;
* char c;
* ```
*/ */
class IntegralType extends ArithmeticType, IntegralOrEnumType { class IntegralType extends ArithmeticType, IntegralOrEnumType {
int kind; int kind;
@@ -497,7 +547,12 @@ class IntegralType extends ArithmeticType, IntegralOrEnumType {
} }
/** /**
* The C/C++ boolean type. See 4.2. * The C/C++ boolean type. See 4.2. This is the C `_Bool` type
* or the C++ `bool` type. For example:
* ```
* extern bool a, b; // C++
* _Bool c, d; // C
* ```
*/ */
class BoolType extends IntegralType { class BoolType extends IntegralType {
BoolType() { builtintypes(underlyingElement(this), _, 4, _, _, _) } BoolType() { builtintypes(underlyingElement(this), _, 4, _, _, _) }
@@ -506,12 +561,23 @@ class BoolType extends IntegralType {
} }
/** /**
* The C/C++ character types. See 4.3. * The C/C++ character types. See 4.3. This includes the `char`,
* `signed char` and `unsigned char` types, all of which are
* distinct from one another. For example:
* ```
* char a, b;
* signed char c, d;
* unsigned char e, f;
* ```
*/ */
abstract class CharType extends IntegralType { } abstract class CharType extends IntegralType { }
/** /**
* The C/C++ char type (which is different to signed char and unsigned char). * The C/C++ `char` type (which is distinct from `signed char` and
* `unsigned char`). For example:
* ```
* char a, b;
* ```
*/ */
class PlainCharType extends CharType { class PlainCharType extends CharType {
PlainCharType() { builtintypes(underlyingElement(this), _, 5, _, _, _) } PlainCharType() { builtintypes(underlyingElement(this), _, 5, _, _, _) }
@@ -520,7 +586,11 @@ class PlainCharType extends CharType {
} }
/** /**
* The C/C++ unsigned char type (which is different to plain char, even when chars are unsigned by default). * The C/C++ `unsigned char` type (which is distinct from plain `char`
* even when `char` is `unsigned` by default).
* ```
* unsigned char e, f;
* ```
*/ */
class UnsignedCharType extends CharType { class UnsignedCharType extends CharType {
UnsignedCharType() { builtintypes(underlyingElement(this), _, 6, _, _, _) } UnsignedCharType() { builtintypes(underlyingElement(this), _, 6, _, _, _) }
@@ -529,7 +599,11 @@ class UnsignedCharType extends CharType {
} }
/** /**
* The C/C++ signed char type (which is different to plain char, even when chars are signed by default). * The C/C++ `signed char` type (which is distinct from plain `char`
* even when `char` is `signed` by default).
* ```
* signed char c, d;
* ```
*/ */
class SignedCharType extends CharType { class SignedCharType extends CharType {
SignedCharType() { builtintypes(underlyingElement(this), _, 7, _, _, _) } SignedCharType() { builtintypes(underlyingElement(this), _, 7, _, _, _) }
@@ -538,7 +612,11 @@ class SignedCharType extends CharType {
} }
/** /**
* The C/C++ short types. See 4.3. * The C/C++ short types. See 4.3. This includes `short`, `signed short`
* and `unsigned short`.
* ```
* signed short ss;
* ```
*/ */
class ShortType extends IntegralType { class ShortType extends IntegralType {
ShortType() { ShortType() {
@@ -551,7 +629,11 @@ class ShortType extends IntegralType {
} }
/** /**
* The C/C++ integer types. See 4.4. * The C/C++ integer types. See 4.4. This includes `int`, `signed int`
* and `unsigned int`.
* ```
* unsigned int ui;
* ```
*/ */
class IntType extends IntegralType { class IntType extends IntegralType {
IntType() { IntType() {
@@ -564,7 +646,11 @@ class IntType extends IntegralType {
} }
/** /**
* The C/C++ long types. See 4.4. * The C/C++ long types. See 4.4. This includes `long`, `signed long`
* and `unsigned long`.
* ```
* long l;
* ```
*/ */
class LongType extends IntegralType { class LongType extends IntegralType {
LongType() { LongType() {
@@ -577,7 +663,11 @@ class LongType extends IntegralType {
} }
/** /**
* The C/C++ long long types. See 4.4. * The C/C++ long long types. See 4.4. This includes `long long`, `signed long long`
* and `unsigned long long`.
* ```
* signed long long sll;
* ```
*/ */
class LongLongType extends IntegralType { class LongLongType extends IntegralType {
LongLongType() { LongLongType() {
@@ -590,7 +680,12 @@ class LongLongType extends IntegralType {
} }
/** /**
* The GNU C __int128 types. * The GNU C __int128 primitive types. They are not part of standard C/C++.
*
* This includes `__int128`, `signed __int128` and `unsigned __int128`.
* ```
* unsigned __int128 ui128;
* ```
*/ */
class Int128Type extends IntegralType { class Int128Type extends IntegralType {
Int128Type() { Int128Type() {
@@ -598,10 +693,18 @@ class Int128Type extends IntegralType {
builtintypes(underlyingElement(this), _, 36, _, _, _) or builtintypes(underlyingElement(this), _, 36, _, _, _) or
builtintypes(underlyingElement(this), _, 37, _, _, _) builtintypes(underlyingElement(this), _, 37, _, _, _)
} }
override string getCanonicalQLClass() { result = "Int128Type" }
} }
/** /**
* The C/C++ floating point types. See 4.5. * The C/C++ floating point types. See 4.5. This includes `float`,
* `double` and `long double` types.
* ```
* float f;
* double d;
* long double ld;
* ```
*/ */
class FloatingPointType extends ArithmeticType { class FloatingPointType extends ArithmeticType {
FloatingPointType() { FloatingPointType() {
@@ -610,14 +713,19 @@ class FloatingPointType extends ArithmeticType {
( (
kind >= 24 and kind <= 32 kind >= 24 and kind <= 32
or or
kind = 38 kind >= 38 and kind <= 42
or
kind >= 45 and kind <= 50
) )
) )
} }
} }
/** /**
* The C/C++ float type. * The C/C++ `float` type.
* ```
* float f;
* ```
*/ */
class FloatType extends FloatingPointType { class FloatType extends FloatingPointType {
FloatType() { builtintypes(underlyingElement(this), _, 24, _, _, _) } FloatType() { builtintypes(underlyingElement(this), _, 24, _, _, _) }
@@ -626,7 +734,10 @@ class FloatType extends FloatingPointType {
} }
/** /**
* The C/C++ double type. * The C/C++ `double` type.
* ```
* double d;
* ```
*/ */
class DoubleType extends FloatingPointType { class DoubleType extends FloatingPointType {
DoubleType() { builtintypes(underlyingElement(this), _, 25, _, _, _) } DoubleType() { builtintypes(underlyingElement(this), _, 25, _, _, _) }
@@ -635,7 +746,10 @@ class DoubleType extends FloatingPointType {
} }
/** /**
* The C/C++ long double type. * The C/C++ `long double` type.
* ```
* long double ld;
* ```
*/ */
class LongDoubleType extends FloatingPointType { class LongDoubleType extends FloatingPointType {
LongDoubleType() { builtintypes(underlyingElement(this), _, 26, _, _, _) } LongDoubleType() { builtintypes(underlyingElement(this), _, 26, _, _, _) }
@@ -644,35 +758,58 @@ class LongDoubleType extends FloatingPointType {
} }
/** /**
* The GNU C __float128 type. * The GNU C `__float128` primitive type. This is not standard C/C++.
* ```
* __float128 f128;
* ```
*/ */
class Float128Type extends FloatingPointType { class Float128Type extends FloatingPointType {
Float128Type() { builtintypes(underlyingElement(this), _, 38, _, _, _) } Float128Type() { builtintypes(underlyingElement(this), _, 38, _, _, _) }
override string getCanonicalQLClass() { result = "Float128Type" }
} }
/** /**
* The GNU C _Decimal32 type. * The GNU C `_Decimal32` primitive type. This is not standard C/C++.
* ```
* _Decimal32 d32;
* ```
*/ */
class Decimal32Type extends FloatingPointType { class Decimal32Type extends FloatingPointType {
Decimal32Type() { builtintypes(underlyingElement(this), _, 40, _, _, _) } Decimal32Type() { builtintypes(underlyingElement(this), _, 40, _, _, _) }
override string getCanonicalQLClass() { result = "Decimal32Type" }
} }
/** /**
* The GNU C _Decimal64 type. * The GNU C `_Decimal64` primitive type. This is not standard C/C++.
* ```
* _Decimal64 d64;
* ```
*/ */
class Decimal64Type extends FloatingPointType { class Decimal64Type extends FloatingPointType {
Decimal64Type() { builtintypes(underlyingElement(this), _, 41, _, _, _) } Decimal64Type() { builtintypes(underlyingElement(this), _, 41, _, _, _) }
override string getCanonicalQLClass() { result = "Decimal64Type" }
} }
/** /**
* The GNU C _Decimal128 type. * The GNU C `_Decimal128` primitive type. This is not standard C/C++.
* ```
* _Decimal128 d128;
* ```
*/ */
class Decimal128Type extends FloatingPointType { class Decimal128Type extends FloatingPointType {
Decimal128Type() { builtintypes(underlyingElement(this), _, 42, _, _, _) } Decimal128Type() { builtintypes(underlyingElement(this), _, 42, _, _, _) }
override string getCanonicalQLClass() { result = "Decimal128Type" }
} }
/** /**
* The C/C++ void type. See 4.7. * The C/C++ `void` type. See 4.7.
* ```
* void foo();
* ```
*/ */
class VoidType extends BuiltInType { class VoidType extends BuiltInType {
VoidType() { builtintypes(underlyingElement(this), _, 3, _, _, _) } VoidType() { builtintypes(underlyingElement(this), _, 3, _, _, _) }
@@ -686,6 +823,9 @@ class VoidType extends BuiltInType {
* Note that on some platforms `wchar_t` doesn't exist as a built-in * Note that on some platforms `wchar_t` doesn't exist as a built-in
* type but a typedef is provided. Consider using the `Wchar_t` QL * type but a typedef is provided. Consider using the `Wchar_t` QL
* class to include these types. * class to include these types.
* ```
* wchar_t wc;
* ```
*/ */
class WideCharType extends IntegralType { class WideCharType extends IntegralType {
WideCharType() { builtintypes(underlyingElement(this), _, 33, _, _, _) } WideCharType() { builtintypes(underlyingElement(this), _, 33, _, _, _) }
@@ -694,7 +834,10 @@ class WideCharType extends IntegralType {
} }
/** /**
* The C/C++ `char16_t` type. * The C/C++ `char16_t` type. This is available starting with C11 and C++11.
* ```
* char16_t c16;
* ```
*/ */
class Char16Type extends IntegralType { class Char16Type extends IntegralType {
Char16Type() { builtintypes(underlyingElement(this), _, 43, _, _, _) } Char16Type() { builtintypes(underlyingElement(this), _, 43, _, _, _) }
@@ -703,7 +846,10 @@ class Char16Type extends IntegralType {
} }
/** /**
* The C/C++ `char32_t` type. * The C/C++ `char32_t` type. This is available starting with C11 and C++11.
* ```
* char32_t c32;
* ```
*/ */
class Char32Type extends IntegralType { class Char32Type extends IntegralType {
Char32Type() { builtintypes(underlyingElement(this), _, 44, _, _, _) } Char32Type() { builtintypes(underlyingElement(this), _, 44, _, _, _) }
@@ -712,13 +858,13 @@ class Char32Type extends IntegralType {
} }
/** /**
* The type of the C++11 nullptr constant. * The (primitive) type of the C++11 `nullptr` constant. It is a
* * distinct type, denoted by `decltype(nullptr)`, that is not itself a pointer
* Note that this is not `nullptr_t`, as `nullptr_t` is defined as: * type or a pointer to member type. The `<cstddef>` header usually defines
* the `std::nullptr_t` type as follows:
* ``` * ```
* typedef decltype(nullptr) nullptr_t; * typedef decltype(nullptr) nullptr_t;
* ``` * ```
* Instead, this is the unspeakable type given by `decltype(nullptr)`.
*/ */
class NullPointerType extends BuiltInType { class NullPointerType extends BuiltInType {
NullPointerType() { builtintypes(underlyingElement(this), _, 34, _, _, _) } NullPointerType() { builtintypes(underlyingElement(this), _, 34, _, _, _) }
@@ -729,8 +875,13 @@ class NullPointerType extends BuiltInType {
/** /**
* A C/C++ derived type. * A C/C++ derived type.
* *
* These are pointer and reference types, array and vector types, and const and volatile types. * These are pointer and reference types, array and GNU vector types, and `const` and `volatile` types.
* In all cases, the type is formed from a single base type. * In all cases, the type is formed from a single base type. For example:
* ```
* int *pi;
* int &ri = *pi;
* const float fa[40];
* ```
*/ */
class DerivedType extends Type, @derivedtype { class DerivedType extends Type, @derivedtype {
override string toString() { result = this.getName() } override string toString() { result = this.getName() }
@@ -775,9 +926,15 @@ class DerivedType extends Type, @derivedtype {
} }
/** /**
* An instance of the C++11 decltype operator. * An instance of the C++11 `decltype` operator. For example:
* ```
* int a;
* decltype(a) b;
* ```
*/ */
class Decltype extends Type, @decltype { class Decltype extends Type, @decltype {
override string getCanonicalQLClass() { result = "Decltype" }
/** /**
* The expression whose type is being obtained by this decltype. * The expression whose type is being obtained by this decltype.
*/ */
@@ -788,17 +945,17 @@ class Decltype extends Type, @decltype {
*/ */
Type getBaseType() { decltypes(underlyingElement(this), _, unresolveElement(result), _) } Type getBaseType() { decltypes(underlyingElement(this), _, unresolveElement(result), _) }
override string getCanonicalQLClass() { result = "Decltype" }
/** /**
* Whether an extra pair of parentheses around the expression would change the semantics of this decltype. * Whether an extra pair of parentheses around the expression would change the semantics of this decltype.
* *
* The following example shows the effect of an extra pair of parentheses: * The following example shows the effect of an extra pair of parentheses:
* struct A { double x; }; * ```
* const A* a = new A(); * struct A { double x; };
* decltype( a->x ); // type is double * const A* a = new A();
* decltype((a->x)); // type is const double&amp; * decltype( a->x ); // type is double
* Consult the C++11 standard for more details. * decltype((a->x)); // type is const double&
* ```
* Please consult the C++11 standard for more details.
*/ */
predicate parenthesesWouldChangeMeaning() { decltypes(underlyingElement(this), _, _, true) } predicate parenthesesWouldChangeMeaning() { decltypes(underlyingElement(this), _, _, true) }
@@ -841,6 +998,10 @@ class Decltype extends Type, @decltype {
/** /**
* A C/C++ pointer type. See 4.9.1. * A C/C++ pointer type. See 4.9.1.
* ```
* void *ptr;
* void **ptr2 = &ptr;
* ```
*/ */
class PointerType extends DerivedType { class PointerType extends DerivedType {
PointerType() { derivedtypes(underlyingElement(this), _, 1, _) } PointerType() { derivedtypes(underlyingElement(this), _, 1, _) }
@@ -863,8 +1024,8 @@ class PointerType extends DerivedType {
/** /**
* A C++ reference type. See 4.9.1. * A C++ reference type. See 4.9.1.
* *
* For C++11 code bases, this includes both lvalue references (&amp;) and rvalue references (&amp;&amp;). * For C++11 code bases, this includes both _lvalue_ references (`&`) and _rvalue_ references (`&&`).
* To distinguish between them, use the LValueReferenceType and RValueReferenceType classes. * To distinguish between them, use the LValueReferenceType and RValueReferenceType QL classes.
*/ */
class ReferenceType extends DerivedType { class ReferenceType extends DerivedType {
ReferenceType() { ReferenceType() {
@@ -889,7 +1050,11 @@ class ReferenceType extends DerivedType {
} }
/** /**
* A C++11 lvalue reference type (e.g. int&amp;). * A C++11 lvalue reference type (e.g. `int &`).
* ```
* int a;
* int& b = a;
* ```
*/ */
class LValueReferenceType extends ReferenceType { class LValueReferenceType extends ReferenceType {
LValueReferenceType() { derivedtypes(underlyingElement(this), _, 2, _) } LValueReferenceType() { derivedtypes(underlyingElement(this), _, 2, _) }
@@ -898,7 +1063,14 @@ class LValueReferenceType extends ReferenceType {
} }
/** /**
* A C++11 rvalue reference type (e.g. int&amp;&amp;). * A C++11 rvalue reference type (e.g., `int &&`). It is used to
* implement "move" semantics for object construction and assignment.
* ```
* class C {
* E e;
* C(C&& from): e(std::move(from.e)) { }
* };
* ```
*/ */
class RValueReferenceType extends ReferenceType { class RValueReferenceType extends ReferenceType {
RValueReferenceType() { derivedtypes(underlyingElement(this), _, 8, _) } RValueReferenceType() { derivedtypes(underlyingElement(this), _, 8, _) }
@@ -910,6 +1082,10 @@ class RValueReferenceType extends ReferenceType {
/** /**
* A type with specifiers. * A type with specifiers.
* ```
* const int a;
* volatile char v;
* ```
*/ */
class SpecifiedType extends DerivedType { class SpecifiedType extends DerivedType {
SpecifiedType() { derivedtypes(underlyingElement(this), _, 3, _) } SpecifiedType() { derivedtypes(underlyingElement(this), _, 3, _) }
@@ -955,6 +1131,9 @@ class SpecifiedType extends DerivedType {
/** /**
* A C/C++ array type. See 4.9.1. * A C/C++ array type. See 4.9.1.
* ```
* char table[32];
* ```
*/ */
class ArrayType extends DerivedType { class ArrayType extends DerivedType {
ArrayType() { derivedtypes(underlyingElement(this), _, 4, _) } ArrayType() { derivedtypes(underlyingElement(this), _, 4, _) }
@@ -1001,10 +1180,16 @@ class ArrayType extends DerivedType {
* A GNU/Clang vector type. * A GNU/Clang vector type.
* *
* In both Clang and GNU compilers, vector types can be introduced using the * In both Clang and GNU compilers, vector types can be introduced using the
* __attribute__((vector_size(byte_size))) syntax. The Clang compiler also * `__attribute__((vector_size(byte_size)))` syntax. The Clang compiler also
* allows vector types to be introduced using the ext_vector_type, * allows vector types to be introduced using the `ext_vector_type`,
* neon_vector_type, and neon_polyvector_type attributes (all of which take * `neon_vector_type`, and `neon_polyvector_type` attributes (all of which take
* an element type rather than a byte size). * an element count rather than a byte size).
*
* In the example below, both `v4si` and `float4` are GNU vector types:
* ```
* typedef int v4si __attribute__ (( vector_size(4*sizeof(int)) ));
* typedef float float4 __attribute__((ext_vector_type(4)));
* ```
*/ */
class GNUVectorType extends DerivedType { class GNUVectorType extends DerivedType {
GNUVectorType() { derivedtypes(underlyingElement(this), _, 5, _) } GNUVectorType() { derivedtypes(underlyingElement(this), _, 5, _) }
@@ -1043,7 +1228,10 @@ class GNUVectorType extends DerivedType {
} }
/** /**
* A C/C++ pointer to function. See 7.7. * A C/C++ pointer to a function. See 7.7.
* ```
* int(* pointer)(const void *element1, const void *element2);
* ```
*/ */
class FunctionPointerType extends FunctionPointerIshType { class FunctionPointerType extends FunctionPointerIshType {
FunctionPointerType() { derivedtypes(underlyingElement(this), _, 6, _) } FunctionPointerType() { derivedtypes(underlyingElement(this), _, 6, _) }
@@ -1058,7 +1246,10 @@ class FunctionPointerType extends FunctionPointerIshType {
} }
/** /**
* A C/C++ reference to function. * A C++ reference to a function.
* ```
* int(& reference)(const void *element1, const void *element2);
* ```
*/ */
class FunctionReferenceType extends FunctionPointerIshType { class FunctionReferenceType extends FunctionPointerIshType {
FunctionReferenceType() { derivedtypes(underlyingElement(this), _, 7, _) } FunctionReferenceType() { derivedtypes(underlyingElement(this), _, 7, _) }
@@ -1073,10 +1264,14 @@ class FunctionReferenceType extends FunctionPointerIshType {
} }
/** /**
* A block type, for example int(^)(char, float). * A block type, for example, `int(^)(char, float)`.
* *
* Block types (along with blocks themselves) are a language extension * Block types (along with blocks themselves) are a language extension
* supported by Clang, and by Apple's branch of GCC. * supported by Clang, and by Apple's branch of GCC.
* ```
* int(^ block)(const char *element1, const char *element2)
* = ^int (const char *element1, const char *element2) { return element1 - element 2; }
* ```
*/ */
class BlockType extends FunctionPointerIshType { class BlockType extends FunctionPointerIshType {
BlockType() { derivedtypes(underlyingElement(this), _, 10, _) } BlockType() { derivedtypes(underlyingElement(this), _, 10, _) }
@@ -1089,7 +1284,9 @@ class BlockType extends FunctionPointerIshType {
} }
/** /**
* A C/C++ pointer to function, or a block. * A C/C++ pointer to a function, a C++ function reference, or a clang/Apple block.
*
* See `FunctionPointerType`, `FunctionReferenceType` and `BlockType` for more information.
*/ */
class FunctionPointerIshType extends DerivedType { class FunctionPointerIshType extends DerivedType {
FunctionPointerIshType() { FunctionPointerIshType() {
@@ -1134,7 +1331,13 @@ class FunctionPointerIshType extends DerivedType {
} }
/** /**
* A C++ pointer to member. See 15.5. * A C++ pointer to data member. See 15.5.
* ```
* class C { int m; };
* int C::* p = &C::m; // pointer to data member m of class C
* class C *;
* int val = c.*p; // access data member
* ```
*/ */
class PointerToMemberType extends Type, @ptrtomember { class PointerToMemberType extends Type, @ptrtomember {
/** a printable representation of this named element */ /** a printable representation of this named element */
@@ -1171,7 +1374,14 @@ class PointerToMemberType extends Type, @ptrtomember {
} }
/** /**
* A C/C++ routine type. This is what results from stripping away the pointer from a function pointer type. * A C/C++ routine type. Conceptually, this is what results from stripping
* away the pointer from a function pointer type. It can also occur in C++
* code, for example the base type of `myRoutineType` in the following code:
* ```
* using myRoutineType = int(int);
*
* myRoutineType *fp = 0;
* ```
*/ */
class RoutineType extends Type, @routinetype { class RoutineType extends Type, @routinetype {
/** a printable representation of this named element */ /** a printable representation of this named element */
@@ -1231,7 +1441,13 @@ class RoutineType extends Type, @routinetype {
} }
/** /**
* A C++ typename template parameter. * A C++ `typename` (or `class`) template parameter.
*
* In the example below, `T` is a template parameter:
* ```
* template <class T>
* class C { };
* ```
*/ */
class TemplateParameter extends UserType { class TemplateParameter extends UserType {
TemplateParameter() { TemplateParameter() {
@@ -1243,7 +1459,16 @@ class TemplateParameter extends UserType {
override predicate involvesTemplateParameter() { any() } override predicate involvesTemplateParameter() { any() }
} }
/** A C++ template template parameter, e.g. template &lt;template &lt;typename,typename> class T>. */ /**
* A C++ template template parameter.
*
* In the example below, `T` is a template template parameter (although its name
* may be omitted):
* ```
* template <template <typename T> class Container, class Elem>
* void foo(const Container<Elem> &value) { }
* ```
*/
class TemplateTemplateParameter extends TemplateParameter { class TemplateTemplateParameter extends TemplateParameter {
TemplateTemplateParameter() { usertypes(underlyingElement(this), _, 8) } TemplateTemplateParameter() { usertypes(underlyingElement(this), _, 8) }
@@ -1251,7 +1476,10 @@ class TemplateTemplateParameter extends TemplateParameter {
} }
/** /**
* A type representing the use of the C++11 auto keyword. * A type representing the use of the C++11 `auto` keyword.
* ```
* auto val = some_typed_expr();
* ```
*/ */
class AutoType extends TemplateParameter { class AutoType extends TemplateParameter {
AutoType() { usertypes(underlyingElement(this), "auto", 7) } AutoType() { usertypes(underlyingElement(this), "auto", 7) }

View File

@@ -2,12 +2,17 @@ import semmle.code.cpp.Type
private import semmle.code.cpp.internal.ResolveClass private import semmle.code.cpp.internal.ResolveClass
/** /**
* A C/C++ typedef type. See 4.9.1. * A C/C++ typedef type. See 4.9.1. For example the types declared on each line of the following code:
* ```
* typedef int my_int;
* using my_int2 = int;
* ```
*/ */
class TypedefType extends UserType { class TypedefType extends UserType {
TypedefType() { usertypes(underlyingElement(this), _, 5) } TypedefType() {
usertypes(underlyingElement(this), _, 5) or
override string getCanonicalQLClass() { result = "TypedefType" } usertypes(underlyingElement(this), _, 14)
}
/** /**
* Gets the base type of this typedef type. * Gets the base type of this typedef type.
@@ -26,10 +31,6 @@ class TypedefType extends UserType {
result = this.getBaseType().getPointerIndirectionLevel() result = this.getBaseType().getPointerIndirectionLevel()
} }
override string explain() {
result = "typedef {" + this.getBaseType().explain() + "} as \"" + this.getName() + "\""
}
override predicate isDeeplyConst() { this.getBaseType().isDeeplyConst() } // Just an alias override predicate isDeeplyConst() { this.getBaseType().isDeeplyConst() } // Just an alias
override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConstBelow() } // Just an alias override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConstBelow() } // Just an alias
@@ -46,7 +47,43 @@ class TypedefType extends UserType {
} }
/** /**
* A C++ typedef type that is directly enclosed by a function. * A traditional C/C++ typedef type. See 4.9.1. For example the type declared in the following code:
* ```
* typedef int my_int;
* ```
*/
class CTypedefType extends TypedefType {
CTypedefType() { usertypes(underlyingElement(this), _, 5) }
override string getCanonicalQLClass() { result = "CTypedefType" }
override string explain() {
result = "typedef {" + this.getBaseType().explain() + "} as \"" + this.getName() + "\""
}
}
/**
* A using alias C++ typedef type. For example the type declared in the following code:
* ```
* using my_int2 = int;
* ```
*/
class UsingAliasTypedefType extends TypedefType {
UsingAliasTypedefType() { usertypes(underlyingElement(this), _, 14) }
override string getCanonicalQLClass() { result = "UsingAliasTypedefType" }
override string explain() {
result = "using {" + this.getBaseType().explain() + "} as \"" + this.getName() + "\""
}
}
/**
* A C++ `typedef` type that is directly enclosed by a function. For example the type declared inside the function `foo` in
* the following code:
* ```
* int foo(void) { typedef int local; }
* ```
*/ */
class LocalTypedefType extends TypedefType { class LocalTypedefType extends TypedefType {
LocalTypedefType() { isLocal() } LocalTypedefType() { isLocal() }
@@ -55,7 +92,11 @@ class LocalTypedefType extends TypedefType {
} }
/** /**
* A C++ typedef type that is directly enclosed by a class, struct or union. * A C++ `typedef` type that is directly enclosed by a `class`, `struct` or `union`. For example the type declared inside
* the class `C` in the following code:
* ```
* class C { typedef int nested; };
* ```
*/ */
class NestedTypedefType extends TypedefType { class NestedTypedefType extends TypedefType {
NestedTypedefType() { this.isMember() } NestedTypedefType() { this.isMember() }

View File

@@ -5,8 +5,14 @@ import semmle.code.cpp.Function
private import semmle.code.cpp.internal.ResolveClass private import semmle.code.cpp.internal.ResolveClass
/** /**
* A C/C++ user-defined type. Examples include `Class`, `Struct`, `Union`, * A C/C++ user-defined type. Examples include `class`, `struct`, `union`,
* `Enum`, and `TypedefType`. * `enum` and `typedef` types.
* ```
* enum e1 { val1, val2 } b;
* enum class e2: short { val3, val4 } c;
* typedef int my_int;
* class C { int a, b; };
* ```
*/ */
class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @usertype { class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @usertype {
/** /**
@@ -88,6 +94,10 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @
/** /**
* A particular definition or forward declaration of a C/C++ user-defined type. * A particular definition or forward declaration of a C/C++ user-defined type.
* ```
* class C;
* typedef int ti;
* ```
*/ */
class TypeDeclarationEntry extends DeclarationEntry, @type_decl { class TypeDeclarationEntry extends DeclarationEntry, @type_decl {
override UserType getDeclaration() { result = getType() } override UserType getDeclaration() { result = getType() }

View File

@@ -155,15 +155,6 @@ class Variable extends Declaration, @variable {
variable_instantiation(underlyingElement(this), unresolveElement(v)) 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 * Holds if this is a compiler-generated variable. For example, a
* [range-based for loop](http://en.cppreference.com/w/cpp/language/range-for) * [range-based for loop](http://en.cppreference.com/w/cpp/language/range-for)
@@ -315,7 +306,7 @@ class ParameterDeclarationEntry extends VariableDeclarationEntry {
* static int c; * static int c;
* } * }
* ``` * ```
* *
* Local variables can be static; use the `isStatic` member predicate to * Local variables can be static; use the `isStatic` member predicate to
* detect those. * detect those.
*/ */
@@ -343,7 +334,7 @@ deprecated class StackVariable extends Variable {
* static int c; * static int c;
* } * }
* ``` * ```
* *
* Local variables can be static; use the `isStatic` member predicate to detect * Local variables can be static; use the `isStatic` member predicate to detect
* those. * those.
* *
@@ -512,9 +503,9 @@ class TemplateVariable extends Variable {
* void myTemplateFunction() { * void myTemplateFunction() {
* T b; * T b;
* } * }
* *
* ... * ...
* *
* myTemplateFunction<int>(); * myTemplateFunction<int>();
* ``` * ```
*/ */

View File

@@ -5,13 +5,17 @@ import cpp
*/ */
predicate allocationFunction(Function f) { predicate allocationFunction(Function f) {
exists(string name | exists(string name |
f.hasGlobalName(name) and f.hasGlobalOrStdName(name) and
( (
name = "malloc" or name = "malloc" or
name = "calloc" or name = "calloc" or
name = "realloc" or name = "realloc" or
name = "strdup" or name = "strdup" or
name = "wcsdup" or name = "wcsdup"
)
or
f.hasGlobalName(name) and
(
name = "_strdup" or name = "_strdup" or
name = "_wcsdup" or name = "_wcsdup" or
name = "_mbsdup" or name = "_mbsdup" or
@@ -59,7 +63,7 @@ predicate allocationCall(FunctionCall fc) {
allocationFunction(fc.getTarget()) and allocationFunction(fc.getTarget()) and
( (
// realloc(ptr, 0) only frees the pointer // 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 name = "free" and argNum = 0
or or
name = "realloc" and argNum = 0 name = "realloc" and argNum = 0
or )
or
f.hasGlobalOrStdName(name) and
(
name = "ExFreePoolWithTag" and argNum = 0 name = "ExFreePoolWithTag" and argNum = 0
or or
name = "ExFreeToLookasideListEx" and argNum = 1 name = "ExFreeToLookasideListEx" and argNum = 1

View File

@@ -44,7 +44,7 @@ predicate memberMayBeVarSize(Class c, MemberVariable v) {
aoe.getAddressable() = v aoe.getAddressable() = v
) )
or or
exists(BuiltInOperationOffsetOf oo | exists(BuiltInOperationBuiltInOffsetOf oo |
// `offsetof(c, v)` using a builtin // `offsetof(c, v)` using a builtin
oo.getAChild().(VariableAccess).getTarget() = v oo.getAChild().(VariableAccess).getTarget() = v
) )

View File

@@ -174,9 +174,7 @@ class MicrosoftInt64Type extends IntegralType {
* `__builtin_va_copy` and `__builtin_va_arg` expressions. * `__builtin_va_copy` and `__builtin_va_arg` expressions.
*/ */
class BuiltInVarArgsList extends Type { class BuiltInVarArgsList extends Type {
BuiltInVarArgsList() { BuiltInVarArgsList() { this.hasName("__builtin_va_list") }
this.hasName("__builtin_va_list")
}
override string getCanonicalQLClass() { result = "BuiltInVarArgsList" } override string getCanonicalQLClass() { result = "BuiltInVarArgsList" }
} }

View File

@@ -28,7 +28,7 @@ class EnvironmentRead extends Expr {
private predicate readsEnvironment(Expr read, string sourceDescription) { private predicate readsEnvironment(Expr read, string sourceDescription) {
exists(FunctionCall call, string name | exists(FunctionCall call, string name |
read = call and read = call and
call.getTarget().hasGlobalName(name) and call.getTarget().hasGlobalOrStdName(name) and
(name = "getenv" or name = "secure_getenv" or name = "_wgetenv") and (name = "getenv" or name = "secure_getenv" or name = "_wgetenv") and
sourceDescription = name sourceDescription = name
) )

View File

@@ -79,3 +79,26 @@ predicate functionContainsPreprocCode(Function f) {
pbdStartLine >= fBlockStartLine pbdStartLine >= fBlockStartLine
) )
} }
/**
* Holds if `e` is completely or partially from a macro definition, as opposed
* to being passed in as an argument.
*
* In the following example, the call to `f` is from a macro definition,
* while `y`, `+`, `1`, and `;` are not. This assumes that no identifier apart
* from `M` refers to a macro.
* ```
* #define M(x) f(x)
* ...
* M(y + 1);
* ```
*/
predicate isFromMacroDefinition(Element e) {
exists(MacroInvocation mi |
// e is in mi
mi.getAnExpandedElement() = e and
// and e was apparently not passed in as a macro parameter
e.getLocation().getStartLine() = mi.getLocation().getStartLine() and
e.getLocation().getStartColumn() = mi.getLocation().getStartColumn()
)
}

View File

@@ -5,7 +5,7 @@ import cpp
*/ */
predicate fopenCall(FunctionCall fc) { predicate fopenCall(FunctionCall fc) {
exists(Function f | f = fc.getTarget() | exists(Function f | f = fc.getTarget() |
f.hasGlobalName("fopen") or f.hasGlobalOrStdName("fopen") or
f.hasGlobalName("open") or f.hasGlobalName("open") or
f.hasGlobalName("_open") or f.hasGlobalName("_open") or
f.hasGlobalName("_wopen") or f.hasGlobalName("_wopen") or
@@ -23,7 +23,7 @@ predicate fopenCall(FunctionCall fc) {
*/ */
predicate fcloseCall(FunctionCall fc, Expr closed) { predicate fcloseCall(FunctionCall fc, Expr closed) {
exists(Function f | f = fc.getTarget() | exists(Function f | f = fc.getTarget() |
f.hasGlobalName("fclose") and f.hasGlobalOrStdName("fclose") and
closed = fc.getArgument(0) closed = fc.getArgument(0)
or or
f.hasGlobalName("close") and f.hasGlobalName("close") and
@@ -32,7 +32,7 @@ predicate fcloseCall(FunctionCall fc, Expr closed) {
f.hasGlobalName("_close") and f.hasGlobalName("_close") and
closed = fc.getArgument(0) closed = fc.getArgument(0)
or or
f.hasGlobalName("CloseHandle") and f.hasGlobalOrStdName("CloseHandle") and
closed = fc.getArgument(0) closed = fc.getArgument(0)
) )
} }

View File

@@ -8,25 +8,32 @@ import semmle.code.cpp.commons.StringAnalysis
import semmle.code.cpp.models.interfaces.FormattingFunction import semmle.code.cpp.models.interfaces.FormattingFunction
import semmle.code.cpp.models.implementations.Printf 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 * A function that can be identified as a `printf` style formatting
* function by its use of the GNU `format` attribute. * function by its use of the GNU `format` attribute.
*/ */
class AttributeFormattingFunction extends FormattingFunction { class AttributeFormattingFunction extends FormattingFunction {
FormatAttribute printf_attrib;
override string getCanonicalQLClass() { result = "AttributeFormattingFunction" } override string getCanonicalQLClass() { result = "AttributeFormattingFunction" }
AttributeFormattingFunction() { AttributeFormattingFunction() {
printf_attrib = getAnAttribute() and exists(PrintfFormatAttribute printf_attrib |
( printf_attrib = getAnAttribute() and
printf_attrib.getArchetype() = "printf" or exists(printf_attrib.getFirstFormatArgIndex()) // exclude `vprintf` style format functions
printf_attrib.getArchetype() = "__printf__" )
) 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) { Expr getConversionArgument(int n) {
exists(FormatLiteral fl, int b, int o | exists(FormatLiteral fl |
fl = this.getFormat() and fl = this.getFormat() and
b = sum(int i, int toSum | i < n and toSum = fl.getNumArgNeeded(i) | toSum) and (
o = fl.getNumArgNeeded(n) and result = this.getFormatArgument(fl.getParameterFieldValue(n))
o > 0 and or
result = this.getFormatArgument(b + o - 1) 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). * an explicit minimum field width).
*/ */
Expr getMinFieldWidthArgument(int n) { Expr getMinFieldWidthArgument(int n) {
exists(FormatLiteral fl, int b | exists(FormatLiteral fl |
fl = this.getFormat() and 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(fl.getMinFieldWidthParameterFieldValue(n))
result = this.getFormatArgument(b) or
result = this.getFormatArgument(fl.getFormatArgumentIndexFor(n, 0)) and
not exists(fl.getMinFieldWidthParameterFieldValue(n))
)
) )
} }
@@ -156,12 +168,14 @@ class FormattingFunctionCall extends Expr {
* precision). * precision).
*/ */
Expr getPrecisionArgument(int n) { Expr getPrecisionArgument(int n) {
exists(FormatLiteral fl, int b, int o | exists(FormatLiteral fl |
fl = this.getFormat() and 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 result = this.getFormatArgument(fl.getPrecisionParameterFieldValue(n))
fl.hasImplicitPrecision(n) and or
result = this.getFormatArgument(b + o) result = this.getFormatArgument(fl.getFormatArgumentIndexFor(n, 1)) and
not exists(fl.getPrecisionParameterFieldValue(n))
)
) )
} }
@@ -169,7 +183,11 @@ class FormattingFunctionCall extends Expr {
* Gets the number of arguments to this call that are parameters to the * Gets the number of arguments to this call that are parameters to the
* format string. * format string.
*/ */
int getNumFormatArgument() { result = count(this.getFormatArgument(_)) } int getNumFormatArgument() {
result = count(this.getFormatArgument(_)) and
// format arguments must be known
exists(getTarget().(FormattingFunction).getFirstFormatArgumentIndex())
}
} }
/** /**
@@ -357,6 +375,14 @@ class FormatLiteral extends Literal {
*/ */
string getParameterField(int n) { this.parseConvSpec(n, _, result, _, _, _, _, _) } 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. * Gets the flags of the nth conversion specifier.
*/ */
@@ -426,6 +452,14 @@ class FormatLiteral extends Literal {
*/ */
int getMinFieldWidth(int n) { result = this.getMinFieldWidthOpt(n).toInt() } 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). * Gets the precision of the nth conversion specifier (empty string if none is given).
*/ */
@@ -456,6 +490,14 @@ class FormatLiteral extends Literal {
else result = this.getPrecisionOpt(n).regexpCapture("\\.([0-9]*)", 1).toInt() 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. * Gets the length flag of the nth conversion specifier.
*/ */
@@ -773,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 * Gets the number of arguments required by the nth conversion specifier
* of this format string. * of this format string.
*/ */
int getNumArgNeeded(int n) { int getNumArgNeeded(int n) {
exists(this.getConvSpecOffset(n)) and exists(this.getConvSpecOffset(n)) and
not this.getConversionChar(n) = "%" and result = count(int mode | hasFormatArgumentIndexFor(n, mode))
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
)
} }
/** /**
@@ -797,7 +869,7 @@ class FormatLiteral extends Literal {
// At least one conversion specifier has a parameter field, in which case, // At least one conversion specifier has a parameter field, in which case,
// they all should have. // they all should have.
result = max(string s | this.getParameterField(_) = s + "$" | s.toInt()) 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))
} }
/** /**

View File

@@ -53,8 +53,8 @@ class AnalysedString extends Expr {
*/ */
class StrlenCall extends FunctionCall { class StrlenCall extends FunctionCall {
StrlenCall() { StrlenCall() {
this.getTarget().hasGlobalName("strlen") or this.getTarget().hasGlobalOrStdName("strlen") or
this.getTarget().hasGlobalName("wcslen") or this.getTarget().hasGlobalOrStdName("wcslen") or
this.getTarget().hasGlobalName("_mbslen") or this.getTarget().hasGlobalName("_mbslen") or
this.getTarget().hasGlobalName("_mbslen_l") or this.getTarget().hasGlobalName("_mbslen_l") or
this.getTarget().hasGlobalName("_mbstrlen") or this.getTarget().hasGlobalName("_mbstrlen") or

View File

@@ -35,7 +35,8 @@ private predicate exprInVoidContext(Expr e) {
exists(CommaExpr c | c.getLeftOperand() = e) exists(CommaExpr c | c.getLeftOperand() = e)
or or
exists(CommaExpr c | c.getRightOperand() = e and c instanceof ExprInVoidContext) exists(CommaExpr c | c.getRightOperand() = e and c instanceof ExprInVoidContext)
or
exists(ForStmt for | for.getUpdate() = e)
) and ) and
not e instanceof Qualifier and
not e.getActualType() instanceof VoidType not e.getActualType() instanceof VoidType
} }

View File

@@ -1,6 +1,7 @@
import cpp import cpp
import BasicBlocks import BasicBlocks
private import semmle.code.cpp.controlflow.internal.ConstantExprs private import semmle.code.cpp.controlflow.internal.ConstantExprs
private import semmle.code.cpp.controlflow.internal.CFG
/** /**
* A control-flow node is either a statement or an expression; in addition, * A control-flow node is either a statement or an expression; in addition,
@@ -86,11 +87,11 @@ import ControlFlowGraphPublic
class ControlFlowNodeBase extends ElementBase, @cfgnode { } class ControlFlowNodeBase extends ElementBase, @cfgnode { }
predicate truecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) { predicate truecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
truecond(unresolveElement(n1), unresolveElement(n2)) qlCFGTrueSuccessor(n1, n2)
} }
predicate falsecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) { predicate falsecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
falsecond(unresolveElement(n1), unresolveElement(n2)) qlCFGFalseSuccessor(n1, n2)
} }
/** /**
@@ -120,7 +121,7 @@ abstract class AdditionalControlFlowEdge extends ControlFlowNodeBase {
* `AdditionalControlFlowEdge`. Use this relation instead of `successors`. * `AdditionalControlFlowEdge`. Use this relation instead of `successors`.
*/ */
predicate successors_extended(ControlFlowNodeBase source, ControlFlowNodeBase target) { predicate successors_extended(ControlFlowNodeBase source, ControlFlowNodeBase target) {
successors(unresolveElement(source), unresolveElement(target)) qlCFGSuccessor(source, target)
or or
source.(AdditionalControlFlowEdge).getAnEdgeTarget() = target source.(AdditionalControlFlowEdge).getAnEdgeTarget() = target
} }

View File

@@ -6,7 +6,7 @@ import Nullness
*/ */
predicate callDereferences(FunctionCall fc, int i) { predicate callDereferences(FunctionCall fc, int i) {
exists(string name | exists(string name |
fc.getTarget().hasGlobalName(name) and fc.getTarget().hasGlobalOrStdName(name) and
( (
name = "bcopy" and i in [0 .. 1] name = "bcopy" and i in [0 .. 1]
or or

View File

@@ -156,7 +156,7 @@ private predicate compares_eq(
/** /**
* If `test => part` and `part => left == right + k` then `test => left == right + k`. * If `test => part` and `part => left == right + k` then `test => left == right + k`.
* Similarly for the case where `test` is false. * Similarly for the case where `test` is false.
*/ */
private predicate logical_comparison_eq( private predicate logical_comparison_eq(
BinaryLogicalOperation test, Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue BinaryLogicalOperation test, Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue
@@ -275,7 +275,7 @@ private predicate compares_ge(
/** /**
* If `test => part` and `part => left < right + k` then `test => left < right + k`. * If `test => part` and `part => left < right + k` then `test => left < right + k`.
* Similarly for the case where `test` evaluates false. * Similarly for the case where `test` evaluates false.
*/ */
private predicate logical_comparison_lt( private predicate logical_comparison_lt(
BinaryLogicalOperation test, Expr left, Expr right, int k, boolean isLt, boolean testIsTrue BinaryLogicalOperation test, Expr left, Expr right, int k, boolean isLt, boolean testIsTrue
@@ -362,7 +362,7 @@ private predicate add_lt(
) )
} }
/** The int value of integer constant expression. */ /** The `int` value of integer constant expression. */
private int int_value(Expr e) { private int int_value(Expr e) {
e.getUnderlyingType() instanceof IntegralType and e.getUnderlyingType() instanceof IntegralType and
result = e.getValue().toInt() result = e.getValue().toInt()

View File

@@ -264,9 +264,9 @@ predicate callMayReturnNull(Call call) {
* Holds if `f` may, directly or indirectly, return a null literal. * Holds if `f` may, directly or indirectly, return a null literal.
*/ */
predicate mayReturnNull(Function f) { predicate mayReturnNull(Function f) {
f.hasGlobalName("malloc") f.hasGlobalOrStdName("malloc")
or or
f.hasGlobalName("calloc") f.hasGlobalOrStdName("calloc")
or or
// f.hasGlobalName("strchr") // f.hasGlobalName("strchr")
// or // or

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