mirror of
https://github.com/github/codeql.git
synced 2026-07-21 03:08:25 +02:00
Merge remote-tracking branch 'upstream/master' into MissingEnumCaseInSwitch-perf
This commit is contained in:
@@ -2,5 +2,4 @@
|
||||
"*/ql/test/qlpack.yml",
|
||||
"*/upgrades/qlpack.yml",
|
||||
"misc/legacy-support/*/qlpack.yml",
|
||||
"misc/suite-helpers/qlpack.yml",
|
||||
"codeql/.codeqlmanifest.json" ] }
|
||||
"misc/suite-helpers/qlpack.yml" ] }
|
||||
|
||||
24
.github/labeler.yml
vendored
Normal file
24
.github/labeler.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
"C++":
|
||||
- cpp/**/*
|
||||
- change-notes/**/*cpp*
|
||||
|
||||
"C#":
|
||||
- csharp/**/*
|
||||
- change-notes/**/*csharp*
|
||||
|
||||
Java:
|
||||
- java/**/*
|
||||
- change-notes/**/*java.*
|
||||
|
||||
JS:
|
||||
- javascript/**/*
|
||||
- change-notes/**/*javascript*
|
||||
|
||||
Python:
|
||||
- python/**/*
|
||||
- change-notes/**/*python*
|
||||
|
||||
documentation:
|
||||
- "**/*.qhelp"
|
||||
- "**/*.md"
|
||||
- docs/**/*
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
# editor and OS artifacts
|
||||
*~
|
||||
.DS_STORE
|
||||
*.swp
|
||||
|
||||
# query compilation caches
|
||||
.cache
|
||||
@@ -15,3 +16,5 @@
|
||||
|
||||
# It's useful (though not required) to be able to unpack codeql in the ql checkout itself
|
||||
/codeql/
|
||||
.vscode/settings.json
|
||||
csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json
|
||||
|
||||
@@ -46,6 +46,12 @@ Follow the steps below to help other users understand what your query does, and
|
||||
Query help files explain the purpose of your query to other users. Write your query help in a `.qhelp` file and save it in the same directory as your new query.
|
||||
For more information on writing query help, see the [Query help style guide](https://github.com/Semmle/ql/blob/master/docs/query-help-style-guide.md).
|
||||
|
||||
7. **Maintain backwards compatibility**
|
||||
|
||||
The standard CodeQL libraries must evolve in a backwards compatible manner. If any backwards incompatible changes need to be made, the existing API must first be marked as deprecated. This is done by adding a `deprecated` annotation along with a QLDoc reference to the replacement API. Only after at least one full release cycle has elapsed may the old API be removed.
|
||||
|
||||
In addition to contributions to our standard queries and libraries, we also welcome contributions of a more experimental nature, which do not need to fulfill all the requirements listed above. See the guidelines for [experimental queries and libraries](docs/experimental.md) for details.
|
||||
|
||||
## Using your personal data
|
||||
|
||||
If you contribute to this project, we will record your name and email
|
||||
|
||||
50
change-notes/1.24/analysis-cpp.md
Normal file
50
change-notes/1.24/analysis-cpp.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# Improvements to C/C++ analysis
|
||||
|
||||
The following changes in version 1.24 affect C/C++ analysis in all applications.
|
||||
|
||||
## General improvements
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
| Implicit function declarations (`cpp/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql`) | correctness, maintainability | This query finds calls to undeclared functions that are compiled by a C compiler. Results are shown on LGTM by default. |
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|----------------------------|------------------------|------------------------------------------------------------------|
|
||||
| Buffer not sufficient for string (`cpp/overflow-calculated`) | More true positive results | This query now identifies a wider variety of buffer allocations using the `semmle.code.cpp.models.interfaces.Allocation` library. |
|
||||
| No space for zero terminator (`cpp/no-space-for-terminator`) | More true positive results | This query now identifies a wider variety of buffer allocations using the `semmle.code.cpp.models.interfaces.Allocation` library. |
|
||||
| Memory is never freed (`cpp/memory-never-freed`) | More true positive results | This query now identifies a wider variety of buffer allocations using the `semmle.code.cpp.models.interfaces.Allocation` library. |
|
||||
| Memory may not be freed (`cpp/memory-may-not-be-freed`) | More true positive results | This query now identifies a wider variety of buffer allocations using the `semmle.code.cpp.models.interfaces.Allocation` library. |
|
||||
| Missing return statement (`cpp/missing-return`) | Fewer false positive results | Functions containing `asm` statements are no longer highlighted by this query. |
|
||||
| No space for zero terminator (`cpp/no-space-for-terminator`) | More correct results | String arguments to formatting functions are now (usually) expected to be null terminated strings. |
|
||||
| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | | This query is no longer run on LGTM. |
|
||||
| No space for zero terminator (`cpp/no-space-for-terminator`) | Fewer false positive results | This query has been modified to be more conservative when identifying which pointers point to null-terminated strings. This approach produces fewer, more accurate results. |
|
||||
| Overloaded assignment does not return 'this' (`cpp/assignment-does-not-return-this`) | Fewer false positive results | This query no longer reports incorrect results in template classes. |
|
||||
| Unsafe array for days of the year (`cpp/leap-year/unsafe-array-for-days-of-the-year`) | | This query is no longer run on LGTM. |
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
* The data-flow library has been improved, which affects and improves some security queries. The improvements are:
|
||||
- Track flow through functions that combine taint tracking with flow through fields.
|
||||
- Track flow through clone-like functions, that is, functions that read contents of a field from a
|
||||
parameter and stores the value in the field of a returned object.
|
||||
* Created the `semmle.code.cpp.models.interfaces.Allocation` library to model allocation such as `new` expressions and calls to `malloc`. This in intended to replace the functionality in `semmle.code.cpp.commons.Alloc` with a more consistent and useful interface.
|
||||
* Created the `semmle.code.cpp.models.interfaces.Deallocation` library to model deallocation such as `delete` expressions and calls to `free`. This in intended to replace the functionality in `semmle.code.cpp.commons.Alloc` with a more consistent and useful interface.
|
||||
* The new class `StackVariable` should be used in place of `LocalScopeVariable`
|
||||
in most cases. The difference is that `StackVariable` does not include
|
||||
variables declared with `static` or `thread_local`.
|
||||
* As a rule of thumb, custom queries about the _values_ of variables should
|
||||
be changed from `LocalScopeVariable` to `StackVariable`, while queries
|
||||
about the _name or scope_ of variables should remain unchanged.
|
||||
* The `LocalScopeVariableReachability` library is deprecated in favor of
|
||||
`StackVariableReachability`. The functionality is the same.
|
||||
* The models library models `strlen` in more detail, and includes common variations such as `wcslen`.
|
||||
* The taint tracking library (`semmle.code.cpp.dataflow.TaintTracking`) has had
|
||||
the following improvements:
|
||||
* The library now models data flow through `strdup` and similar functions.
|
||||
* The library now models data flow through formatting functions such as `sprintf`.
|
||||
* The security pack taint tracking library (`semmle.code.cpp.security.TaintTracking`) uses a new intermediate representation. This provides a more precise analysis of pointers to stack variables and flow through parameters, improving the results of many security queries.
|
||||
* The global value numbering library (`semmle.code.cpp.valuenumbering.GlobalValueNumbering`) uses a new intermediate representation to provide a more precise analysis of heap allocated memory and pointers to stack variables.
|
||||
45
change-notes/1.24/analysis-csharp.md
Normal file
45
change-notes/1.24/analysis-csharp.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Improvements to C# analysis
|
||||
|
||||
The following changes in version 1.24 affect C# analysis in all applications.
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
| Assembly path injection (`cs/assembly-path-injection`) | security, external/cwe/cwe-114 | Finds user-controlled data used to load an assembly. |
|
||||
| Insecure configuration for ASP.NET requestValidationMode (`cs/insecure-request-validation-mode`) | security, external/cwe/cwe-016 | Finds where this attribute has been set to a value less than 4.5, which turns off some validation features and makes the application less secure. |
|
||||
| Insecure SQL connection (`cs/insecure-sql-connection`) | security, external/cwe/cwe-327 | Finds unencrypted SQL connection strings. |
|
||||
| Page request validation is disabled (`cs/web/request-validation-disabled`) | security, frameworks/asp.net, external/cwe/cwe-016 | Finds where ASP.NET page request validation has been disabled, which could make the application less secure. |
|
||||
| Serialization check bypass (`cs/serialization-check-bypass`) | security, external/cwe/cwe-20 | Finds where data is not validated in a deserialization method. |
|
||||
| XML injection (`cs/xml-injection`) | security, external/cwe/cwe-091 | Finds user-controlled data that is used to write directly to an XML document. |
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|------------------------------|------------------------|-----------------------------------|
|
||||
| Useless assignment to local variable (`cs/useless-assignment-to-local`) | Fewer false positive results | Results have been removed when the variable is named `_` in a `foreach` statement. |
|
||||
| Potentially dangerous use of non-short-circuit logic (`cs/non-short-circuit`) | Fewer false positive results | Results have been removed when the expression contains an `out` parameter. |
|
||||
| Dereferenced variable may be null (`cs/dereferenced-value-may-be-null`) | More results | Results are reported from parameters with a default value of `null`. |
|
||||
| Useless assignment to local variable (`cs/useless-assignment-to-local`) | Fewer false positive results | Results have been removed when the value assigned is an (implicitly or explicitly) cast default-like value. For example, `var s = (string)null` and `string s = default`. |
|
||||
|
||||
## Removal of old queries
|
||||
|
||||
## Changes to code extraction
|
||||
|
||||
* Tuple expressions, for example `(int,bool)` in `default((int,bool))` are now extracted correctly.
|
||||
* Expression nullability flow state is extracted.
|
||||
* Implicitly typed `stackalloc` expressions are now extracted correctly.
|
||||
* The difference between `stackalloc` array creations and normal array creations is extracted.
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
* The data-flow library has been improved, which affects and improves most security queries. The improvements are:
|
||||
- Track flow through methods that combine taint tracking with flow through fields.
|
||||
- Track flow through clone-like methods, that is, methods that read contents of a field from a
|
||||
parameter and stores the value in the field of a returned object.
|
||||
* The taint tracking library now tracks flow through (implicit or explicit) conversion operator calls.
|
||||
* [Code contracts](https://docs.microsoft.com/en-us/dotnet/framework/debug-trace-profile/code-contracts) are now recognized, and are treated like any other assertion methods.
|
||||
* Expression nullability flow state is given by the predicates `Expr.hasNotNullFlowState()` and `Expr.hasMaybeNullFlowState()`.
|
||||
* `stackalloc` array creations are now represented by the QL class `Stackalloc`. Previously they were represented by the class `ArrayCreation`.
|
||||
|
||||
## Changes to autobuilder
|
||||
42
change-notes/1.24/analysis-java.md
Normal file
42
change-notes/1.24/analysis-java.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Improvements to Java analysis
|
||||
|
||||
The following changes in version 1.24 affect Java analysis in all applications.
|
||||
|
||||
## General improvements
|
||||
|
||||
* Alert suppression can now be done with single-line block comments (`/* ... */`) as well as line comments (`// ...`).
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
| Disabled Spring CSRF protection (`java/spring-disabled-csrf-protection`) | security, external/cwe/cwe-352 | Finds disabled Cross-Site Request Forgery (CSRF) protection in Spring. Results are shown on LGTM by default. |
|
||||
| Failure to use HTTPS or SFTP URL in Maven artifact upload/download (`java/maven/non-https-url`) | security, external/cwe/cwe-300, external/cwe/cwe-319, external/cwe/cwe-494, external/cwe/cwe-829 | Finds use of insecure protocols during Maven dependency resolution. Results are shown on LGTM by default. |
|
||||
| LDAP query built from user-controlled sources (`java/ldap-injection`) | security, external/cwe/cwe-090 | Finds LDAP queries vulnerable to injection of unsanitized user-controlled input. Results are shown on LGTM by default. |
|
||||
| Left shift by more than the type width (`java/lshift-larger-than-type-width`) | correctness | Finds left shifts of ints by 32 bits or more and left shifts of longs by 64 bits or more. Results are shown on LGTM by default. |
|
||||
| Suspicious date format (`java/suspicious-date-format`) | correctness | Finds date format patterns that use placeholders that are likely to be incorrect. Results are shown on LGTM by default. |
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|------------------------------|------------------------|-----------------------------------|
|
||||
| Dereferenced variable may be null (`java/dereferenced-value-may-be-null`) | Fewer false positives | Final fields with a non-null initializer are no longer reported. |
|
||||
| Expression always evaluates to the same value (`java/evaluation-to-constant`) | Fewer false positives | Expressions of the form `0 * x` are usually intended and no longer reported. Also left shift of ints by 32 bits and longs by 64 bits are no longer reported as they are not constant, these results are instead reported by the new query `java/lshift-larger-than-type-width`. |
|
||||
| Useless null check (`java/useless-null-check`) | More true positives | Useless checks on final fields with a non-null initializer are now reported. |
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
* The data-flow library has been improved, which affects and improves most security queries. The improvements are:
|
||||
- Track flow through methods that combine taint tracking with flow through fields.
|
||||
- Track flow through clone-like methods, that is, methods that read contents of a field from a
|
||||
parameter and stores the value in the field of a returned object.
|
||||
* Identification of test classes has been improved. Previously, one of the
|
||||
match conditions would classify any class with a name containing the string
|
||||
"Test" as a test class, but now this matching has been replaced with one that
|
||||
looks for the occurrence of actual unit-test annotations. This affects the
|
||||
general file classification mechanism and thus suppression of alerts, and
|
||||
also any security queries using taint tracking, as test classes act as
|
||||
default barriers stopping taint flow.
|
||||
* Parentheses are now no longer modelled directly in the AST, that is, the
|
||||
`ParExpr` class is empty. Instead, a parenthesized expression can be
|
||||
identified with the `Expr.isParenthesized()` member predicate.
|
||||
67
change-notes/1.24/analysis-javascript.md
Normal file
67
change-notes/1.24/analysis-javascript.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Improvements to JavaScript analysis
|
||||
|
||||
## General improvements
|
||||
|
||||
* TypeScript 3.8 is now supported.
|
||||
|
||||
* Alert suppression can now be done with single-line block comments (`/* ... */`) as well as line comments (`// ...`).
|
||||
|
||||
* Imports with the `.js` extension can now be resolved to a TypeScript file,
|
||||
when the import refers to a file generated by TypeScript.
|
||||
|
||||
* Imports that rely on path-mappings from a `tsconfig.json` file can now be resolved.
|
||||
|
||||
* Export declarations of the form `export * as ns from "x"` are now analyzed more precisely.
|
||||
|
||||
* The analysis of sanitizer guards has improved, leading to fewer false-positive results from the security queries.
|
||||
|
||||
* The call graph construction has been improved, leading to more results from the security queries:
|
||||
- Calls can now be resolved to indirectly-defined class members in more cases.
|
||||
- Calls through partial invocations such as `.bind` can now be resolved in more cases.
|
||||
|
||||
* Support for the following frameworks and libraries has been improved:
|
||||
- [Electron](https://electronjs.org/)
|
||||
- [Handlebars](https://www.npmjs.com/package/handlebars)
|
||||
- [Koa](https://www.npmjs.com/package/koa)
|
||||
- [Node.js](https://nodejs.org/)
|
||||
- [Socket.IO](https://socket.io/)
|
||||
- [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API)
|
||||
- [chrome-remote-interface](https://www.npmjs.com/package/chrome-remote-interface)
|
||||
- [for-in](https://www.npmjs.com/package/for-in)
|
||||
- [for-own](https://www.npmjs.com/package/for-own)
|
||||
- [http2](https://nodejs.org/api/http2.html)
|
||||
- [lazy-cache](https://www.npmjs.com/package/lazy-cache)
|
||||
- [react](https://www.npmjs.com/package/react)
|
||||
- [send](https://www.npmjs.com/package/send)
|
||||
- [typeahead.js](https://www.npmjs.com/package/typeahead.js)
|
||||
- [ws](https://github.com/websockets/ws)
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|---------------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Cross-site scripting through exception (`js/xss-through-exception`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities where an exception is written to the DOM. Results are not shown on LGTM by default. |
|
||||
| Regular expression always matches (`js/regex/always-matches`) | correctness, regular-expressions | Highlights regular expression checks that trivially succeed by matching an empty substring. Results are shown on LGTM by default. |
|
||||
| Missing await (`js/missing-await`) | correctness | Highlights expressions that operate directly on a promise object in a nonsensical way, instead of awaiting its result. Results are shown on LGTM by default. |
|
||||
| Polynomial regular expression used on uncontrolled data (`js/polynomial-redos`) | security, external/cwe/cwe-730, external/cwe/cwe-400 | Highlights expensive regular expressions that may be used on malicious input. Results are shown on LGTM by default. |
|
||||
| Prototype pollution in utility function (`js/prototype-pollution-utility`) | security, external/cwe/cwe-400, external/cwe/cwe-471 | Highlights recursive copying operations that are susceptible to prototype pollution. Results are shown on LGTM by default. |
|
||||
| Unsafe jQuery plugin (`js/unsafe-jquery-plugin`) | Highlights potential XSS vulnerabilities in unsafely designed jQuery plugins. Results are shown on LGTM by default. |
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
|
||||
| Clear-text logging of sensitive information (`js/clear-text-logging`) | More results | More results involving `process.env` and indirect calls to logging methods are recognized. |
|
||||
| Duplicate parameter names (`js/duplicate-parameter-name`) | Fewer results | This query now recognizes additional parameters that reasonably can have duplicated names. |
|
||||
| Incomplete string escaping or encoding (`js/incomplete-sanitization`) | Fewer false positive results | This query now recognizes additional cases where a single replacement is likely to be intentional. |
|
||||
| Unbound event handler receiver (`js/unbound-event-handler-receiver`) | Fewer false positive results | This query now recognizes additional ways event handler receivers can be bound. |
|
||||
| Expression has no effect (`js/useless-expression`) | Fewer false positive results | The query now recognizes block-level flow type annotations and ignores the first statement of a try block. |
|
||||
| Use of call stack introspection in strict mode (`js/strict-mode-call-stack-introspection`) | Fewer false positive results | The query no longer flags expression statements. |
|
||||
| Missing CSRF middleware (`js/missing-token-validation`) | Fewer false positive results | The query reports fewer duplicates and only flags handlers that explicitly access cookie data. |
|
||||
| Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional ways dangerous paths can be constructed. |
|
||||
| Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional ways of constructing arguments to `cmd.exe` and `/bin/sh`. |
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
* The predicates `RegExpTerm.getSuccessor` and `RegExpTerm.getPredecessor` have been changed to reflect textual, not operational, matching order. This only makes a difference in lookbehind assertions, which are operationally matched backwards. Previously, `getSuccessor` would mimick this, so in an assertion `(?<=ab)` the term `b` would be considered the predecessor, not the successor, of `a`. Textually, however, `a` is still matched before `b`, and this is the order we now follow.
|
||||
* An extensible model of the `EventEmitter` pattern has been implemented.
|
||||
37
change-notes/1.24/analysis-python.md
Normal file
37
change-notes/1.24/analysis-python.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Improvements to Python analysis
|
||||
|
||||
The following changes in version 1.24 affect Python analysis in all applications.
|
||||
|
||||
## General improvements
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|----------------------------|------------------------|------------------------------------------------------------------|
|
||||
|
||||
### Web framework support
|
||||
|
||||
The QL-library support for the web frameworks Bottle, CherryPy, Falcon, Pyramid, TurboGears, Tornado, and Twisted have
|
||||
been fixed so they provide a proper HttpRequestTaintSource, instead of a TaintSource. This will enable results for the following queries:
|
||||
|
||||
- py/path-injection
|
||||
- py/command-line-injection
|
||||
- py/reflective-xss
|
||||
- py/sql-injection
|
||||
- py/code-injection
|
||||
- py/unsafe-deserialization
|
||||
- py/url-redirection
|
||||
|
||||
The QL-library support for the web framework Twisted have been fixed so they provide a proper
|
||||
HttpResponseTaintSink, instead of a TaintSink. This will enable results for the following
|
||||
queries:
|
||||
|
||||
- py/reflective-xss
|
||||
- py/stack-trace-exposure
|
||||
|
||||
## Changes to libraries
|
||||
7
change-notes/1.24/extractor-javascript.md
Normal file
7
change-notes/1.24/extractor-javascript.md
Normal file
@@ -0,0 +1,7 @@
|
||||
[[ condition: enterprise-only ]]
|
||||
|
||||
# Improvements to JavaScript analysis
|
||||
|
||||
## Changes to code extraction
|
||||
|
||||
* `import.meta` expressions no longer result in a syntax error in JavaScript files.
|
||||
@@ -82,6 +82,14 @@
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/IRType.qll"
|
||||
],
|
||||
"IR IRConfiguration": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/IRConfiguration.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/IRConfiguration.qll"
|
||||
],
|
||||
"IR UseSoundEscapeAnalysis": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/UseSoundEscapeAnalysis.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/UseSoundEscapeAnalysis.qll"
|
||||
],
|
||||
"IR Operand Tag": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/internal/OperandTag.qll"
|
||||
@@ -143,6 +151,11 @@
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/Opcode.qll"
|
||||
],
|
||||
"IR SSASanity": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSASanity.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSASanity.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSASanity.qll"
|
||||
],
|
||||
"C++ IR InstructionImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll",
|
||||
@@ -177,9 +190,14 @@
|
||||
"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": [
|
||||
"SSA AliasAnalysis": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll"
|
||||
],
|
||||
"C++ SSA AliasAnalysisImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysisImports.qll"
|
||||
],
|
||||
"C++ IR ValueNumberingImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
|
||||
@@ -190,6 +208,10 @@
|
||||
"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 AliasConfiguration (unaliased_ssa)": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll"
|
||||
],
|
||||
"IR SSA SSAConstruction": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll",
|
||||
@@ -200,7 +222,14 @@
|
||||
"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"
|
||||
],
|
||||
"IR ValueNumber": [
|
||||
"IR ValueNumberInternal": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll"
|
||||
],
|
||||
"C++ IR ValueNumber": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll",
|
||||
@@ -260,5 +289,12 @@
|
||||
"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"
|
||||
],
|
||||
"XML": [
|
||||
"cpp/ql/src/semmle/code/cpp/XML.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/XML.qll",
|
||||
"java/ql/src/semmle/code/xml/XML.qll",
|
||||
"javascript/ql/src/semmle/javascript/XML.qll",
|
||||
"python/ql/src/semmle/python/xml/XML.qll"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/ExprHasNoEffect.ql: /Correctness/Common Errors
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/TooFewArguments.ql: /Correctness/Common Errors
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/TooManyArguments.ql: /Correctness/Common Errors
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql: /Correctness/Common Errors
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/ShortCircuitBitMask.ql: /Correctness/Common Errors
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.ql: /Correctness/Common Errors
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Arithmetic/FloatComparison.ql: /Correctness/Common Errors
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/ExprHasNoEffect.ql: /Correctness/Common Errors
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/TooFewArguments.ql: /Correctness/Common Errors
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/TooManyArguments.ql: /Correctness/Common Errors
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql: /Correctness/Common Errors
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/ShortCircuitBitMask.ql: /Correctness/Common Errors
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Likely Typos/MissingEnumCaseInSwitch.ql: /Correctness/Common Errors
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Arithmetic/FloatComparison.ql: /Correctness/Common Errors
|
||||
|
||||
@@ -10,12 +10,25 @@ import cpp
|
||||
/**
|
||||
* An alert suppression comment.
|
||||
*/
|
||||
class SuppressionComment extends CppStyleComment {
|
||||
class SuppressionComment extends Comment {
|
||||
string annotation;
|
||||
string text;
|
||||
|
||||
SuppressionComment() {
|
||||
text = getContents().suffix(2) and
|
||||
(
|
||||
this instanceof CppStyleComment and
|
||||
// strip the beginning slashes
|
||||
text = getContents().suffix(2)
|
||||
or
|
||||
this instanceof CStyleComment and
|
||||
// strip both the beginning /* and the end */ the comment
|
||||
exists(string text0 |
|
||||
text0 = getContents().suffix(2) and
|
||||
text = text0.prefix(text0.length() - 2)
|
||||
) and
|
||||
// The /* */ comment must be a single-line comment
|
||||
not text.matches("%\n%")
|
||||
) and
|
||||
(
|
||||
// match `lgtm[...]` anywhere in the comment
|
||||
annotation = text.regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @description A function that uses more functions and variables from another file than functions and variables from its own file. This function might be better placed in the other file, to avoid exposing internals of the file it depends on.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @precision high
|
||||
* @precision medium
|
||||
* @id cpp/feature-envy
|
||||
* @tags maintainability
|
||||
* modularity
|
||||
@@ -25,7 +25,8 @@ predicate functionUsesFunction(Function source, Function f, File target) {
|
||||
}
|
||||
|
||||
predicate dependencyCount(Function source, File target, int res) {
|
||||
res = strictcount(Declaration d |
|
||||
res =
|
||||
strictcount(Declaration d |
|
||||
functionUsesVariable(source, d, target) or
|
||||
functionUsesFunction(source, d, target)
|
||||
)
|
||||
|
||||
@@ -38,14 +38,16 @@ where
|
||||
n = count(Function f | f.fromSource()).toString()
|
||||
or
|
||||
l = "Number of Lines Of Code" and
|
||||
n = sum(File f, int toSum |
|
||||
n =
|
||||
sum(File f, int toSum |
|
||||
f.fromSource() and toSum = f.getMetrics().getNumberOfLinesOfCode()
|
||||
|
|
||||
toSum
|
||||
).toString()
|
||||
or
|
||||
l = "Self-Containedness" and
|
||||
n = (
|
||||
n =
|
||||
(
|
||||
100 * sum(Class c | c.fromSource() | c.getMetrics().getEfferentSourceCoupling()) /
|
||||
sum(Class c | c.fromSource() | c.getMetrics().getEfferentCoupling())
|
||||
).toString() + "%"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @description Two files share too much information about each other (accessing many operations or variables in both directions). It would be better to invert some of the dependencies to reduce the coupling between the two files.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @precision high
|
||||
* @precision medium
|
||||
* @id cpp/file-intimacy
|
||||
* @tags maintainability
|
||||
* modularity
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @description Finds classes with many fields; they could probably be refactored by breaking them down into smaller classes, and using composition.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @precision high
|
||||
* @precision medium
|
||||
* @id cpp/class-many-fields
|
||||
* @tags maintainability
|
||||
* statistical
|
||||
@@ -80,11 +80,8 @@ class VariableDeclarationLine extends TVariableDeclarationInfo {
|
||||
* (that is, the first is 0, the second is 1 and so on).
|
||||
*/
|
||||
private int getRank() {
|
||||
line = rank[result](VariableDeclarationLine vdl, int l |
|
||||
vdl = TVariableDeclarationLine(c, f, l)
|
||||
|
|
||||
l
|
||||
)
|
||||
line =
|
||||
rank[result](VariableDeclarationLine vdl, int l | vdl = TVariableDeclarationLine(c, f, l) | l)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,7 +130,8 @@ class VariableDeclarationGroup extends VariableDeclarationLine {
|
||||
* Gets the number of uniquely named `VariableDeclarationEntry`s in this group.
|
||||
*/
|
||||
int getCount() {
|
||||
result = count(VariableDeclarationLine l |
|
||||
result =
|
||||
count(VariableDeclarationLine l |
|
||||
l = getProximateNext*()
|
||||
|
|
||||
l.getAVDE().getVariable().getName()
|
||||
@@ -166,7 +164,8 @@ class ExtClass extends Class {
|
||||
|
||||
from ExtClass c, int n, VariableDeclarationGroup vdg, string suffix
|
||||
where
|
||||
n = strictcount(string fieldName |
|
||||
n =
|
||||
strictcount(string fieldName |
|
||||
exists(Field f |
|
||||
f.getDeclaringType() = c and
|
||||
fieldName = f.getName() and
|
||||
|
||||
@@ -50,7 +50,8 @@ class BlockOrNonChild extends Element {
|
||||
|
||||
private int getNonContiguousStartRankIn(AffectedFile file) {
|
||||
// When using `rank` with `order by`, the ranks may not be contiguous.
|
||||
this = rank[result](BlockOrNonChild boc, int startLine, int startCol |
|
||||
this =
|
||||
rank[result](BlockOrNonChild boc, int startLine, int startCol |
|
||||
boc.getLocation().hasLocationInfo(file.getAbsolutePath(), startLine, startCol, _, _)
|
||||
|
|
||||
boc order by startLine, startCol
|
||||
@@ -58,13 +59,15 @@ class BlockOrNonChild extends Element {
|
||||
}
|
||||
|
||||
int getStartRankIn(AffectedFile file) {
|
||||
this.getNonContiguousStartRankIn(file) = rank[result](int rnk |
|
||||
this.getNonContiguousStartRankIn(file) =
|
||||
rank[result](int rnk |
|
||||
exists(BlockOrNonChild boc | boc.getNonContiguousStartRankIn(file) = rnk)
|
||||
)
|
||||
}
|
||||
|
||||
int getNonContiguousEndRankIn(AffectedFile file) {
|
||||
this = rank[result](BlockOrNonChild boc, int endLine, int endCol |
|
||||
this =
|
||||
rank[result](BlockOrNonChild boc, int endLine, int endCol |
|
||||
boc.getLocation().hasLocationInfo(file.getAbsolutePath(), _, _, endLine, endCol)
|
||||
|
|
||||
boc order by endLine, endCol
|
||||
@@ -79,9 +82,8 @@ predicate emptyBlockContainsNonchild(Block b) {
|
||||
emptyBlock(_, b) and
|
||||
exists(BlockOrNonChild c, AffectedFile file |
|
||||
c.(BlockOrNonChild).getStartRankIn(file) = 1 + b.(BlockOrNonChild).getStartRankIn(file) and
|
||||
c.(BlockOrNonChild).getNonContiguousEndRankIn(file) < b
|
||||
.(BlockOrNonChild)
|
||||
.getNonContiguousEndRankIn(file)
|
||||
c.(BlockOrNonChild).getNonContiguousEndRankIn(file) <
|
||||
b.(BlockOrNonChild).getNonContiguousEndRankIn(file)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id cpp/japanese-era/exact-era-date
|
||||
* @precision medium
|
||||
* @tags reliability
|
||||
* @precision low
|
||||
* @tags maintainability
|
||||
* reliability
|
||||
* japanese-era
|
||||
*/
|
||||
|
||||
|
||||
@@ -307,7 +307,8 @@ predicate nonTrivialValue(string value, Literal literal) {
|
||||
}
|
||||
|
||||
predicate valueOccurrenceCount(string value, int n) {
|
||||
n = strictcount(Location loc |
|
||||
n =
|
||||
strictcount(Location loc |
|
||||
exists(Literal lit | lit.getLocation() = loc | nonTrivialValue(value, lit)) and
|
||||
// Exclude generated files (they do not have the same maintainability
|
||||
// concerns as ordinary source files)
|
||||
@@ -338,7 +339,8 @@ predicate check(Literal lit, string value, int n, File f) {
|
||||
}
|
||||
|
||||
predicate checkWithFileCount(string value, int overallCount, int fileCount, File f) {
|
||||
fileCount = strictcount(Location loc |
|
||||
fileCount =
|
||||
strictcount(Location loc |
|
||||
exists(Literal lit | lit.getLocation() = loc | check(lit, value, overallCount, f))
|
||||
)
|
||||
}
|
||||
@@ -364,7 +366,8 @@ predicate firstOccurrence(Literal lit, string value, int n) {
|
||||
predicate magicConstant(Literal e, string msg) {
|
||||
exists(string value, int n |
|
||||
firstOccurrence(e, value, n) and
|
||||
msg = "Magic constant: literal '" + value + "' is repeated " + n.toString() +
|
||||
msg =
|
||||
"Magic constant: literal '" + value + "' is repeated " + n.toString() +
|
||||
" times and should be encapsulated in a constant."
|
||||
)
|
||||
}
|
||||
|
||||
@@ -28,13 +28,15 @@ import cpp
|
||||
// design question and carries has no safety risk.
|
||||
predicate generatedCopyAssignment(CopyConstructor cc, string msg) {
|
||||
cc.getDeclaringType().hasImplicitCopyAssignmentOperator() and
|
||||
msg = "No matching copy assignment operator in class " + cc.getDeclaringType().getName() +
|
||||
msg =
|
||||
"No matching copy assignment operator in class " + cc.getDeclaringType().getName() +
|
||||
". It is good practice to match a copy constructor with a " + "copy assignment operator."
|
||||
}
|
||||
|
||||
predicate generatedCopyConstructor(CopyAssignmentOperator ca, string msg) {
|
||||
ca.getDeclaringType().hasImplicitCopyConstructor() and
|
||||
msg = "No matching copy constructor in class " + ca.getDeclaringType().getName() +
|
||||
msg =
|
||||
"No matching copy constructor in class " + ca.getDeclaringType().getName() +
|
||||
". It is good practice to match a copy assignment operator with a " + "copy constructor."
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ predicate testAndBranch(Expr e, Stmt branch) {
|
||||
)
|
||||
}
|
||||
|
||||
predicate choice(LocalScopeVariable v, Stmt branch, string value) {
|
||||
predicate choice(StackVariable v, Stmt branch, string value) {
|
||||
exists(AnalysedExpr e |
|
||||
testAndBranch(e, branch) and
|
||||
(
|
||||
@@ -33,7 +33,7 @@ predicate choice(LocalScopeVariable v, Stmt branch, string value) {
|
||||
)
|
||||
}
|
||||
|
||||
predicate guarded(LocalScopeVariable v, Stmt loopstart, AnalysedExpr child) {
|
||||
predicate guarded(StackVariable v, Stmt loopstart, AnalysedExpr child) {
|
||||
choice(v, loopstart, _) and
|
||||
loopstart.getChildStmt*() = child.getEnclosingStmt() and
|
||||
(definition(v, child) or exists(child.getNullSuccessor(v)))
|
||||
@@ -47,9 +47,7 @@ predicate addressLeak(Variable v, Stmt leak) {
|
||||
)
|
||||
}
|
||||
|
||||
from
|
||||
LocalScopeVariable v, Stmt branch, AnalysedExpr cond, string context, string test,
|
||||
string testresult
|
||||
from StackVariable v, Stmt branch, AnalysedExpr cond, string context, string test, string testresult
|
||||
where
|
||||
choice(v, branch, context) and
|
||||
forall(ControlFlowNode def | definition(v, def) and definitionReaches(def, cond) |
|
||||
|
||||
@@ -23,14 +23,14 @@ predicate closeCall(FunctionCall fc, Variable v) {
|
||||
)
|
||||
}
|
||||
|
||||
predicate openDefinition(LocalScopeVariable v, ControlFlowNode def) {
|
||||
predicate openDefinition(StackVariable v, ControlFlowNode def) {
|
||||
exists(Expr expr | exprDefinition(v, def, expr) and allocateDescriptorCall(expr))
|
||||
}
|
||||
|
||||
predicate openReaches(ControlFlowNode def, ControlFlowNode node) {
|
||||
exists(LocalScopeVariable v | openDefinition(v, def) and node = def.getASuccessor())
|
||||
exists(StackVariable v | openDefinition(v, def) and node = def.getASuccessor())
|
||||
or
|
||||
exists(LocalScopeVariable v, ControlFlowNode mid |
|
||||
exists(StackVariable v, ControlFlowNode mid |
|
||||
openDefinition(v, def) and
|
||||
openReaches(def, mid) and
|
||||
not errorSuccessor(v, mid) and
|
||||
@@ -40,7 +40,7 @@ predicate openReaches(ControlFlowNode def, ControlFlowNode node) {
|
||||
)
|
||||
}
|
||||
|
||||
predicate assignedToFieldOrGlobal(LocalScopeVariable v, Assignment assign) {
|
||||
predicate assignedToFieldOrGlobal(StackVariable v, Assignment assign) {
|
||||
exists(Variable external |
|
||||
assign.getRValue() = v.getAnAccess() and
|
||||
assign.getLValue().(VariableAccess).getTarget() = external and
|
||||
@@ -48,7 +48,7 @@ predicate assignedToFieldOrGlobal(LocalScopeVariable v, Assignment assign) {
|
||||
)
|
||||
}
|
||||
|
||||
from LocalScopeVariable v, ControlFlowNode def, ReturnStmt ret
|
||||
from StackVariable v, ControlFlowNode def, ReturnStmt ret
|
||||
where
|
||||
openDefinition(v, def) and
|
||||
openReaches(def, ret) and
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
This rule finds calls to <code>open</code> or <code>socket</code> where there is no corresponding <code>close</code> call in the program analyzed.
|
||||
This rule finds calls to <code>socket</code> where there is no corresponding <code>close</code> call in the program analyzed.
|
||||
Leaving descriptors open will cause a resource leak that will persist even after the program terminates.
|
||||
</p>
|
||||
|
||||
@@ -14,7 +14,7 @@ Leaving descriptors open will cause a resource leak that will persist even after
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Ensure that all file or socket descriptors allocated by the program are freed before it terminates.</p>
|
||||
<p>Ensure that all socket descriptors allocated by the program are freed before it terminates.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Open descriptor never closed
|
||||
* @description Functions that always return before closing the socket or file they opened leak resources.
|
||||
* @description Functions that always return before closing the socket they opened leak resources.
|
||||
* @kind problem
|
||||
* @id cpp/descriptor-never-closed
|
||||
* @problem.severity warning
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*/
|
||||
|
||||
import FileClosed
|
||||
import semmle.code.cpp.controlflow.LocalScopeVariableReachability
|
||||
import semmle.code.cpp.controlflow.StackVariableReachability
|
||||
|
||||
/**
|
||||
* Extend the NullValue class used by Nullness.qll to include simple -1 as a 'null' value
|
||||
@@ -68,18 +68,18 @@ predicate fcloseCallOrIndirect(FunctionCall fc, Variable v) {
|
||||
)
|
||||
}
|
||||
|
||||
predicate fopenDefinition(LocalScopeVariable v, ControlFlowNode def) {
|
||||
predicate fopenDefinition(StackVariable v, ControlFlowNode def) {
|
||||
exists(Expr expr | exprDefinition(v, def, expr) and fopenCallOrIndirect(expr))
|
||||
}
|
||||
|
||||
class FOpenVariableReachability extends LocalScopeVariableReachabilityWithReassignment {
|
||||
class FOpenVariableReachability extends StackVariableReachabilityWithReassignment {
|
||||
FOpenVariableReachability() { this = "FOpenVariableReachability" }
|
||||
|
||||
override predicate isSourceActual(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isSourceActual(ControlFlowNode node, StackVariable v) {
|
||||
fopenDefinition(v, node)
|
||||
}
|
||||
|
||||
override predicate isSinkActual(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isSinkActual(ControlFlowNode node, StackVariable v) {
|
||||
// node may be used in fopenReaches
|
||||
exists(node.(AnalysedExpr).getNullSuccessor(v)) or
|
||||
fcloseCallOrIndirect(node, v) or
|
||||
@@ -88,15 +88,13 @@ class FOpenVariableReachability extends LocalScopeVariableReachabilityWithReassi
|
||||
v.getFunction() = node.(ReturnStmt).getEnclosingFunction()
|
||||
}
|
||||
|
||||
override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) {
|
||||
definitionBarrier(v, node)
|
||||
}
|
||||
override predicate isBarrier(ControlFlowNode node, StackVariable v) { definitionBarrier(v, node) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The value from fopen at `def` is still held in Variable `v` upon entering `node`.
|
||||
*/
|
||||
predicate fopenVariableReaches(LocalScopeVariable v, ControlFlowNode def, ControlFlowNode node) {
|
||||
predicate fopenVariableReaches(StackVariable v, ControlFlowNode def, ControlFlowNode node) {
|
||||
exists(FOpenVariableReachability r |
|
||||
// reachability
|
||||
r.reachesTo(def, _, node, v)
|
||||
@@ -107,25 +105,23 @@ predicate fopenVariableReaches(LocalScopeVariable v, ControlFlowNode def, Contro
|
||||
)
|
||||
}
|
||||
|
||||
class FOpenReachability extends LocalScopeVariableReachabilityExt {
|
||||
class FOpenReachability extends StackVariableReachabilityExt {
|
||||
FOpenReachability() { this = "FOpenReachability" }
|
||||
|
||||
override predicate isSource(ControlFlowNode node, LocalScopeVariable v) {
|
||||
fopenDefinition(v, node)
|
||||
}
|
||||
override predicate isSource(ControlFlowNode node, StackVariable v) { fopenDefinition(v, node) }
|
||||
|
||||
override predicate isSink(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isSink(ControlFlowNode node, StackVariable v) {
|
||||
v.getFunction() = node.(ReturnStmt).getEnclosingFunction()
|
||||
}
|
||||
|
||||
override predicate isBarrier(
|
||||
ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, LocalScopeVariable v
|
||||
ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, StackVariable v
|
||||
) {
|
||||
isSource(source, v) and
|
||||
next = node.getASuccessor() and
|
||||
// the file (stored in any variable `v0`) opened at `source` is closed or
|
||||
// assigned to a global at node, or NULL checked on the edge node -> next.
|
||||
exists(LocalScopeVariable v0 | fopenVariableReaches(v0, source, node) |
|
||||
exists(StackVariable v0 | fopenVariableReaches(v0, source, node) |
|
||||
node.(AnalysedExpr).getNullSuccessor(v0) = next or
|
||||
fcloseCallOrIndirect(node, v0) or
|
||||
assignedToFieldOrGlobal(v0, node)
|
||||
@@ -142,11 +138,11 @@ predicate fopenReaches(ControlFlowNode def, ControlFlowNode node) {
|
||||
exists(FOpenReachability r | r.reaches(def, _, node))
|
||||
}
|
||||
|
||||
predicate assignedToFieldOrGlobal(LocalScopeVariable v, Expr e) {
|
||||
// assigned to anything except a LocalScopeVariable
|
||||
predicate assignedToFieldOrGlobal(StackVariable v, Expr e) {
|
||||
// assigned to anything except a StackVariable
|
||||
// (typically a field or global, but for example also *ptr = v)
|
||||
e.(Assignment).getRValue() = v.getAnAccess() and
|
||||
not e.(Assignment).getLValue().(VariableAccess).getTarget() instanceof LocalScopeVariable
|
||||
not e.(Assignment).getLValue().(VariableAccess).getTarget() instanceof StackVariable
|
||||
or
|
||||
exists(Expr midExpr, Function mid, int arg |
|
||||
// indirect assignment
|
||||
@@ -163,7 +159,7 @@ predicate assignedToFieldOrGlobal(LocalScopeVariable v, Expr e) {
|
||||
from ControlFlowNode def, ReturnStmt ret
|
||||
where
|
||||
fopenReaches(def, ret) and
|
||||
not exists(LocalScopeVariable v |
|
||||
not exists(StackVariable v |
|
||||
fopenVariableReaches(v, def, ret) and
|
||||
ret.getAChild*() = v.getAnAccess()
|
||||
)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import cpp
|
||||
|
||||
from LocalScopeVariable v, ControlFlowNode def, VariableAccess checked, VariableAccess unchecked
|
||||
from StackVariable v, ControlFlowNode def, VariableAccess checked, VariableAccess unchecked
|
||||
where
|
||||
checked = v.getAnAccess() and
|
||||
dereferenced(checked) and
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
import cpp
|
||||
|
||||
predicate negativeCheck(LocalScopeVariable v, ComparisonOperation op) {
|
||||
predicate negativeCheck(StackVariable v, ComparisonOperation op) {
|
||||
exists(int varindex, string constant, Literal lit |
|
||||
op.getChild(varindex) = v.getAnAccess() and
|
||||
op.getChild(1 - varindex) = lit and
|
||||
@@ -38,7 +38,7 @@ predicate negativeCheck(LocalScopeVariable v, ComparisonOperation op) {
|
||||
)
|
||||
}
|
||||
|
||||
from LocalScopeVariable v, ArrayExpr dangerous, Expr check
|
||||
from StackVariable v, ArrayExpr dangerous, Expr check
|
||||
where
|
||||
useUsePair(v, dangerous.getArrayOffset(), check.getAChild()) and
|
||||
negativeCheck(v, check) and
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
import semmle.code.cpp.pointsto.PointsTo
|
||||
|
||||
private predicate freed(Expr e) {
|
||||
exists(FunctionCall fc, Expr arg |
|
||||
freeCall(fc, arg) and
|
||||
arg = e
|
||||
)
|
||||
or
|
||||
exists(DeleteExpr de | de.getExpr() = e)
|
||||
or
|
||||
exists(DeleteArrayExpr de | de.getExpr() = e)
|
||||
e = any(DeallocationExpr de).getFreedExpr()
|
||||
or
|
||||
exists(ExprCall c |
|
||||
// cautiously assume that any ExprCall could be a freeCall.
|
||||
@@ -22,7 +15,4 @@ class FreedExpr extends PointsToExpr {
|
||||
override predicate interesting() { freed(this) }
|
||||
}
|
||||
|
||||
predicate allocMayBeFreed(Expr alloc) {
|
||||
isAllocationExpr(alloc) and
|
||||
anythingPointsTo(alloc)
|
||||
}
|
||||
predicate allocMayBeFreed(AllocationExpr alloc) { anythingPointsTo(alloc) }
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*/
|
||||
|
||||
import MemoryFreed
|
||||
import semmle.code.cpp.controlflow.LocalScopeVariableReachability
|
||||
import semmle.code.cpp.controlflow.StackVariableReachability
|
||||
|
||||
/**
|
||||
* 'call' is either a direct call to f, or a possible call to f
|
||||
@@ -24,7 +24,7 @@ predicate mayCallFunction(Expr call, Function f) {
|
||||
|
||||
predicate allocCallOrIndirect(Expr e) {
|
||||
// direct alloc call
|
||||
isAllocationExpr(e) and
|
||||
e.(AllocationExpr).requiresDealloc() and
|
||||
// We are only interested in alloc calls that are
|
||||
// actually freed somehow, as MemoryNeverFreed
|
||||
// will catch those that aren't.
|
||||
@@ -53,8 +53,7 @@ predicate allocCallOrIndirect(Expr e) {
|
||||
* can cause memory leaks.
|
||||
*/
|
||||
predicate verifiedRealloc(FunctionCall reallocCall, Variable v, ControlFlowNode verified) {
|
||||
reallocCall.getTarget().hasGlobalOrStdName("realloc") and
|
||||
reallocCall.getArgument(0) = v.getAnAccess() and
|
||||
reallocCall.(AllocationExpr).getReallocPtr() = v.getAnAccess() and
|
||||
(
|
||||
exists(Variable newV, ControlFlowNode node |
|
||||
// a realloc followed by a null check at 'node' (return the non-null
|
||||
@@ -71,23 +70,19 @@ predicate verifiedRealloc(FunctionCall reallocCall, Variable v, ControlFlowNode
|
||||
or
|
||||
// a realloc(ptr, 0), which always succeeds and frees
|
||||
// (return the realloc itself)
|
||||
reallocCall.getArgument(1).getValue() = "0" and
|
||||
reallocCall.(AllocationExpr).getReallocPtr().getValue() = "0" and
|
||||
verified = reallocCall
|
||||
)
|
||||
}
|
||||
|
||||
predicate freeCallOrIndirect(ControlFlowNode n, Variable v) {
|
||||
// direct free call
|
||||
freeCall(n, v.getAnAccess()) and
|
||||
not n.(FunctionCall).getTarget().hasGlobalOrStdName("realloc")
|
||||
n.(DeallocationExpr).getFreedExpr() = v.getAnAccess() and
|
||||
not exists(n.(AllocationExpr).getReallocPtr())
|
||||
or
|
||||
// verified realloc call
|
||||
verifiedRealloc(_, v, n)
|
||||
or
|
||||
n.(DeleteExpr).getExpr() = v.getAnAccess()
|
||||
or
|
||||
n.(DeleteArrayExpr).getExpr() = v.getAnAccess()
|
||||
or
|
||||
exists(FunctionCall midcall, Function mid, int arg |
|
||||
// indirect free call
|
||||
n.(Call).getArgument(arg) = v.getAnAccess() and
|
||||
@@ -97,18 +92,18 @@ predicate freeCallOrIndirect(ControlFlowNode n, Variable v) {
|
||||
)
|
||||
}
|
||||
|
||||
predicate allocationDefinition(LocalScopeVariable v, ControlFlowNode def) {
|
||||
predicate allocationDefinition(StackVariable v, ControlFlowNode def) {
|
||||
exists(Expr expr | exprDefinition(v, def, expr) and allocCallOrIndirect(expr))
|
||||
}
|
||||
|
||||
class AllocVariableReachability extends LocalScopeVariableReachabilityWithReassignment {
|
||||
class AllocVariableReachability extends StackVariableReachabilityWithReassignment {
|
||||
AllocVariableReachability() { this = "AllocVariableReachability" }
|
||||
|
||||
override predicate isSourceActual(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isSourceActual(ControlFlowNode node, StackVariable v) {
|
||||
allocationDefinition(v, node)
|
||||
}
|
||||
|
||||
override predicate isSinkActual(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isSinkActual(ControlFlowNode node, StackVariable v) {
|
||||
// node may be used in allocationReaches
|
||||
exists(node.(AnalysedExpr).getNullSuccessor(v)) or
|
||||
freeCallOrIndirect(node, v) or
|
||||
@@ -117,15 +112,13 @@ class AllocVariableReachability extends LocalScopeVariableReachabilityWithReassi
|
||||
v.getFunction() = node.(ReturnStmt).getEnclosingFunction()
|
||||
}
|
||||
|
||||
override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) {
|
||||
definitionBarrier(v, node)
|
||||
}
|
||||
override predicate isBarrier(ControlFlowNode node, StackVariable v) { definitionBarrier(v, node) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The value from allocation `def` is still held in Variable `v` upon entering `node`.
|
||||
*/
|
||||
predicate allocatedVariableReaches(LocalScopeVariable v, ControlFlowNode def, ControlFlowNode node) {
|
||||
predicate allocatedVariableReaches(StackVariable v, ControlFlowNode def, ControlFlowNode node) {
|
||||
exists(AllocVariableReachability r |
|
||||
// reachability
|
||||
r.reachesTo(def, _, node, v)
|
||||
@@ -136,25 +129,25 @@ predicate allocatedVariableReaches(LocalScopeVariable v, ControlFlowNode def, Co
|
||||
)
|
||||
}
|
||||
|
||||
class AllocReachability extends LocalScopeVariableReachabilityExt {
|
||||
class AllocReachability extends StackVariableReachabilityExt {
|
||||
AllocReachability() { this = "AllocReachability" }
|
||||
|
||||
override predicate isSource(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isSource(ControlFlowNode node, StackVariable v) {
|
||||
allocationDefinition(v, node)
|
||||
}
|
||||
|
||||
override predicate isSink(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isSink(ControlFlowNode node, StackVariable v) {
|
||||
v.getFunction() = node.(ReturnStmt).getEnclosingFunction()
|
||||
}
|
||||
|
||||
override predicate isBarrier(
|
||||
ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, LocalScopeVariable v
|
||||
ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, StackVariable v
|
||||
) {
|
||||
isSource(source, v) and
|
||||
next = node.getASuccessor() and
|
||||
// the memory (stored in any variable `v0`) allocated at `source` is freed or
|
||||
// assigned to a global at node, or NULL checked on the edge node -> next.
|
||||
exists(LocalScopeVariable v0 | allocatedVariableReaches(v0, source, node) |
|
||||
exists(StackVariable v0 | allocatedVariableReaches(v0, source, node) |
|
||||
node.(AnalysedExpr).getNullSuccessor(v0) = next or
|
||||
freeCallOrIndirect(node, v0) or
|
||||
assignedToFieldOrGlobal(v0, node)
|
||||
@@ -171,11 +164,11 @@ predicate allocationReaches(ControlFlowNode def, ControlFlowNode node) {
|
||||
exists(AllocReachability r | r.reaches(def, _, node))
|
||||
}
|
||||
|
||||
predicate assignedToFieldOrGlobal(LocalScopeVariable v, Expr e) {
|
||||
// assigned to anything except a LocalScopeVariable
|
||||
predicate assignedToFieldOrGlobal(StackVariable v, Expr e) {
|
||||
// assigned to anything except a StackVariable
|
||||
// (typically a field or global, but for example also *ptr = v)
|
||||
e.(Assignment).getRValue() = v.getAnAccess() and
|
||||
not e.(Assignment).getLValue().(VariableAccess).getTarget() instanceof LocalScopeVariable
|
||||
not e.(Assignment).getLValue().(VariableAccess).getTarget() instanceof StackVariable
|
||||
or
|
||||
exists(Expr midExpr, Function mid, int arg |
|
||||
// indirect assignment
|
||||
@@ -192,7 +185,7 @@ predicate assignedToFieldOrGlobal(LocalScopeVariable v, Expr e) {
|
||||
from ControlFlowNode def, ReturnStmt ret
|
||||
where
|
||||
allocationReaches(def, ret) and
|
||||
not exists(LocalScopeVariable v |
|
||||
not exists(StackVariable v |
|
||||
allocatedVariableReaches(v, def, ret) and
|
||||
ret.getAChild*() = v.getAnAccess()
|
||||
)
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
import MemoryFreed
|
||||
|
||||
from Expr alloc
|
||||
where isAllocationExpr(alloc) and not allocMayBeFreed(alloc)
|
||||
from AllocationExpr alloc
|
||||
where
|
||||
alloc.requiresDealloc() and
|
||||
not allocMayBeFreed(alloc)
|
||||
select alloc, "This memory is never freed"
|
||||
|
||||
@@ -43,7 +43,7 @@ class FunctionWithNegativeReturn extends Function {
|
||||
predicate dangerousUse(IntegralReturnValue val, Expr use) {
|
||||
exists(ArrayExpr ae | ae.getArrayOffset() = val and use = val)
|
||||
or
|
||||
exists(LocalScopeVariable v, ControlFlowNode def, ArrayExpr ae |
|
||||
exists(StackVariable v, ControlFlowNode def, ArrayExpr ae |
|
||||
exprDefinition(v, def, val) and
|
||||
use = ae.getArrayOffset() and
|
||||
not boundsChecked(v, use) and
|
||||
@@ -54,7 +54,7 @@ predicate dangerousUse(IntegralReturnValue val, Expr use) {
|
||||
val = use and
|
||||
use.getType().getUnderlyingType() instanceof PointerType
|
||||
or
|
||||
exists(LocalScopeVariable v, ControlFlowNode def, AddExpr add |
|
||||
exists(StackVariable v, ControlFlowNode def, AddExpr add |
|
||||
exprDefinition(v, def, val) and
|
||||
definitionUsePair(v, def, use) and
|
||||
add.getAnOperand() = use and
|
||||
|
||||
@@ -60,7 +60,7 @@ predicate allocExprOrIndirect(Expr alloc, string kind) {
|
||||
pragma[nomagic]
|
||||
private predicate allocReachesVariable(Variable v, Expr alloc, string kind) {
|
||||
exists(Expr mid |
|
||||
not v instanceof LocalScopeVariable and
|
||||
not v instanceof StackVariable and
|
||||
v.getAnAssignedValue() = mid and
|
||||
allocReaches0(mid, alloc, kind)
|
||||
)
|
||||
@@ -76,7 +76,7 @@ private predicate allocReaches0(Expr e, Expr alloc, string kind) {
|
||||
allocExprOrIndirect(alloc, kind) and
|
||||
e = alloc
|
||||
or
|
||||
exists(SsaDefinition def, LocalScopeVariable v |
|
||||
exists(SsaDefinition def, StackVariable v |
|
||||
// alloc via SSA
|
||||
allocReaches0(def.getAnUltimateDefiningValue(v), alloc, kind) and
|
||||
e = def.getAUse(v)
|
||||
|
||||
@@ -11,26 +11,16 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
class MallocCall extends FunctionCall {
|
||||
MallocCall() { this.getTarget().hasGlobalOrStdName("malloc") }
|
||||
|
||||
Expr getAllocatedSize() {
|
||||
if this.getArgument(0) instanceof VariableAccess
|
||||
then
|
||||
exists(LocalScopeVariable v, ControlFlowNode def |
|
||||
definitionUsePair(v, def, this.getArgument(0)) and
|
||||
exprDefinition(v, def, result)
|
||||
)
|
||||
else result = this.getArgument(0)
|
||||
}
|
||||
}
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.Allocation
|
||||
|
||||
predicate spaceProblem(FunctionCall append, string msg) {
|
||||
exists(MallocCall malloc, StrlenCall strlen, AddExpr add, FunctionCall insert, Variable buffer |
|
||||
exists(
|
||||
AllocationExpr malloc, StrlenCall strlen, AddExpr add, FunctionCall insert, Variable buffer
|
||||
|
|
||||
add.getAChild() = strlen and
|
||||
exists(add.getAChild().getValue()) and
|
||||
malloc.getAllocatedSize() = add and
|
||||
DataFlow::localExprFlow(add, malloc.getSizeExpr()) and
|
||||
buffer.getAnAccess() = strlen.getStringExpr() and
|
||||
(
|
||||
insert.getTarget().hasGlobalOrStdName("strcpy") or
|
||||
@@ -43,7 +33,8 @@ predicate spaceProblem(FunctionCall append, string msg) {
|
||||
malloc.getASuccessor+() = insert and
|
||||
insert.getArgument(1) = buffer.getAnAccess() and
|
||||
insert.getASuccessor+() = append and
|
||||
msg = "This buffer only contains enough room for '" + buffer.getName() + "' (copied on line " +
|
||||
msg =
|
||||
"This buffer only contains enough room for '" + buffer.getName() + "' (copied on line " +
|
||||
insert.getLocation().getStartLine().toString() + ")"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -51,7 +51,8 @@ predicate overflowOffsetInLoop(BufferAccess bufaccess, string msg) {
|
||||
loop.getStmt().getAChild*() = bufaccess.getEnclosingStmt() and
|
||||
loop.limit() >= bufaccess.bufferSize() and
|
||||
loop.counter().getAnAccess() = bufaccess.getArrayOffset() and
|
||||
msg = "Potential buffer-overflow: counter '" + loop.counter().toString() + "' <= " +
|
||||
msg =
|
||||
"Potential buffer-overflow: counter '" + loop.counter().toString() + "' <= " +
|
||||
loop.limit().toString() + " but '" + bufaccess.buffer().getName() + "' has " +
|
||||
bufaccess.bufferSize().toString() + " elements."
|
||||
)
|
||||
@@ -106,8 +107,9 @@ predicate wrongBufferSize(Expr error, string msg) {
|
||||
statedSize = min(call.statedSizeValue()) and
|
||||
statedSize > bufsize and
|
||||
error = call.statedSizeExpr() and
|
||||
msg = "Potential buffer-overflow: '" + buf.getName() + "' has size " + bufsize.toString() +
|
||||
" not " + statedSize + "."
|
||||
msg =
|
||||
"Potential buffer-overflow: '" + buf.getName() + "' has size " + bufsize.toString() + " not " +
|
||||
statedSize + "."
|
||||
)
|
||||
}
|
||||
|
||||
@@ -121,8 +123,9 @@ predicate outOfBounds(BufferAccess bufaccess, string msg) {
|
||||
or
|
||||
access = size and not exists(AddressOfExpr addof | bufaccess = addof.getOperand())
|
||||
) and
|
||||
msg = "Potential buffer-overflow: '" + buf + "' has size " + size.toString() + " but '" + buf +
|
||||
"[" + access.toString() + "]' is accessed here."
|
||||
msg =
|
||||
"Potential buffer-overflow: '" + buf + "' has size " + size.toString() + " but '" + buf + "[" +
|
||||
access.toString() + "]' is accessed here."
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -20,11 +20,10 @@ class ReturnPointsToExpr extends PointsToExpr {
|
||||
ReturnStmt getReturnStmt() { result.getExpr().getFullyConverted() = this }
|
||||
}
|
||||
|
||||
from ReturnPointsToExpr ret, LocalVariable local, float confidence
|
||||
from ReturnPointsToExpr ret, StackVariable local, float confidence
|
||||
where
|
||||
ret.pointsTo() = local and
|
||||
ret.getReturnStmt().getEnclosingFunction() = local.getFunction() and
|
||||
not local.isStatic() and
|
||||
confidence = ret.confidence() and
|
||||
confidence > 0.01
|
||||
select ret,
|
||||
|
||||
@@ -23,7 +23,8 @@ predicate important(Function f, string message) {
|
||||
predicate dubious(Function f, string message) {
|
||||
not important(f, _) and
|
||||
exists(Options opts, int used, int total, int percentage |
|
||||
used = count(FunctionCall fc |
|
||||
used =
|
||||
count(FunctionCall fc |
|
||||
fc.getTarget() = f and not opts.okToIgnoreReturnValue(fc) and not unused(fc)
|
||||
) and
|
||||
total = count(FunctionCall fc | fc.getTarget() = f and not opts.okToIgnoreReturnValue(fc)) and
|
||||
|
||||
@@ -20,11 +20,10 @@ class ScopeUtilityClass extends Class {
|
||||
Call getAUse() { result = this.getAConstructor().getACallToThisFunction() }
|
||||
}
|
||||
|
||||
from LocalScopeVariable v, ControlFlowNode def
|
||||
from StackVariable v, ControlFlowNode def
|
||||
where
|
||||
definition(v, def) and
|
||||
not definitionUsePair(v, def, _) and
|
||||
not v.isStatic() and
|
||||
not v.getAnAccess().isAddressOfAccess() and
|
||||
// parameter initializers are not in the call-graph at the moment
|
||||
not v.(Parameter).getInitializer().getExpr() = def and
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.LocalScopeVariableReachability
|
||||
import semmle.code.cpp.controlflow.StackVariableReachability
|
||||
|
||||
/** `e` is an expression that frees the memory pointed to by `v`. */
|
||||
predicate isFreeExpr(Expr e, LocalScopeVariable v) {
|
||||
predicate isFreeExpr(Expr e, StackVariable v) {
|
||||
exists(VariableAccess va | va.getTarget() = v |
|
||||
exists(FunctionCall fc | fc = e |
|
||||
fc.getTarget().hasGlobalOrStdName("free") and
|
||||
@@ -27,7 +27,7 @@ predicate isFreeExpr(Expr e, LocalScopeVariable v) {
|
||||
}
|
||||
|
||||
/** `e` is an expression that (may) dereference `v`. */
|
||||
predicate isDerefExpr(Expr e, LocalScopeVariable v) {
|
||||
predicate isDerefExpr(Expr e, StackVariable v) {
|
||||
v.getAnAccess() = e and dereferenced(e)
|
||||
or
|
||||
isDerefByCallExpr(_, _, e, v)
|
||||
@@ -39,27 +39,27 @@ predicate isDerefExpr(Expr e, LocalScopeVariable v) {
|
||||
* or a source code function that dereferences the relevant
|
||||
* parameter.
|
||||
*/
|
||||
predicate isDerefByCallExpr(Call c, int i, VariableAccess va, LocalScopeVariable v) {
|
||||
predicate isDerefByCallExpr(Call c, int i, VariableAccess va, StackVariable v) {
|
||||
v.getAnAccess() = va and
|
||||
va = c.getAnArgumentSubExpr(i) and
|
||||
not c.passesByReference(i, va) and
|
||||
(c.getTarget().hasEntryPoint() implies isDerefExpr(_, c.getTarget().getParameter(i)))
|
||||
}
|
||||
|
||||
class UseAfterFreeReachability extends LocalScopeVariableReachability {
|
||||
class UseAfterFreeReachability extends StackVariableReachability {
|
||||
UseAfterFreeReachability() { this = "UseAfterFree" }
|
||||
|
||||
override predicate isSource(ControlFlowNode node, LocalScopeVariable v) { isFreeExpr(node, v) }
|
||||
override predicate isSource(ControlFlowNode node, StackVariable v) { isFreeExpr(node, v) }
|
||||
|
||||
override predicate isSink(ControlFlowNode node, LocalScopeVariable v) { isDerefExpr(node, v) }
|
||||
override predicate isSink(ControlFlowNode node, StackVariable v) { isDerefExpr(node, v) }
|
||||
|
||||
override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isBarrier(ControlFlowNode node, StackVariable v) {
|
||||
definitionBarrier(v, node) or
|
||||
isFreeExpr(node, v)
|
||||
}
|
||||
}
|
||||
|
||||
from UseAfterFreeReachability r, LocalScopeVariable v, Expr free, Expr e
|
||||
from UseAfterFreeReachability r, StackVariable v, Expr free, Expr e
|
||||
where r.reaches(free, v, e)
|
||||
select e, "Memory pointed to by '" + v.getName().toString() + "' may have been previously freed $@",
|
||||
free, "here"
|
||||
|
||||
@@ -18,14 +18,13 @@ string getCommentTextCaptioned(Comment c, string caption) {
|
||||
dontCare = commentBody.regexpFind("\\n[/* \\t\\x0B\\f\\r]*" + caption, _, offset) and
|
||||
interestingSuffix = commentBody.suffix(offset) and
|
||||
endOfLine = interestingSuffix.indexOf("\n", 1, 0) and
|
||||
captionedLine = interestingSuffix
|
||||
captionedLine =
|
||||
interestingSuffix
|
||||
.prefix(endOfLine)
|
||||
.regexpReplaceAll("^[/*\\s]*" + caption + "\\s*:?", "")
|
||||
.trim() and
|
||||
followingLine = interestingSuffix
|
||||
.prefix(interestingSuffix.indexOf("\n", 2, 0))
|
||||
.suffix(endOfLine)
|
||||
.trim() and
|
||||
followingLine =
|
||||
interestingSuffix.prefix(interestingSuffix.indexOf("\n", 2, 0)).suffix(endOfLine).trim() and
|
||||
if captionedLine = ""
|
||||
then result = caption + " comment"
|
||||
else
|
||||
|
||||
@@ -56,7 +56,8 @@ VariableAccess getAnIncrement(Variable var) {
|
||||
exists(AssignAddExpr a | a.getLValue() = result and a.getRValue().getValue().toInt() > 0)
|
||||
or
|
||||
exists(AssignExpr a | a.getLValue() = result |
|
||||
a.getRValue() = any(AddExpr ae |
|
||||
a.getRValue() =
|
||||
any(AddExpr ae |
|
||||
ae.getAnOperand() = var.getAnAccess() and
|
||||
ae.getAnOperand().getValue().toInt() > 0
|
||||
)
|
||||
@@ -72,7 +73,8 @@ VariableAccess getADecrement(Variable var) {
|
||||
exists(AssignSubExpr a | a.getLValue() = result and a.getRValue().getValue().toInt() > 0)
|
||||
or
|
||||
exists(AssignExpr a | a.getLValue() = result |
|
||||
a.getRValue() = any(SubExpr ae |
|
||||
a.getRValue() =
|
||||
any(SubExpr ae |
|
||||
ae.getLeftOperand() = var.getAnAccess() and
|
||||
ae.getRightOperand().getValue().toInt() > 0
|
||||
)
|
||||
@@ -128,14 +130,16 @@ where
|
||||
exists(VariableAccess bound |
|
||||
upperBoundCheck(loop, bound) and
|
||||
reachesNoInc(bound, bound) and
|
||||
msg = "The loop counter " + bound.getTarget().getName() +
|
||||
msg =
|
||||
"The loop counter " + bound.getTarget().getName() +
|
||||
" is not always incremented in the loop body."
|
||||
)
|
||||
or
|
||||
exists(VariableAccess bound |
|
||||
lowerBoundCheck(loop, bound) and
|
||||
reachesNoDec(bound, bound) and
|
||||
msg = "The loop counter " + bound.getTarget().getName() +
|
||||
msg =
|
||||
"The loop counter " + bound.getTarget().getName() +
|
||||
" is not always decremented in the loop body."
|
||||
)
|
||||
)
|
||||
|
||||
@@ -21,6 +21,7 @@ where
|
||||
if call.getTarget() = call.getEnclosingFunction()
|
||||
then msg = "This call directly invokes its containing function $@."
|
||||
else
|
||||
msg = "The function " + call.getEnclosingFunction() +
|
||||
msg =
|
||||
"The function " + call.getEnclosingFunction() +
|
||||
" is indirectly recursive via this call to $@."
|
||||
select call, msg, call.getTarget(), call.getTarget().getName()
|
||||
|
||||
@@ -17,7 +17,8 @@ predicate lockOrder(LockOperation outer, LockOperation inner) {
|
||||
}
|
||||
|
||||
int orderCount(Declaration outerLock, Declaration innerLock) {
|
||||
result = strictcount(LockOperation outer, LockOperation inner |
|
||||
result =
|
||||
strictcount(LockOperation outer, LockOperation inner |
|
||||
outer.getLocked() = outerLock and
|
||||
inner.getLocked() = innerLock and
|
||||
lockOrder(outer, inner)
|
||||
@@ -27,6 +28,6 @@ int orderCount(Declaration outerLock, Declaration innerLock) {
|
||||
from LockOperation outer, LockOperation inner
|
||||
where
|
||||
lockOrder(outer, inner) and
|
||||
orderCount(outer.getLocked(), inner.getLocked()) <= orderCount(inner.getLocked(),
|
||||
outer.getLocked())
|
||||
orderCount(outer.getLocked(), inner.getLocked()) <=
|
||||
orderCount(inner.getLocked(), outer.getLocked())
|
||||
select inner, "Out-of-order locks: A " + inner.say() + " usually precedes a $@.", outer, outer.say()
|
||||
|
||||
@@ -81,10 +81,8 @@ class LockingPrimitive extends FunctionCall, LockOperation {
|
||||
override Function getLocked() { result = this.getTarget() }
|
||||
|
||||
override UnlockOperation getMatchingUnlock() {
|
||||
result.(UnlockingPrimitive).getTarget().getName() = this
|
||||
.getTarget()
|
||||
.getName()
|
||||
.replaceAll("Lock", "Unlock")
|
||||
result.(UnlockingPrimitive).getTarget().getName() =
|
||||
this.getTarget().getName().replaceAll("Lock", "Unlock")
|
||||
}
|
||||
|
||||
override string say() { result = "call to " + getLocked().getName() }
|
||||
|
||||
@@ -29,7 +29,8 @@ where
|
||||
numStmt(f, line) = cnt and
|
||||
cnt > 1 and
|
||||
o.onLine(f, line) and
|
||||
o.getLocation().getStartColumn() = min(OneLineStmt other, int toMin |
|
||||
o.getLocation().getStartColumn() =
|
||||
min(OneLineStmt other, int toMin |
|
||||
other.onLine(f, line) and toMin = other.getLocation().getStartColumn()
|
||||
|
|
||||
toMin
|
||||
|
||||
@@ -14,7 +14,8 @@ import cpp
|
||||
string var(Variable v) {
|
||||
exists(int level | level = v.getType().getPointerIndirectionLevel() |
|
||||
level > 2 and
|
||||
result = "The type of " + v.getName() + " uses " + level +
|
||||
result =
|
||||
"The type of " + v.getName() + " uses " + level +
|
||||
" levels of pointer indirection -- maximum allowed is 2."
|
||||
)
|
||||
}
|
||||
@@ -22,7 +23,8 @@ string var(Variable v) {
|
||||
string fun(Function f) {
|
||||
exists(int level | level = f.getType().getPointerIndirectionLevel() |
|
||||
level > 2 and
|
||||
result = "The return type of " + f.getName() + " uses " + level +
|
||||
result =
|
||||
"The return type of " + f.getName() + " uses " + level +
|
||||
" levels of pointer indirection -- maximum allowed is 2."
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
import cpp
|
||||
|
||||
int firstCodeLine(File f) {
|
||||
result = min(Declaration d, Location l, int toMin |
|
||||
result =
|
||||
min(Declaration d, Location l, int toMin |
|
||||
(
|
||||
l = d.getLocation() and
|
||||
l.getFile() = f and
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.SSA
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
|
||||
/**
|
||||
* Holds if `e` is either:
|
||||
@@ -57,13 +59,122 @@ Expr getMulOperand(MulExpr me) { result = me.getAnOperand() }
|
||||
* ```
|
||||
*/
|
||||
int getEffectiveMulOperands(MulExpr me) {
|
||||
result = count(Expr op |
|
||||
result =
|
||||
count(Expr op |
|
||||
op = getMulOperand*(me) and
|
||||
not op instanceof MulExpr and
|
||||
not likelySmall(op)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* As SimpleRangeAnalysis does not support reasoning about multiplication
|
||||
* we create a tiny abstract interpreter for handling multiplication, which
|
||||
* we invoke only after weeding out of all of trivial cases that we do
|
||||
* not care about. By default, the maximum and minimum values are computed
|
||||
* using SimpleRangeAnalysis.
|
||||
*/
|
||||
class AnalyzableExpr extends Expr {
|
||||
float maxValue() { result = upperBound(this.getFullyConverted()) }
|
||||
|
||||
float minValue() { result = lowerBound(this.getFullyConverted()) }
|
||||
}
|
||||
|
||||
class ParenAnalyzableExpr extends AnalyzableExpr, ParenthesisExpr {
|
||||
override float maxValue() { result = this.getExpr().(AnalyzableExpr).maxValue() }
|
||||
|
||||
override float minValue() { result = this.getExpr().(AnalyzableExpr).minValue() }
|
||||
}
|
||||
|
||||
class MulAnalyzableExpr extends AnalyzableExpr, MulExpr {
|
||||
override float maxValue() {
|
||||
exists(float x1, float y1, float x2, float y2 |
|
||||
x1 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() and
|
||||
x2 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() and
|
||||
y1 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue() and
|
||||
y2 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue() and
|
||||
result = (x1 * y1).maximum(x1 * y2).maximum(x2 * y1).maximum(x2 * y2)
|
||||
)
|
||||
}
|
||||
|
||||
override float minValue() {
|
||||
exists(float x1, float x2, float y1, float y2 |
|
||||
x1 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() and
|
||||
x2 = this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() and
|
||||
y1 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue() and
|
||||
y2 = this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue() and
|
||||
result = (x1 * y1).minimum(x1 * y2).minimum(x2 * y1).minimum(x2 * y2)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class AddAnalyzableExpr extends AnalyzableExpr, AddExpr {
|
||||
override float maxValue() {
|
||||
result =
|
||||
this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() +
|
||||
this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue()
|
||||
}
|
||||
|
||||
override float minValue() {
|
||||
result =
|
||||
this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() +
|
||||
this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue()
|
||||
}
|
||||
}
|
||||
|
||||
class SubAnalyzableExpr extends AnalyzableExpr, SubExpr {
|
||||
override float maxValue() {
|
||||
result =
|
||||
this.getLeftOperand().getFullyConverted().(AnalyzableExpr).maxValue() -
|
||||
this.getRightOperand().getFullyConverted().(AnalyzableExpr).minValue()
|
||||
}
|
||||
|
||||
override float minValue() {
|
||||
result =
|
||||
this.getLeftOperand().getFullyConverted().(AnalyzableExpr).minValue() -
|
||||
this.getRightOperand().getFullyConverted().(AnalyzableExpr).maxValue()
|
||||
}
|
||||
}
|
||||
|
||||
class VarAnalyzableExpr extends AnalyzableExpr, VariableAccess {
|
||||
VarAnalyzableExpr() { this.getTarget() instanceof StackVariable }
|
||||
|
||||
override float maxValue() {
|
||||
exists(SsaDefinition def, Variable v |
|
||||
def.getAUse(v) = this and
|
||||
// if there is a defining expression, use that for
|
||||
// computing the maximum value. Otherwise, assign the
|
||||
// variable the largest possible value it can hold
|
||||
if exists(def.getDefiningValue(v))
|
||||
then result = def.getDefiningValue(v).(AnalyzableExpr).maxValue()
|
||||
else result = upperBound(this)
|
||||
)
|
||||
}
|
||||
|
||||
override float minValue() {
|
||||
exists(SsaDefinition def, Variable v |
|
||||
def.getAUse(v) = this and
|
||||
if exists(def.getDefiningValue(v))
|
||||
then result = def.getDefiningValue(v).(AnalyzableExpr).minValue()
|
||||
else result = lowerBound(this)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `t` is not an instance of `IntegralType`,
|
||||
* or if `me` cannot be proven to not overflow
|
||||
*/
|
||||
predicate overflows(MulExpr me, Type t) {
|
||||
t instanceof IntegralType
|
||||
implies
|
||||
(
|
||||
me.(MulAnalyzableExpr).maxValue() > exprMaxVal(me)
|
||||
or
|
||||
me.(MulAnalyzableExpr).minValue() < exprMinVal(me)
|
||||
)
|
||||
}
|
||||
|
||||
from MulExpr me, Type t1, Type t2
|
||||
where
|
||||
t1 = me.getType().getUnderlyingType() and
|
||||
@@ -101,7 +212,11 @@ where
|
||||
e = other.(BinaryOperation).getAnOperand*()
|
||||
) and
|
||||
e.(Literal).getType().getSize() = t2.getSize()
|
||||
)
|
||||
) and
|
||||
// only report if we cannot prove that the result of the
|
||||
// multiplication will be less (resp. greater) than the
|
||||
// maximum (resp. minimum) number we can compute.
|
||||
overflows(me, t1)
|
||||
select me,
|
||||
"Multiplication result may overflow '" + me.getType().toString() + "' before it is converted to '"
|
||||
+ me.getFullyConverted().getType().toString() + "'."
|
||||
|
||||
@@ -52,10 +52,7 @@ predicate introducesNewField(Class derived, Class base) {
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, CastToPointerArithFlow cfg
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
source.getNode().asExpr().getFullyConverted().getUnspecifiedType() = sink
|
||||
.getNode()
|
||||
.asExpr()
|
||||
.getFullyConverted()
|
||||
.getUnspecifiedType()
|
||||
source.getNode().asExpr().getFullyConverted().getUnspecifiedType() =
|
||||
sink.getNode().asExpr().getFullyConverted().getUnspecifiedType()
|
||||
select sink, source, sink,
|
||||
"Pointer arithmetic here may be done with the wrong type because of the cast $@.", source, "here"
|
||||
|
||||
@@ -37,7 +37,7 @@ predicate flowsToExprImpl(Expr source, Expr sink, boolean pathMightOverflow) {
|
||||
pathMightOverflow = false and
|
||||
source.(FunctionCall).getTarget().(Snprintf).returnsFullFormatLength()
|
||||
or
|
||||
exists(RangeSsaDefinition def, LocalScopeVariable v |
|
||||
exists(RangeSsaDefinition def, StackVariable v |
|
||||
flowsToDef(source, def, v, pathMightOverflow) and
|
||||
sink = def.getAUse(v)
|
||||
)
|
||||
@@ -63,9 +63,7 @@ predicate flowsToExprImpl(Expr source, Expr sink, boolean pathMightOverflow) {
|
||||
* `pathMightOverflow` is true if there is an arithmetic operation
|
||||
* on the path that might overflow.
|
||||
*/
|
||||
predicate flowsToDef(
|
||||
Expr source, RangeSsaDefinition def, LocalScopeVariable v, boolean pathMightOverflow
|
||||
) {
|
||||
predicate flowsToDef(Expr source, RangeSsaDefinition def, StackVariable v, boolean pathMightOverflow) {
|
||||
// Might the current definition overflow?
|
||||
exists(boolean otherMightOverflow | flowsToDefImpl(source, def, v, otherMightOverflow) |
|
||||
if defMightOverflow(def, v)
|
||||
@@ -86,7 +84,7 @@ predicate flowsToDef(
|
||||
* the path. But it is a good way to reduce the number of false positives.
|
||||
*/
|
||||
predicate flowsToDefImpl(
|
||||
Expr source, RangeSsaDefinition def, LocalScopeVariable v, boolean pathMightOverflow
|
||||
Expr source, RangeSsaDefinition def, StackVariable v, boolean pathMightOverflow
|
||||
) {
|
||||
// Assignment or initialization: `e = v;`
|
||||
exists(Expr e |
|
||||
|
||||
@@ -130,11 +130,8 @@ predicate trivialConversion(ExpectedType expected, Type actual) {
|
||||
or
|
||||
// allow a pointer to any integral type of the same size
|
||||
// (this permits signedness changes)
|
||||
expected.(PointerType).getBaseType().(IntegralType).getSize() = actual
|
||||
.(PointerType)
|
||||
.getBaseType()
|
||||
.(IntegralType)
|
||||
.getSize()
|
||||
expected.(PointerType).getBaseType().(IntegralType).getSize() =
|
||||
actual.(PointerType).getBaseType().(IntegralType).getSize()
|
||||
or
|
||||
expected = actual
|
||||
)
|
||||
|
||||
@@ -25,10 +25,16 @@ predicate assertInvocation(File f, int line) {
|
||||
)
|
||||
}
|
||||
|
||||
predicate nullCheckAssert(Expr e, Variable v, Declaration qualifier) {
|
||||
nullCheckInCondition(e, v, qualifier) and
|
||||
class InterestingExpr extends Expr {
|
||||
InterestingExpr() { nullCheckInCondition(this, _, _) }
|
||||
}
|
||||
|
||||
predicate nullCheckAssert(InterestingExpr e, Variable v, Declaration qualifier) {
|
||||
exists(File f, int i |
|
||||
e.getLocation().getStartLine() = i and e.getFile() = f and assertInvocation(f, i)
|
||||
e.getLocation().getStartLine() = i and
|
||||
e.getFile() = f and
|
||||
assertInvocation(f, i) and
|
||||
nullCheckInCondition(e, v, qualifier)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
* @id cpp/leap-year/adding-365-days-per-year
|
||||
* @precision medium
|
||||
* @tags leap-year
|
||||
* correctness
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* @id cpp/leap-year/unchecked-after-arithmetic-year-modification
|
||||
* @precision medium
|
||||
* @tags leap-year
|
||||
* correctness
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
* @id cpp/leap-year/unchecked-return-value-for-time-conversion-function
|
||||
* @precision medium
|
||||
* @tags leap-year
|
||||
* correctness
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id cpp/leap-year/unsafe-array-for-days-of-the-year
|
||||
* @precision medium
|
||||
* @precision low
|
||||
* @tags security
|
||||
* leap-year
|
||||
*/
|
||||
|
||||
@@ -12,24 +12,24 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.LocalScopeVariableReachability
|
||||
import semmle.code.cpp.controlflow.StackVariableReachability
|
||||
|
||||
class UndefReachability extends LocalScopeVariableReachability {
|
||||
class UndefReachability extends StackVariableReachability {
|
||||
UndefReachability() { this = "UndefReachability" }
|
||||
|
||||
override predicate isSource(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isSource(ControlFlowNode node, StackVariable v) {
|
||||
candidateVariable(v) and
|
||||
node = v.getParentScope() and
|
||||
not v instanceof Parameter and
|
||||
not v.hasInitializer()
|
||||
}
|
||||
|
||||
override predicate isSink(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isSink(ControlFlowNode node, StackVariable v) {
|
||||
candidateVariable(v) and
|
||||
node = v.getAnAccess()
|
||||
}
|
||||
|
||||
override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isBarrier(ControlFlowNode node, StackVariable v) {
|
||||
node.(AssignExpr).getLValue() = v.getAnAccess()
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,12 @@ abstract class BooleanControllingAssignment extends AssignExpr {
|
||||
abstract predicate isWhitelisted();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an operand of a logical operation expression (we need the restriction
|
||||
* to BinaryLogicalOperation expressions to get the correct transitive closure).
|
||||
*/
|
||||
Expr getComparisonOperand(BinaryLogicalOperation op) { result = op.getAnOperand() }
|
||||
|
||||
class BooleanControllingAssignmentInExpr extends BooleanControllingAssignment {
|
||||
BooleanControllingAssignmentInExpr() {
|
||||
this.getParent() instanceof UnaryLogicalOperation or
|
||||
@@ -45,7 +51,18 @@ class BooleanControllingAssignmentInExpr extends BooleanControllingAssignment {
|
||||
exists(ConditionalExpr c | c.getCondition() = this)
|
||||
}
|
||||
|
||||
override predicate isWhitelisted() { this.getConversion().(ParenthesisExpr).isParenthesised() }
|
||||
override predicate isWhitelisted() {
|
||||
this.getConversion().(ParenthesisExpr).isParenthesised()
|
||||
or
|
||||
// whitelist this assignment if all comparison operations in the expression that this
|
||||
// assignment is part of, are not parenthesized. In that case it seems like programmer
|
||||
// is fine with unparenthesized comparison operands to binary logical operators, and
|
||||
// the parenthesis around this assignment was used to call it out as an assignment.
|
||||
this.isParenthesised() and
|
||||
forex(ComparisonOperation op | op = getComparisonOperand*(this.getParent+()) |
|
||||
not op.isParenthesised()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class BooleanControllingAssignmentInStmt extends BooleanControllingAssignment {
|
||||
@@ -65,7 +82,8 @@ class BooleanControllingAssignmentInStmt extends BooleanControllingAssignment {
|
||||
*/
|
||||
predicate candidateResult(BooleanControllingAssignment ae) {
|
||||
ae.getRValue().isConstant() and
|
||||
not ae.isWhitelisted()
|
||||
not ae.isWhitelisted() and
|
||||
not ae.getRValue() instanceof StringLiteral
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,5 +99,6 @@ predicate candidateVariable(Variable v) {
|
||||
from BooleanControllingAssignment ae, UndefReachability undef
|
||||
where
|
||||
candidateResult(ae) and
|
||||
not ae.isFromUninstantiatedTemplate(_) and
|
||||
not undef.reaches(_, ae.getLValue().(VariableAccess).getTarget(), ae.getLValue())
|
||||
select ae, "Use of '=' where '==' may have been intended."
|
||||
|
||||
@@ -15,7 +15,10 @@ import cpp
|
||||
|
||||
from ExprInVoidContext op
|
||||
where
|
||||
op instanceof EQExpr
|
||||
or
|
||||
op.(FunctionCall).getTarget().hasName("operator==")
|
||||
not op.isUnevaluated() and
|
||||
(
|
||||
op instanceof EQExpr
|
||||
or
|
||||
op.(FunctionCall).getTarget().hasName("operator==")
|
||||
)
|
||||
select op, "This '==' operator has no effect. The assignment ('=') operator was probably intended."
|
||||
|
||||
@@ -65,11 +65,8 @@ predicate functionDefinedInIfDefRecursive(Function f) {
|
||||
* break encapsulation.
|
||||
*/
|
||||
predicate baseCall(FunctionCall call) {
|
||||
call.getNameQualifier().getQualifyingElement() = call
|
||||
.getEnclosingFunction()
|
||||
.getDeclaringType()
|
||||
.(Class)
|
||||
.getABaseClass+()
|
||||
call.getNameQualifier().getQualifyingElement() =
|
||||
call.getEnclosingFunction().getDeclaringType().(Class).getABaseClass+()
|
||||
}
|
||||
|
||||
from PureExprInVoidContext peivc, Locatable parent, Locatable info, string info_text, string tail
|
||||
@@ -84,8 +81,10 @@ where
|
||||
not peivc.getEnclosingFunction().isDefaulted() and
|
||||
not exists(Macro m | peivc = m.getAnInvocation().getAnExpandedElement()) and
|
||||
not peivc.isFromTemplateInstantiation(_) and
|
||||
not peivc.isFromUninstantiatedTemplate(_) and
|
||||
parent = peivc.getParent() and
|
||||
not parent.isInMacroExpansion() and
|
||||
not peivc.isUnevaluated() and
|
||||
not parent instanceof PureExprInVoidContext and
|
||||
not peivc.getEnclosingFunction().isCompilerGenerated() and
|
||||
not peivc.getType() instanceof UnknownType and
|
||||
|
||||
@@ -35,7 +35,8 @@ predicate booleanLiteral(Literal l) {
|
||||
string boolLiteralInLogicalOp(Literal literal) {
|
||||
booleanLiteral(literal) and
|
||||
literal.getParent() instanceof BinaryLogicalOperation and
|
||||
result = "Literal value " + literal.getValueText() +
|
||||
result =
|
||||
"Literal value " + literal.getValueText() +
|
||||
" is used in a logical expression; simplify or use a constant."
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,8 @@ where
|
||||
l = v.log2().floor() and
|
||||
if v = 2.pow(l)
|
||||
then
|
||||
msg = "Operand to short-circuiting operator looks like a flag (" + v + " = 2 ^ " + l +
|
||||
msg =
|
||||
"Operand to short-circuiting operator looks like a flag (" + v + " = 2 ^ " + l +
|
||||
"), may be typo for bitwise operator."
|
||||
else
|
||||
exists(string kind |
|
||||
@@ -49,7 +50,8 @@ where
|
||||
or
|
||||
e instanceof OctalLiteral and kind = "an octal literal"
|
||||
) and
|
||||
msg = "Operand to short-circuiting operator is " + kind +
|
||||
msg =
|
||||
"Operand to short-circuiting operator is " + kind +
|
||||
", and therefore likely a flag; a bitwise operator may be intended."
|
||||
)
|
||||
select e, msg
|
||||
|
||||
@@ -63,7 +63,8 @@ predicate isStringCopyUsedInLogicalOperationOrCondition(FunctionCall func, Expr
|
||||
func = ce.getCondition()
|
||||
)
|
||||
) and
|
||||
msg = "Return value of " + func.getTarget().getName() +
|
||||
msg =
|
||||
"Return value of " + func.getTarget().getName() +
|
||||
" used directly in a conditional expression."
|
||||
)
|
||||
}
|
||||
|
||||
@@ -111,17 +111,20 @@ predicate illDefinedForStmt(ForStmt for, string message) {
|
||||
illDefinedForStmtWrongDirection(for, v, initialCondition, terminalCondition, isIncr) and
|
||||
if for.conditionAlwaysFalse()
|
||||
then
|
||||
message = "Ill-defined for-loop: a loop using variable \"" + v + "\" counts " +
|
||||
message =
|
||||
"Ill-defined for-loop: a loop using variable \"" + v + "\" counts " +
|
||||
forLoopdirection(isIncr) + " from a value (" + initialCondition +
|
||||
"), but the terminal condition is always false."
|
||||
else
|
||||
if for.conditionAlwaysTrue()
|
||||
then
|
||||
message = "Ill-defined for-loop: a loop using variable \"" + v + "\" counts " +
|
||||
message =
|
||||
"Ill-defined for-loop: a loop using variable \"" + v + "\" counts " +
|
||||
forLoopdirection(isIncr) + " from a value (" + initialCondition +
|
||||
"), but the terminal condition is always true."
|
||||
else
|
||||
message = "Ill-defined for-loop: a loop using variable \"" + v + "\" counts " +
|
||||
message =
|
||||
"Ill-defined for-loop: a loop using variable \"" + v + "\" counts " +
|
||||
forLoopdirection(isIncr) + " from a value (" + initialCondition +
|
||||
"), but the terminal condition is " + forLoopTerminalConditionRelationship(isIncr) +
|
||||
" (" + terminalCondition + ")."
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.LocalScopeVariableReachability
|
||||
import semmle.code.cpp.controlflow.StackVariableReachability
|
||||
import semmle.code.cpp.commons.NullTermination
|
||||
|
||||
/**
|
||||
@@ -22,10 +22,10 @@ DeclStmt declWithNoInit(LocalVariable v) {
|
||||
not exists(v.getInitializer())
|
||||
}
|
||||
|
||||
class ImproperNullTerminationReachability extends LocalScopeVariableReachabilityWithReassignment {
|
||||
class ImproperNullTerminationReachability extends StackVariableReachabilityWithReassignment {
|
||||
ImproperNullTerminationReachability() { this = "ImproperNullTerminationReachability" }
|
||||
|
||||
override predicate isSourceActual(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isSourceActual(ControlFlowNode node, StackVariable v) {
|
||||
node = declWithNoInit(v)
|
||||
or
|
||||
exists(Call c, VariableAccess va |
|
||||
@@ -36,12 +36,12 @@ class ImproperNullTerminationReachability extends LocalScopeVariableReachability
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSinkActual(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isSinkActual(ControlFlowNode node, StackVariable v) {
|
||||
node.(VariableAccess).getTarget() = v and
|
||||
variableMustBeNullTerminated(node)
|
||||
}
|
||||
|
||||
override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isBarrier(ControlFlowNode node, StackVariable v) {
|
||||
exprDefinition(v, node, _) or
|
||||
mayAddNullTerminator(node, v.getAnAccess()) or
|
||||
isSinkActual(node, v) // only report first use
|
||||
|
||||
@@ -122,7 +122,7 @@ class MallocSizeExpr extends BufferAccess, FunctionCall {
|
||||
|
||||
override Expr getPointer() { none() }
|
||||
|
||||
override Expr getAccessedLength() { result = getArgument(1) }
|
||||
override Expr getAccessedLength() { result = getArgument(0) }
|
||||
}
|
||||
|
||||
class NetworkFunctionCall extends FunctionCall {
|
||||
|
||||
@@ -30,9 +30,9 @@ class SprintfCall extends FunctionCall {
|
||||
predicate isDangerous() { this.getMaxConvertedLength() > this.getBufferSize() }
|
||||
|
||||
string getDescription() {
|
||||
result = "This conversion may yield a string of length " +
|
||||
this.getMaxConvertedLength().toString() + ", which exceeds the allocated buffer size of " +
|
||||
this.getBufferSize().toString()
|
||||
result =
|
||||
"This conversion may yield a string of length " + this.getMaxConvertedLength().toString() +
|
||||
", which exceeds the allocated buffer size of " + this.getBufferSize().toString()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,10 +42,8 @@ predicate hasNontrivialConversion(Expr e) {
|
||||
hasNontrivialConversion(e.getConversion())
|
||||
}
|
||||
|
||||
from LocalScopeVariable var, VariableAccess va, ReturnStmt r
|
||||
from StackVariable var, VariableAccess va, ReturnStmt r
|
||||
where
|
||||
not var.isStatic() and
|
||||
not var.isThreadLocal() and
|
||||
not var.getUnspecifiedType() instanceof ReferenceType and
|
||||
not r.isFromUninstantiatedTemplate(_) and
|
||||
va = var.getAnAccess() and
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.LocalScopeVariableReachability
|
||||
import semmle.code.cpp.controlflow.StackVariableReachability
|
||||
|
||||
/**
|
||||
* Auxiliary predicate: Types that don't require initialization
|
||||
@@ -40,23 +40,17 @@ DeclStmt declWithNoInit(LocalVariable v) {
|
||||
result.getADeclaration() = v and
|
||||
not exists(v.getInitializer()) and
|
||||
/* The type of the variable is not stack-allocated. */
|
||||
not allocatedType(v.getType()) and
|
||||
/* The variable is not static (otherwise it is zeroed). */
|
||||
not v.isStatic() and
|
||||
/* The variable is not extern (otherwise it is zeroed). */
|
||||
not v.hasSpecifier("extern")
|
||||
not allocatedType(v.getType())
|
||||
}
|
||||
|
||||
class UninitialisedLocalReachability extends LocalScopeVariableReachability {
|
||||
class UninitialisedLocalReachability extends StackVariableReachability {
|
||||
UninitialisedLocalReachability() { this = "UninitialisedLocal" }
|
||||
|
||||
override predicate isSource(ControlFlowNode node, LocalScopeVariable v) {
|
||||
node = declWithNoInit(v)
|
||||
}
|
||||
override predicate isSource(ControlFlowNode node, StackVariable v) { node = declWithNoInit(v) }
|
||||
|
||||
override predicate isSink(ControlFlowNode node, LocalScopeVariable v) { useOfVarActual(v, node) }
|
||||
override predicate isSink(ControlFlowNode node, StackVariable v) { useOfVarActual(v, node) }
|
||||
|
||||
override predicate isBarrier(ControlFlowNode node, LocalScopeVariable v) {
|
||||
override predicate isBarrier(ControlFlowNode node, StackVariable v) {
|
||||
// only report the _first_ possibly uninitialized use
|
||||
useOfVarActual(v, node) or
|
||||
definitionBarrier(v, node)
|
||||
|
||||
@@ -3,9 +3,20 @@
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using TLS or SSLv23 protool from the boost::asio library, but not disabling deprecated protocols or disabling minimum-recommended protocols.</p>
|
||||
<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>
|
||||
|
||||
<recommendation>
|
||||
<p>When using the TLS or SSLv23 protocol, set the <code>no_tlsv1</code> and <code>no_tlsv1_1</code> options, but do not set <code>no_tlsv1_2</code>. When using the SSLv23 protocol, also set the <code>no_sslv3</code> option.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>In the following example, the <code>no_tlsv1_1</code> option has not been set. Use of TLS 1.1 is not recommended.</p>
|
||||
<sample src="TlsSettingsMisconfigurationBad.cpp"/>
|
||||
<p>In the corrected example, the <code>no_tlsv1</code> and <code>no_tlsv1_1</code> options have both been set, ensuring the use of TLS 1.2 or later.</p>
|
||||
<sample src="TlsSettingsMisconfigurationGood.cpp"/>
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
<a href="https://www.boost.org/doc/libs/1_71_0/doc/html/boost_asio.html">Boost.Asio documentation</a>.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @name Boost_asio TLS Settings Misconfiguration
|
||||
* @description Using TLS or SSLv23 protool from the boost::asio library, but not disabling deprecated protocols or disabling minimum-recommended protocols
|
||||
* @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
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
void useTLS_bad()
|
||||
{
|
||||
boost::asio::ssl::context ctx(boost::asio::ssl::context::tls);
|
||||
ctx.set_options(boost::asio::ssl::context::no_tlsv1); // BAD: missing no_tlsv1_1
|
||||
|
||||
// ...
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
void useTLS_good()
|
||||
{
|
||||
boost::asio::ssl::context ctx(boost::asio::ssl::context::tls);
|
||||
ctx.set_options(boost::asio::ssl::context::no_tlsv1 | boost::asio::ssl::context::no_tlsv1_1); // GOOD
|
||||
|
||||
// ...
|
||||
}
|
||||
@@ -4,13 +4,22 @@
|
||||
<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>
|
||||
|
||||
<recommendation>
|
||||
<p>Only use modern protocols such as TLS 1.2 or TLS 1.3.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>In the following example, the <code>sslv2</code> protocol is specified. This protocol is out of date and its use is not recommended.</p>
|
||||
<sample src="UseOfDeprecatedHardcodedProtocolBad.cpp"/>
|
||||
<p>In the corrected example, the <code>tlsv13</code> protocol is used instead.</p>
|
||||
<sample src="UseOfDeprecatedHardcodedProtocolGood.cpp"/>
|
||||
</example>
|
||||
|
||||
<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>
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
void useProtocol_bad()
|
||||
{
|
||||
boost::asio::ssl::context ctx_sslv2(boost::asio::ssl::context::sslv2); // BAD: outdated protocol
|
||||
|
||||
// ...
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
void useProtocol_good()
|
||||
{
|
||||
boost::asio::ssl::context cxt_tlsv13(boost::asio::ssl::context::tlsv13);
|
||||
|
||||
// ...
|
||||
}
|
||||
@@ -28,7 +28,8 @@ class NullInstruction extends ConstantValueInstruction {
|
||||
}
|
||||
|
||||
predicate explicitNullTestOfInstruction(Instruction checked, Instruction bool) {
|
||||
bool = any(CompareInstruction cmp |
|
||||
bool =
|
||||
any(CompareInstruction cmp |
|
||||
exists(NullInstruction null |
|
||||
cmp.getLeft() = null and cmp.getRight() = checked
|
||||
or
|
||||
@@ -40,7 +41,8 @@ predicate explicitNullTestOfInstruction(Instruction checked, Instruction bool) {
|
||||
)
|
||||
)
|
||||
or
|
||||
bool = any(ConvertInstruction convert |
|
||||
bool =
|
||||
any(ConvertInstruction convert |
|
||||
checked = convert.getUnary() and
|
||||
convert.getResultType() instanceof BoolType and
|
||||
checked.getResultType() instanceof PointerType
|
||||
|
||||
@@ -17,7 +17,9 @@ where
|
||||
hasSuperfluousConstReturn(f) and
|
||||
if f.hasSpecifier("const") or f.isStatic()
|
||||
then
|
||||
message = "The 'const' modifier has no effect on return types. The 'const' modifying the return type can be removed."
|
||||
message =
|
||||
"The 'const' modifier has no effect on return types. The 'const' modifying the return type can be removed."
|
||||
else
|
||||
message = "The 'const' modifier has no effect on return types. For a const function, the 'const' should go after the parameter list."
|
||||
message =
|
||||
"The 'const' modifier has no effect on return types. For a const function, the 'const' should go after the parameter list."
|
||||
select f, message
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/* '#include <stdlib.h>' was forgotton */
|
||||
|
||||
int main(void) {
|
||||
/* 'int malloc()' assumed */
|
||||
unsigned char *p = malloc(100);
|
||||
*p = 'a';
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>A function is called without a prior function declaration or definition.
|
||||
When this happens, the compiler generates an implicit declaration of the function,
|
||||
specifying an integer return type and no parameters.
|
||||
If the implicit declaration does not match the true signature of the function, the
|
||||
function may behave unpredictably.</p>
|
||||
|
||||
<p>This may indicate a misspelled function name, or that the required header containing
|
||||
the function declaration has not been included.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Provide an explicit declaration of the function before invoking it.</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="ImplicitFunctionDeclaration.c" />
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>SEI CERT C Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/DCL31-C.+Declare+identifiers+before+using+them">DCL31-C. Declare identifiers before using them</a></li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* @name Implicit function declaration
|
||||
* @description An implicitly declared function is assumed to take no
|
||||
* arguments and return an integer. If this assumption does not hold, it
|
||||
* may lead to unpredictable behavior.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @id cpp/implicit-function-declaration
|
||||
* @tags correctness
|
||||
* maintainability
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import MistypedFunctionArguments
|
||||
import TooFewArguments
|
||||
import TooManyArguments
|
||||
import semmle.code.cpp.commons.Exclusions
|
||||
|
||||
predicate locInfo(Locatable e, File file, int line, int col) {
|
||||
e.getFile() = file and
|
||||
e.getLocation().getStartLine() = line and
|
||||
e.getLocation().getStartColumn() = col
|
||||
}
|
||||
|
||||
predicate sameLocation(FunctionDeclarationEntry fde, FunctionCall fc) {
|
||||
exists(File file, int line, int col |
|
||||
locInfo(fde, file, line, col) and
|
||||
locInfo(fc, file, line, col)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isCompiledAsC(File f) {
|
||||
f.compiledAsC()
|
||||
or
|
||||
exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
|
||||
}
|
||||
|
||||
from FunctionDeclarationEntry fdeIm, FunctionCall fc
|
||||
where
|
||||
isCompiledAsC(fdeIm.getFile()) and
|
||||
not isFromMacroDefinition(fc) and
|
||||
fdeIm.isImplicit() and
|
||||
sameLocation(fdeIm, fc) and
|
||||
not mistypedFunctionArguments(fc, _, _) and
|
||||
not tooFewArguments(fc, _) and
|
||||
not tooManyArguments(fc, _)
|
||||
select fc, "Function call implicitly declares '" + fdeIm.getName() + "'."
|
||||
@@ -12,95 +12,10 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
predicate arithTypesMatch(Type arg, Type parm) {
|
||||
arg = parm
|
||||
or
|
||||
arg.getSize() = parm.getSize() and
|
||||
(
|
||||
arg instanceof IntegralOrEnumType and
|
||||
parm instanceof IntegralOrEnumType
|
||||
or
|
||||
arg instanceof FloatingPointType and
|
||||
parm instanceof FloatingPointType
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
predicate nestedPointerArgTypeMayBeUsed(Type arg, Type parm) {
|
||||
// arithmetic types
|
||||
arithTypesMatch(arg, parm)
|
||||
or
|
||||
// conversion to/from pointers to void is allowed
|
||||
arg instanceof VoidType
|
||||
or
|
||||
parm instanceof VoidType
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
predicate pointerArgTypeMayBeUsed(Type arg, Type parm) {
|
||||
nestedPointerArgTypeMayBeUsed(arg, parm)
|
||||
or
|
||||
// nested pointers
|
||||
nestedPointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
|
||||
parm.(PointerType).getBaseType().getUnspecifiedType())
|
||||
or
|
||||
nestedPointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
|
||||
parm.(PointerType).getBaseType().getUnspecifiedType())
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
predicate argTypeMayBeUsed(Type arg, Type parm) {
|
||||
// arithmetic types
|
||||
arithTypesMatch(arg, parm)
|
||||
or
|
||||
// pointers to compatible types
|
||||
pointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
|
||||
parm.(PointerType).getBaseType().getUnspecifiedType())
|
||||
or
|
||||
pointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
|
||||
parm.(PointerType).getBaseType().getUnspecifiedType())
|
||||
or
|
||||
// C11 arrays
|
||||
pointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
|
||||
parm.(ArrayType).getBaseType().getUnspecifiedType())
|
||||
or
|
||||
pointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
|
||||
parm.(ArrayType).getBaseType().getUnspecifiedType())
|
||||
}
|
||||
|
||||
// This predicate holds whenever expression `arg` may be used to initialize
|
||||
// function parameter `parm` without need for run-time conversion.
|
||||
pragma[inline]
|
||||
predicate argMayBeUsed(Expr arg, Parameter parm) {
|
||||
argTypeMayBeUsed(arg.getFullyConverted().getUnspecifiedType(), parm.getUnspecifiedType())
|
||||
}
|
||||
|
||||
// True if function was ()-declared, but not (void)-declared or K&R-defined
|
||||
predicate hasZeroParamDecl(Function f) {
|
||||
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
not fde.hasVoidParamList() and fde.getNumberOfParameters() = 0 and not fde.isDefinition()
|
||||
)
|
||||
}
|
||||
|
||||
// True if this file (or header) was compiled as a C file
|
||||
predicate isCompiledAsC(Function f) {
|
||||
exists(File file | file.compiledAsC() |
|
||||
file = f.getFile() or file.getAnIncludedFile+() = f.getFile()
|
||||
)
|
||||
}
|
||||
import MistypedFunctionArguments
|
||||
|
||||
from FunctionCall fc, Function f, Parameter p
|
||||
where
|
||||
f = fc.getTarget() and
|
||||
p = f.getAParameter() and
|
||||
hasZeroParamDecl(f) and
|
||||
isCompiledAsC(f) and
|
||||
not f.isVarargs() and
|
||||
not f instanceof BuiltInFunction and
|
||||
p.getIndex() < fc.getNumberOfArguments() and
|
||||
// Parameter p and its corresponding call argument must have mismatched types
|
||||
not argMayBeUsed(fc.getArgument(p.getIndex()), p)
|
||||
where mistypedFunctionArguments(fc, f, p)
|
||||
select fc, "Calling $@: argument $@ of type $@ is incompatible with parameter $@.", f, f.toString(),
|
||||
fc.getArgument(p.getIndex()) as arg, arg.toString(),
|
||||
arg.getExplicitlyConverted().getUnspecifiedType() as atype, atype.toString(), p, p.getTypedName()
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Provides the implementation of the MistypedFunctionArguments query. The
|
||||
* query is implemented as a library, so that we can avoid producing
|
||||
* duplicate results in other similar queries.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
private predicate arithTypesMatch(Type arg, Type parm) {
|
||||
arg = parm
|
||||
or
|
||||
arg.getSize() = parm.getSize() and
|
||||
(
|
||||
arg instanceof IntegralOrEnumType and
|
||||
parm instanceof IntegralOrEnumType
|
||||
or
|
||||
arg instanceof FloatingPointType and
|
||||
parm instanceof FloatingPointType
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate nestedPointerArgTypeMayBeUsed(Type arg, Type parm) {
|
||||
// arithmetic types
|
||||
arithTypesMatch(arg, parm)
|
||||
or
|
||||
// conversion to/from pointers to void is allowed
|
||||
arg instanceof VoidType
|
||||
or
|
||||
parm instanceof VoidType
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate pointerArgTypeMayBeUsed(Type arg, Type parm) {
|
||||
nestedPointerArgTypeMayBeUsed(arg, parm)
|
||||
or
|
||||
// nested pointers
|
||||
nestedPointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
|
||||
parm.(PointerType).getBaseType().getUnspecifiedType())
|
||||
or
|
||||
nestedPointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
|
||||
parm.(PointerType).getBaseType().getUnspecifiedType())
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate argTypeMayBeUsed(Type arg, Type parm) {
|
||||
// arithmetic types
|
||||
arithTypesMatch(arg, parm)
|
||||
or
|
||||
// pointers to compatible types
|
||||
pointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
|
||||
parm.(PointerType).getBaseType().getUnspecifiedType())
|
||||
or
|
||||
pointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
|
||||
parm.(PointerType).getBaseType().getUnspecifiedType())
|
||||
or
|
||||
// C11 arrays
|
||||
pointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(),
|
||||
parm.(ArrayType).getBaseType().getUnspecifiedType())
|
||||
or
|
||||
pointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(),
|
||||
parm.(ArrayType).getBaseType().getUnspecifiedType())
|
||||
}
|
||||
|
||||
// This predicate holds whenever expression `arg` may be used to initialize
|
||||
// function parameter `parm` without need for run-time conversion.
|
||||
pragma[inline]
|
||||
private predicate argMayBeUsed(Expr arg, Parameter parm) {
|
||||
argTypeMayBeUsed(arg.getFullyConverted().getUnspecifiedType(), parm.getUnspecifiedType())
|
||||
}
|
||||
|
||||
// True if function was ()-declared, but not (void)-declared or K&R-defined
|
||||
private predicate hasZeroParamDecl(Function f) {
|
||||
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
not fde.hasVoidParamList() and fde.getNumberOfParameters() = 0 and not fde.isDefinition()
|
||||
)
|
||||
}
|
||||
|
||||
// True if this file (or header) was compiled as a C file
|
||||
private predicate isCompiledAsC(File f) {
|
||||
f.compiledAsC()
|
||||
or
|
||||
exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
|
||||
}
|
||||
|
||||
predicate mistypedFunctionArguments(FunctionCall fc, Function f, Parameter p) {
|
||||
f = fc.getTarget() and
|
||||
p = f.getAParameter() and
|
||||
hasZeroParamDecl(f) and
|
||||
isCompiledAsC(f.getFile()) and
|
||||
not f.isVarargs() and
|
||||
not f instanceof BuiltInFunction and
|
||||
p.getIndex() < fc.getNumberOfArguments() and
|
||||
// Parameter p and its corresponding call argument must have mismatched types
|
||||
not argMayBeUsed(fc.getArgument(p.getIndex()), p)
|
||||
}
|
||||
@@ -15,31 +15,8 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
// True if function was ()-declared, but not (void)-declared or K&R-defined
|
||||
predicate hasZeroParamDecl(Function f) {
|
||||
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
not fde.hasVoidParamList() and fde.getNumberOfParameters() = 0 and not fde.isDefinition()
|
||||
)
|
||||
}
|
||||
|
||||
// True if this file (or header) was compiled as a C file
|
||||
predicate isCompiledAsC(Function f) {
|
||||
exists(File file | file.compiledAsC() |
|
||||
file = f.getFile() or file.getAnIncludedFile+() = f.getFile()
|
||||
)
|
||||
}
|
||||
import TooFewArguments
|
||||
|
||||
from FunctionCall fc, Function f
|
||||
where
|
||||
f = fc.getTarget() and
|
||||
not f.isVarargs() and
|
||||
not f instanceof BuiltInFunction and
|
||||
hasZeroParamDecl(f) and
|
||||
isCompiledAsC(f) and
|
||||
// There is an explicit declaration of the function whose parameter count is larger
|
||||
// than the number of call arguments
|
||||
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
fde.getNumberOfParameters() > fc.getNumberOfArguments()
|
||||
)
|
||||
where tooFewArguments(fc, f)
|
||||
select fc, "This call has fewer arguments than required by $@.", f, f.toString()
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Provides the implementation of the TooFewArguments query. The
|
||||
* query is implemented as a library, so that we can avoid producing
|
||||
* duplicate results in other similar queries.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
// True if function was ()-declared, but not (void)-declared or K&R-defined
|
||||
private predicate hasZeroParamDecl(Function f) {
|
||||
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
not fde.hasVoidParamList() and fde.getNumberOfParameters() = 0 and not fde.isDefinition()
|
||||
)
|
||||
}
|
||||
|
||||
// True if this file (or header) was compiled as a C file
|
||||
private predicate isCompiledAsC(File f) {
|
||||
f.compiledAsC()
|
||||
or
|
||||
exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
|
||||
}
|
||||
|
||||
predicate tooFewArguments(FunctionCall fc, Function f) {
|
||||
f = fc.getTarget() and
|
||||
not f.isVarargs() and
|
||||
not f instanceof BuiltInFunction and
|
||||
hasZeroParamDecl(f) and
|
||||
isCompiledAsC(f.getFile()) and
|
||||
// There is an explicit declaration of the function whose parameter count is larger
|
||||
// than the number of call arguments
|
||||
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
fde.getNumberOfParameters() > fc.getNumberOfArguments()
|
||||
)
|
||||
}
|
||||
@@ -12,35 +12,8 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
// True if function was ()-declared, but not (void)-declared or K&R-defined
|
||||
// or implicitly declared (i.e., lacking a prototype)
|
||||
predicate hasZeroParamDecl(Function f) {
|
||||
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
not fde.isImplicit() and
|
||||
not fde.hasVoidParamList() and
|
||||
fde.getNumberOfParameters() = 0 and
|
||||
not fde.isDefinition()
|
||||
)
|
||||
}
|
||||
|
||||
// True if this file (or header) was compiled as a C file
|
||||
predicate isCompiledAsC(Function f) {
|
||||
exists(File file | file.compiledAsC() |
|
||||
file = f.getFile() or file.getAnIncludedFile+() = f.getFile()
|
||||
)
|
||||
}
|
||||
import TooManyArguments
|
||||
|
||||
from FunctionCall fc, Function f
|
||||
where
|
||||
f = fc.getTarget() and
|
||||
not f.isVarargs() and
|
||||
hasZeroParamDecl(f) and
|
||||
isCompiledAsC(f) and
|
||||
exists(f.getBlock()) and
|
||||
// There must not exist a declaration with the number of parameters
|
||||
// at least as large as the number of call arguments
|
||||
not exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
fde.getNumberOfParameters() >= fc.getNumberOfArguments()
|
||||
)
|
||||
where tooManyArguments(fc, f)
|
||||
select fc, "This call has more arguments than required by $@.", f, f.toString()
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Provides the implementation of the TooManyArguments query. The
|
||||
* query is implemented as a library, so that we can avoid producing
|
||||
* duplicate results in other similar queries.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
// True if function was ()-declared, but not (void)-declared or K&R-defined
|
||||
// or implicitly declared (i.e., lacking a prototype)
|
||||
private predicate hasZeroParamDecl(Function f) {
|
||||
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
not fde.isImplicit() and
|
||||
not fde.hasVoidParamList() and
|
||||
fde.getNumberOfParameters() = 0 and
|
||||
not fde.isDefinition()
|
||||
)
|
||||
}
|
||||
|
||||
// True if this file (or header) was compiled as a C file
|
||||
private predicate isCompiledAsC(File f) {
|
||||
f.compiledAsC()
|
||||
or
|
||||
exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
|
||||
}
|
||||
|
||||
predicate tooManyArguments(FunctionCall fc, Function f) {
|
||||
f = fc.getTarget() and
|
||||
not f.isVarargs() and
|
||||
hasZeroParamDecl(f) and
|
||||
isCompiledAsC(f.getFile()) and
|
||||
exists(f.getBlock()) and
|
||||
// There must not exist a declaration with the number of parameters
|
||||
// at least as large as the number of call arguments
|
||||
not exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
||||
fde.getNumberOfParameters() >= fc.getNumberOfArguments()
|
||||
)
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import cpp
|
||||
from Class c, int n
|
||||
where
|
||||
c.fromSource() and
|
||||
n = c.getMetrics().getNumberOfMembers() +
|
||||
n =
|
||||
c.getMetrics().getNumberOfMembers() +
|
||||
sum(Function f | c.getACanonicalMemberFunction() = f | f.getMetrics().getNumberOfLinesOfCode())
|
||||
select c, n order by n desc
|
||||
|
||||
@@ -15,16 +15,15 @@ import cpp
|
||||
from Class c, int ccLoc, int loc
|
||||
where
|
||||
c.fromSource() and
|
||||
ccLoc = sum(Function f |
|
||||
ccLoc =
|
||||
sum(Function f |
|
||||
c.getACanonicalMemberFunction() = f and
|
||||
f.getMetrics().getCyclomaticComplexity() > 18
|
||||
|
|
||||
f.getMetrics().getNumberOfLinesOfCode()
|
||||
) and
|
||||
loc = sum(Function f |
|
||||
c.getACanonicalMemberFunction() = f
|
||||
|
|
||||
f.getMetrics().getNumberOfLinesOfCode()
|
||||
) + c.getMetrics().getNumberOfMembers() and
|
||||
loc =
|
||||
sum(Function f | c.getACanonicalMemberFunction() = f | f.getMetrics().getNumberOfLinesOfCode()) +
|
||||
c.getMetrics().getNumberOfMembers() and
|
||||
loc != 0
|
||||
select c, (ccLoc * 100).(float) / loc as n order by n desc
|
||||
|
||||
@@ -76,7 +76,8 @@ class Library extends LibraryT {
|
||||
* `sourceFile` is not in `destLib`).
|
||||
*/
|
||||
predicate libDependencies(File sourceFile, Library destLib, int num) {
|
||||
num = strictcount(Element source, Element dest, File destFile |
|
||||
num =
|
||||
strictcount(Element source, Element dest, File destFile |
|
||||
// dependency from source -> dest.
|
||||
dependsOnSimple(source, dest) and
|
||||
sourceFile = source.getFile() and
|
||||
@@ -101,7 +102,7 @@ predicate libDependencies(File sourceFile, Library destLib, int num) {
|
||||
predicate encodedDependencies(File source, string encodedDependency, int num) {
|
||||
exists(Library destLib |
|
||||
libDependencies(source, destLib, num) and
|
||||
encodedDependency = "/" + source.getRelativePath() + "<|>" + destLib.getName() + "<|>" +
|
||||
destLib.getVersion()
|
||||
encodedDependency =
|
||||
"/" + source.getRelativePath() + "<|>" + destLib.getName() + "<|>" + destLib.getVersion()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -85,7 +85,8 @@ private predicate closeWithDepth(int depth, File f, PreprocessorDirective close,
|
||||
predicate length(PreprocessorDirective open, int length) {
|
||||
exists(int depth, File f, int start, int end |
|
||||
openWithDepth(depth, f, open, start) and
|
||||
end = min(PreprocessorDirective endif, int closeLine |
|
||||
end =
|
||||
min(PreprocessorDirective endif, int closeLine |
|
||||
closeWithDepth(depth, f, endif, closeLine) and
|
||||
closeLine > start
|
||||
|
|
||||
|
||||
@@ -19,7 +19,8 @@ where
|
||||
if loc > 0
|
||||
then
|
||||
// Weighted average of complexity by function length
|
||||
complexity = sum(FunctionDeclarationEntry fde |
|
||||
complexity =
|
||||
sum(FunctionDeclarationEntry fde |
|
||||
fde.getFile() = f
|
||||
|
|
||||
fde.getNumberOfLines() * fde.getCyclomaticComplexity()
|
||||
|
||||
@@ -17,7 +17,8 @@ import external.CodeDuplication
|
||||
|
||||
from File f, int n
|
||||
where
|
||||
n = count(int line |
|
||||
n =
|
||||
count(int line |
|
||||
exists(DuplicateBlock d | d.sourceFile() = f |
|
||||
line in [d.sourceStartLine() .. d.sourceEndLine()]
|
||||
) and
|
||||
|
||||
@@ -15,7 +15,8 @@ import cpp
|
||||
from File f, int n
|
||||
where
|
||||
f.fromSource() and
|
||||
n = count(Comment c |
|
||||
n =
|
||||
count(Comment c |
|
||||
c.getFile() = f and
|
||||
(
|
||||
c.getContents().matches("%TODO%") or
|
||||
|
||||
@@ -20,7 +20,8 @@ predicate callToOperator(FunctionCall fc) {
|
||||
from Function f, int n, int o
|
||||
where
|
||||
strictcount(f.getEntryPoint()) = 1 and
|
||||
o = count(FunctionCall c |
|
||||
o =
|
||||
count(FunctionCall c |
|
||||
c.getEnclosingFunction() = f and
|
||||
not c.isInMacroExpansion() and
|
||||
not c.isCompilerGenerated() and
|
||||
|
||||
@@ -16,7 +16,8 @@ import external.VCS
|
||||
|
||||
from File f, int n
|
||||
where
|
||||
n = sum(Commit entry, int churn |
|
||||
n =
|
||||
sum(Commit entry, int churn |
|
||||
churn = entry.getRecentChurnForFile(f) and
|
||||
not artificialChange(entry)
|
||||
|
|
||||
|
||||
@@ -16,7 +16,8 @@ import external.VCS
|
||||
|
||||
from File f, int n
|
||||
where
|
||||
n = sum(Commit entry, int churn |
|
||||
n =
|
||||
sum(Commit entry, int churn |
|
||||
churn = entry.getRecentAdditionsForFile(f) and
|
||||
not artificialChange(entry)
|
||||
|
|
||||
|
||||
@@ -16,7 +16,8 @@ import external.VCS
|
||||
|
||||
from File f, int n
|
||||
where
|
||||
n = sum(Commit entry, int churn |
|
||||
n =
|
||||
sum(Commit entry, int churn |
|
||||
churn = entry.getRecentDeletionsForFile(f) and
|
||||
not artificialChange(entry)
|
||||
|
|
||||
|
||||
@@ -25,7 +25,8 @@ predicate inRange(Commit first, Commit second) {
|
||||
}
|
||||
|
||||
int recommitsForFile(File f) {
|
||||
result = count(Commit recommit |
|
||||
result =
|
||||
count(Commit recommit |
|
||||
f = recommit.getAnAffectedFile() and
|
||||
exists(Commit prev | inRange(prev, recommit))
|
||||
)
|
||||
|
||||
@@ -15,7 +15,8 @@ import external.VCS
|
||||
|
||||
from File f, int n
|
||||
where
|
||||
n = count(Commit e |
|
||||
n =
|
||||
count(Commit e |
|
||||
e.getAnAffectedFile() = f and
|
||||
e.daysToNow() <= 180 and
|
||||
not artificialChange(e)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user