mirror of
https://github.com/github/codeql.git
synced 2026-07-20 18:58:36 +02:00
Merge remote-tracking branch 'upstream/master' into infinite-loops-visible
Moved the change note to 1.23.
This commit is contained in:
3
.codeqlmanifest.json
Normal file
3
.codeqlmanifest.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{ "provide": [ "*/ql/src/qlpack.yml",
|
||||
"misc/legacy-support/*/qlpack.yml",
|
||||
"misc/suite-helpers/qlpack.yml" ] }
|
||||
24
.github/ISSUE_TEMPLATE/lgtm-com---false-positive.md
vendored
Normal file
24
.github/ISSUE_TEMPLATE/lgtm-com---false-positive.md
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: LGTM.com - false positive
|
||||
about: Tell us about an alert that shouldn't be reported
|
||||
title: LGTM.com - false positive
|
||||
labels: false-positive
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Description of the false positive**
|
||||
|
||||
<!-- Please explain briefly why you think it shouldn't be included. -->
|
||||
|
||||
**URL to the alert on the project page on LGTM.com**
|
||||
|
||||
<!--
|
||||
1. Open the project on LGTM.com.
|
||||
For example, https://lgtm.com/projects/g/pallets/click/.
|
||||
2. Switch to the `Alerts` tab. For example, https://lgtm.com/projects/g/pallets/click/alerts/.
|
||||
3. Scroll to the alert that you would like to report.
|
||||
4. Click on the right most icon `View this alert within the complete file`.
|
||||
5. A new browser tab opens. Copy and paste the page URL here.
|
||||
For example, https://lgtm.com/projects/g/pallets/click/snapshot/719fb7d8322b0767cdd1e5903ba3eb3233ba8dd5/files/click/_winconsole.py#xa08d213ab3289f87:1.
|
||||
-->
|
||||
14
.github/ISSUE_TEMPLATE/ql---general.md
vendored
Normal file
14
.github/ISSUE_TEMPLATE/ql---general.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
name: General issue
|
||||
about: Tell us if you think something is wrong or if you have a question
|
||||
title: General issue
|
||||
labels: question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Description of the issue**
|
||||
|
||||
<!-- Please explain briefly what is the problem.
|
||||
If it is about an LGTM project, please include its URL.-->
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -12,3 +12,6 @@
|
||||
# Visual studio temporaries, except a file used by QL4VS
|
||||
.vs/*
|
||||
!.vs/VSWorkspaceSettings.json
|
||||
|
||||
# It's useful (though not required) to be able to unpack codeql in the ql checkout itself
|
||||
/codeql/
|
||||
|
||||
@@ -2,4 +2,9 @@
|
||||
/java/ @Semmle/java
|
||||
/javascript/ @Semmle/js
|
||||
/cpp/ @Semmle/cpp-analysis
|
||||
/cpp/**/*.qhelp @semmledocs-ac
|
||||
/cpp/**/*.qhelp @hubwriter
|
||||
/csharp/**/*.qhelp @jf205
|
||||
/java/**/*.qhelp @felicitymay
|
||||
/javascript/**/*.qhelp @mchammer01
|
||||
/python/**/*.qhelp @felicitymay
|
||||
/docs/language/ @shati-patel @jf205
|
||||
|
||||
@@ -14,7 +14,7 @@ Our community strives to:
|
||||
* Posting, or threatening to post, people’s personally identifying information (“doxing”).
|
||||
* Insults, especially those using discriminatory terms or slurs.
|
||||
* Behavior that could be perceived as sexual attention.
|
||||
* Advocating for or encouraging any of the above behaviors.
|
||||
* Advocating for or encouraging any of the above behaviors.
|
||||
* Understand disagreements: Disagreements, both social and technical, are useful learning opportunities. Seek to understand others’ viewpoints and resolve differences constructively.
|
||||
|
||||
This code is not exhaustive or complete. It serves to capture our common understanding of a productive, collaborative environment. We expect the code to be followed in spirit as much as in the letter.
|
||||
|
||||
@@ -1,10 +1,50 @@
|
||||
# Contributing to QL
|
||||
|
||||
We welcome contributions to our standard library and standard checks, got an idea for a new check, or how to improve an existing query? Then please go ahead an open a Pull Request!
|
||||
We welcome contributions to our standard library and standard checks. Got an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request!
|
||||
|
||||
Before we accept your pull request, we will require that you have agreed to our Contributor License Agreement, this is not something that you need to do before you submit your pull request, but until you've done so, we will be unable to accept your contribution.
|
||||
Before we accept your pull request, we require that you have agreed to our Contributor License Agreement, this is not something that you need to do before you submit your pull request, but until you've done so, we will be unable to accept your contribution.
|
||||
|
||||
Please read our [QL Style Guide](docs/ql-style-guide.md) for information on how to format QL code in this repository.
|
||||
## Adding a new query
|
||||
|
||||
If you have an idea for a query that you would like to share with other Semmle users, please open a pull request to add it to this repository.
|
||||
Follow the steps below to help other users understand what your query does, and to ensure that your query is consistent with the other Semmle queries.
|
||||
|
||||
1. **Consult the QL documentation for query writers**
|
||||
|
||||
There is lots of useful documentation to help you write QL, ranging from information about query file structure to language-specific tutorials. For more information on the documentation available, see [Writing QL queries](https://help.semmle.com/QL/learn-ql/writing-queries/writing-queries.html) on [help.semmle.com](https://help.semmle.com).
|
||||
|
||||
2. **Format your QL correctly**
|
||||
|
||||
All of Semmle's standard QL queries and libraries are uniformly formatted for clarity and consistency, so we strongly recommend that all QL contributions follow the same formatting guidelines. If you use QL for Eclipse, you can auto-format your query in the [QL editor](https://help.semmle.com/ql-for-eclipse/Content/WebHelp/ql-editor.html). For more information, see the [QL style guide](https://github.com/Semmle/ql/blob/master/docs/ql-style-guide.md).
|
||||
|
||||
3. **Make sure your query has the correct metadata**
|
||||
|
||||
Query metadata is used by Semmle's analysis to identify your query and make sure the query results are displayed properly.
|
||||
The most important metadata to include are the `@name`, `@description`, and the `@kind`.
|
||||
Other metadata properties (`@precision`, `@severity`, and `@tags`) are usually added after the query has been reviewed by Semmle staff.
|
||||
For more information on writing query metadata, see the [Query metadata style guide](https://github.com/Semmle/ql/blob/master/docs/query-metadata-style-guide.md).
|
||||
|
||||
4. **Make sure the `select` statement is compatible with the query type**
|
||||
|
||||
The `select` statement of your query must be compatible with the query type (determined by the `@kind` metadata property) for alert or path results to be displayed correctly in LGTM and QL for Eclipse.
|
||||
For more information on `select` statement format, see [Introduction to query files](https://help.semmle.com/QL/learn-ql/writing-queries/introduction-to-queries.html#select-clause) on help.semmle.com.
|
||||
|
||||
5. **Save your query in a `.ql` file in correct language directory in this repository**
|
||||
|
||||
There are five language-specific directories in this repository:
|
||||
|
||||
* C/C++: `ql/cpp/ql/src`
|
||||
* C#: `ql/csharp/ql/src`
|
||||
* Java: `ql/java/ql/src`
|
||||
* JavaScript: `ql/javascript/ql/src`
|
||||
* Python: `ql/python/ql/src`
|
||||
|
||||
Each language-specific directory contains further subdirectories that group queries based on their `@tags` properties or purpose. Select the appropriate subdirectory for your new query, or create a new one if necessary.
|
||||
|
||||
6. **Write a query help file**
|
||||
|
||||
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).
|
||||
|
||||
## Using your personal data
|
||||
|
||||
|
||||
@@ -1,25 +1,42 @@
|
||||
# Improvements to C/C++ analysis
|
||||
|
||||
## General improvements
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
The following changes in version 1.22 affect C/C++ analysis in all applications.
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|----------------------------|------------------------|------------------------------------------------------------------|
|
||||
| Expression has no effect (`cpp/useless-expression`) | Fewer false positive results | Calls to functions with the `weak` attribute are no longer considered to be side effect free, because they could be overridden with a different implementation at link time. |
|
||||
| No space for zero terminator (`cpp/no-space-for-terminator`) | Fewer false positive results | False positives involving strings that are not null-terminated have been excluded. |
|
||||
| Call to alloca in a loop (`cpp/alloca-in-loop`) | Fewer false positive results | The query no longer highlights code where the stack allocation could not be reached multiple times in the loop, typically due to a `break` or `return` statement. |
|
||||
| Continue statement that does not continue (`cpp/continue-in-false-loop`) | Fewer false positive results | Analysis is now restricted to `do`-`while` loops. This query is now run and displayed by default on LGTM. |
|
||||
| Expression has no effect (`cpp/useless-expression`) | Fewer false positive results | Calls to functions with the `weak` attribute are no longer considered to be side-effect free, because they could be overridden with a different implementation at link time. |
|
||||
| No space for zero terminator (`cpp/no-space-for-terminator`) | Fewer false positive results | False positive results for strings that are not null-terminated have been excluded. |
|
||||
| Non-constant format string (`cpp/non-constant-format`) | Fewer false positive results | The query was rewritten using the taint-tracking library. |
|
||||
| Sign check of bitwise operation (`cpp/bitwise-sign-check`) | Fewer false positive and more true positive results | The query now understands the direction of each comparison, making it more accurate. |
|
||||
| Suspicious pointer scaling (`cpp/suspicious-pointer-scaling`) | Lower precision | The precision of this query has been reduced to "medium". This coding pattern is used intentionally and safely in a number of real-world projects. Results are no longer displayed on LGTM unless you choose to display them. |
|
||||
| Non-constant format string (`cpp/non-constant-format`) | Fewer false positive results | Rewritten using the taint-tracking library. |
|
||||
| Comparison of narrow type with wide type in loop condition (`cpp/comparison-with-wider-type`) | Higher precision | The precision of this query has been increased to "high" as the alerts from this query have proved to be valuable on real-world projects. With this precision, results are now displayed by default in LGTM. |
|
||||
| Variable used in its own initializer (`cpp/use-in-own-initializer`) | Fewer false positive results | False positive results for constant variables with the same name in different namespaces have been removed. |
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
- The data flow library (`semmle.code.cpp.dataflow.DataFlow`) has had the
|
||||
following improvements, all of which benefit the taint tracking library
|
||||
(`semmle.code.cpp.dataflow.TaintTracking`) as well.
|
||||
- This release includes preliminary support for interprocedural flow through
|
||||
fields (non-static data members). In some cases, data stored in a field in
|
||||
one function can now flow to a read of the same field in a different
|
||||
function.
|
||||
- The possibility of specifying barrier edges using
|
||||
`isBarrierEdge`/`isSanitizerEdge` in data-flow and taint-tracking
|
||||
configurations has been replaced with the option of specifying in- and
|
||||
out-barriers on nodes by overriding `isBarrierIn`/`isSanitizerIn` and
|
||||
`isBarrierOut`/`isSanitizerOut`. This should be simpler to use effectively,
|
||||
as it does not require knowledge about the actual edges used internally by
|
||||
the library.
|
||||
- The library now models data flow through `std::swap`.
|
||||
- Recursion through the `DataFlow` library is now always a compile error. Such recursion has been deprecated since release 1.16 in March 2018. If one `DataFlow::Configuration` needs to depend on the results of another, switch one of them to use one of the `DataFlow2` through `DataFlow4` libraries.
|
||||
- In the `semmle.code.cpp.dataflow.TaintTracking` library, the second copy of `Configuration` has been renamed from `TaintTracking::Configuration2` to `TaintTracking2::Configuration`, and the old name is now deprecated. Import `semmle.code.cpp.dataflow.TaintTracking2` to access the new name.
|
||||
- The `semmle.code.cpp.security.TaintTracking` library now considers a pointer difference calculation as blocking taint flow.
|
||||
- The predicate `Variable.getAnAssignedValue()` now reports assignments to fields resulting from aggregate initialization (` = {...}`).
|
||||
- The predicate `TypeMention.toString()` has been simplified to always return the string "`type mention`". This may improve performance when using `Element.toString()` or its descendants.
|
||||
- The `semmle.code.cpp.security.TaintTracking` library now considers a pointer difference calculation as blocking taint flow.
|
||||
- Fixed the `LocalScopeVariableReachability.qll` library's handling of loops with an entry condition is both always true upon first entry, and where there is more than one control flow path through the loop condition. This change increases the accuracy of the `LocalScopeVariableReachability.qll` library and queries which depend on it.
|
||||
- Fixed the `LocalScopeVariableReachability.qll` library's handling of loops where the entry condition is always true on first entry, and where there is more than one control flow path through the loop condition. This change increases the accuracy of the `LocalScopeVariableReachability.qll` library and queries that depend on it.
|
||||
- There is a new `Variable.isThreadLocal()` predicate. It can be used to tell whether a variable is `thread_local`.
|
||||
- C/C++ code examples have been added to QLDoc comments on many more classes in the QL libraries.
|
||||
|
||||
@@ -1,29 +1,37 @@
|
||||
# Improvements to C# analysis
|
||||
|
||||
The following changes in version 1.22 affect C# analysis in all applications.
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|------------------------------|------------------------|-----------------------------------|
|
||||
| Added lines (`cs/vcs/added-lines-per-file`) | No results | Query has been removed. |
|
||||
| Churned lines (`cs/vcs/churn-per-file`) | No results | Query has been removed. |
|
||||
| Constant condition (`cs/constant-condition`) | Fewer false positive results | Results have been removed for default cases (`_`) in switch expressions. |
|
||||
| Defect filter | No results | Query has been removed. |
|
||||
| Defect from SVN | No results | Query has been removed. |
|
||||
| Deleted lines (`cs/vcs/deleted-lines-per-file`) | No results | Query has been removed. |
|
||||
| Dispose may not be called if an exception is thrown during execution (`cs/dispose-not-called-on-throw`) | Fewer false positive results | Results have been removed where an object is disposed both by a `using` statement and a `Dispose` call. |
|
||||
| Files edited in pairs | No results | Query has been removed. |
|
||||
| Filter: only files recently edited | No results | Query has been removed. |
|
||||
| Large files currently edited | No results | Query has been removed. |
|
||||
| Metric from SVN | No results | Query has been removed. |
|
||||
| Number of authors (version control) (`cs/vcs/authors-per-file`) | No results | Query has been removed. |
|
||||
| Number of file-level changes (`cs/vcs/commits-per-file`) | No results | Query has been removed. |
|
||||
| Number of co-committed files (`cs/vcs/co-commits-per-file`) | No results | Query has been removed. |
|
||||
| Number of file re-commits (`cs/vcs/recommits-per-file`) | No results | Query has been removed. |
|
||||
| Number of recent file changes (`cs/vcs/recent-commits-per-file`) | No results | Query has been removed. |
|
||||
| Number of authors | No results | Query has been removed. |
|
||||
| Number of commits | No results | Query has been removed. |
|
||||
| Poorly documented files with many authors | No results | Query has been removed. |
|
||||
| Recent activity | No results | Query has been removed. |
|
||||
| Unchecked return value (`cs/unchecked-return-value`) | Fewer false positive results | Method calls that are expression bodies of `void` callables (for example, the call to `Foo` in `void Bar() => Foo()`) are no longer considered to use the return value. |
|
||||
|
||||
## Removal of old queries
|
||||
|
||||
The following historic queries are no longer available in the distribution:
|
||||
|
||||
* Added lines (`cs/vcs/added-lines-per-file`)
|
||||
* Churned lines (`cs/vcs/churn-per-file`)
|
||||
* Defect filter
|
||||
* Defect from SVN
|
||||
* Deleted lines (`cs/vcs/deleted-lines-per-file`)
|
||||
* Files edited in pairs
|
||||
* Filter: only files recently edited
|
||||
* Large files currently edited
|
||||
* Metric from SVN
|
||||
* Number of authors (version control) (`cs/vcs/authors-per-file`)
|
||||
* Number of file-level changes (`cs/vcs/commits-per-file`)
|
||||
* Number of co-committed files (`cs/vcs/co-commits-per-file`)
|
||||
* Number of file re-commits (`cs/vcs/recommits-per-file`)
|
||||
* Number of recent file changes (`cs/vcs/recent-commits-per-file`)
|
||||
* Number of authors
|
||||
* Number of commits
|
||||
* Poorly documented files with many authors
|
||||
* Recent activity
|
||||
|
||||
## Changes to code extraction
|
||||
|
||||
@@ -33,12 +41,18 @@
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
* The new class `AnnotatedType` models types with type annotations, including nullability information, return kinds (`ref` and `readonly ref`), and parameter kinds (`in`, `out`, and `ref`)
|
||||
- The new predicate `Assignable.getAnnotatedType()` gets the annotated type of an assignable (such as a variable or a property)
|
||||
- The new predicates `Callable.getAnnotatedReturnType()` and `DelegateType.getAnnotatedReturnType()` get the annotated type of the return value
|
||||
- The new predicate `ArrayType.getAnnotatedElementType()` gets the annotated type of the array element
|
||||
- The new predicate `ConstructedGeneric.getAnnotatedTypeArgument()` gets the annotated type of a type argument
|
||||
- The new predicate `TypeParameterConstraints.getAnAnnotatedTypeConstraint()` gets a type constraint with type annotations
|
||||
* The new class `SuppressNullableWarningExpr` models suppress-nullable-warning expressions such as `x!`
|
||||
|
||||
## Changes to autobuilder
|
||||
* The new class `AnnotatedType` models types with type annotations, including nullability information, return kinds (`ref` and `readonly ref`), and parameter kinds (`in`, `out`, and `ref`).
|
||||
- The new predicate `Assignable.getAnnotatedType()` gets the annotated type of an assignable (such as a variable or a property).
|
||||
- The new predicates `Callable.getAnnotatedReturnType()` and `DelegateType.getAnnotatedReturnType()` gets the annotated type of the return value.
|
||||
- The new predicate `ArrayType.getAnnotatedElementType()` gets the annotated type of the array element.
|
||||
- The new predicate `ConstructedGeneric.getAnnotatedTypeArgument()` gets the annotated type of a type argument.
|
||||
- The new predicate `TypeParameterConstraints.getAnAnnotatedTypeConstraint()` gets a type constraint with type annotations.
|
||||
* The new class `SuppressNullableWarningExpr` models suppress-nullable-warning expressions such as `x!`.
|
||||
* The data-flow and taint-tracking libraries now support flow through fields. All existing configurations will have field-flow enabled by default, but it can be disabled by adding `override int fieldFlowBranchLimit() { result = 0 }` to the configuration class. Field assignments, `this.Foo = x`, object initializers, `new C() { Foo = x }`, and field initializers `int Foo = 0` are supported.
|
||||
* The possibility of specifying barrier edges using
|
||||
`isBarrierEdge`/`isSanitizerEdge` in data-flow and taint-tracking
|
||||
configurations has been replaced with the option of specifying in- and
|
||||
out-barriers on nodes by overriding `isBarrierIn`/`isSanitizerIn` and
|
||||
`isBarrierOut`/`isSanitizerOut`. This should be simpler to use effectively,
|
||||
as it does not require knowledge about the actual edges used internally by
|
||||
the library.
|
||||
|
||||
@@ -1,10 +1,35 @@
|
||||
# Improvements to Java analysis
|
||||
|
||||
The following changes in version 1.22 affect Java analysis in all applications.
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|----------------------------|------------------------|------------------------------------------------------------------|
|
||||
| Equals method does not inspect argument type (`java/unchecked-cast-in-equals`) | Fewer false positive and more true positive results | Precision has been improved by doing a bit of inter-procedural analysis and relying less on ad-hoc method names. |
|
||||
| Uncontrolled data in arithmetic expression (`java/uncontrolled-arithmetic`) | Fewer false positive results | Precision has been improved in several ways, in particular, by better detection of guards along the data-flow path. |
|
||||
| Uncontrolled data used in path expression (`java/path-injection`) | Fewer false positive results | The query no longer reports results guarded by `!var.contains("..")`. |
|
||||
| User-controlled data in arithmetic expression (`java/tainted-arithmetic`) | Fewer false positive results | Precision has been improved in several ways, in particular, by better detection of guards along the data-flow path. |
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
* The virtual dispatch library has been updated to give more precise dispatch
|
||||
targets for `Object.toString()` calls. This affects all security queries and
|
||||
removes false positive results that arose from paths through impossible `toString()`
|
||||
calls.
|
||||
* The library `VCS.qll` and all queries that imported it have been removed.
|
||||
* The second copy of the interprocedural `TaintTracking` library has been
|
||||
renamed from `TaintTracking::Configuration2` to
|
||||
`TaintTracking2::Configuration`, and the old name is now deprecated. Import
|
||||
`semmle.code.java.dataflow.TaintTracking2` to access the new name.
|
||||
* The data-flow library now makes it easier to specify barriers/sanitizers
|
||||
arising from guards by overriding the predicate
|
||||
`isBarrierGuard`/`isSanitizerGuard` on data-flow and taint-tracking
|
||||
configurations respectively.
|
||||
* The possibility of specifying barrier edges using
|
||||
`isBarrierEdge`/`isSanitizerEdge` in data-flow and taint-tracking
|
||||
configurations has been replaced with the option of specifying in- and
|
||||
out-barriers on nodes by overriding `isBarrierIn`/`isSanitizerIn` and
|
||||
`isBarrierOut`/`isSanitizerOut`. This should be simpler to use effectively,
|
||||
as it does not require knowledge about the actual edges used internally by
|
||||
the library.
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
## General improvements
|
||||
|
||||
* Automatic classification of test files has been improved, in particular `__tests__` and `__mocks__` folders (as used by [Jest](https://jestjs.io)) are now recognized.
|
||||
|
||||
* Support for the following frameworks and libraries has been improved:
|
||||
- [cross-spawn](https://www.npmjs.com/package/cross-spawn)
|
||||
- [cross-spawn-async](https://www.npmjs.com/package/cross-spawn-async)
|
||||
@@ -12,18 +14,38 @@
|
||||
- [remote-exec](https://www.npmjs.com/package/remote-exec)
|
||||
|
||||
* Support for tracking data flow and taint through getter functions (that is, functions that return a property of one of their arguments) and through the receiver object of method calls has been improved. This may produce more security alerts.
|
||||
|
||||
|
||||
* Taint tracking through object property names has been made more precise, resulting in fewer false positive results.
|
||||
|
||||
* Method calls are now resolved in more cases, due to improved class hierarchy analysis. This may produce more security alerts.
|
||||
|
||||
* Jump-to-definition now resolves calls to their definition in more cases, and supports jumping from a JSDoc type annotation to its definition.
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------|----------|-------------|
|
||||
| | | |
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|---------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Indirect uncontrolled command line (`js/indirect-command-line-injection`) | correctness, security, external/cwe/cwe-078, external/cwe/cwe-088 | Highlights command-line invocations that may indirectly introduce a command-line injection vulnerability elsewhere, indicating a possible violation of [CWE-78](https://cwe.mitre.org/data/definitions/78.html). Results are not shown on LGTM by default. |
|
||||
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
|
||||
| Shift out of range | Fewer false positive results | This rule now correctly handles BigInt shift operands. |
|
||||
| Conflicting HTML element attributes (`js/conflicting-html-attribute`) | No changes to results | Results are no longer shown on LGTM by default. |
|
||||
| Shift out of range (`js/shift-out-of-range`| Fewer false positive results | This rule now correctly handles BigInt shift operands. |
|
||||
| Superfluous trailing arguments (`js/superfluous-trailing-arguments`) | Fewer false-positive results. | This rule no longer flags calls to placeholder functions that trivially throw an exception. |
|
||||
| Undocumented parameter (`js/jsdoc/missing-parameter`) | No changes to results | This rule is now run on LGTM, although its results are still not shown by default. |
|
||||
| Missing space in string concatenation (`js/missing-space-in-concatenation`) | Fewer false positive results | The rule now requires a word-like part exists in the string concatenation. |
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
- The `getName()` predicate on functions and classes now gets a name that is
|
||||
inferred from the context if the function or class was not declared with a name.
|
||||
- The two-argument and three-argument variants of `DataFlow::Configuration::isBarrier` and
|
||||
`TaintTracking::Configuration::isSanitizer` have been deprecated. Overriding them no
|
||||
longer has any effect. Use `isBarrierEdge` and `isSanitizerEdge` instead.
|
||||
- The QLDoc for most AST classes have been expanded with concrete syntax examples.
|
||||
- Tutorials on how to use [flow labels](https://help.semmle.com/QL/learn-ql/javascript/flow-labels.html)
|
||||
and [type tracking](https://help.semmle.com/QL/learn-ql/javascript/type-tracking.html) have been published,
|
||||
as well as a [data flow cheat sheet](https://help.semmle.com/QL/learn-ql/javascript/dataflow-cheat-sheet.html) for quick reference.
|
||||
|
||||
38
change-notes/1.22/analysis-python.md
Normal file
38
change-notes/1.22/analysis-python.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Improvements to Python analysis
|
||||
|
||||
|
||||
## General improvements
|
||||
|
||||
### Points-to
|
||||
Tracking of "unknown" values from modules that are absent from the database has been improved. Particularly when an "unknown" value is used as a decorator, the decorated function is tracked.
|
||||
|
||||
### Loop unrolling
|
||||
The extractor now unrolls a single iteration of loops that are known to run at least once. This improves analysis in cases like the following
|
||||
|
||||
```python
|
||||
if seq:
|
||||
for x in seq:
|
||||
y = x
|
||||
y # y is defined here
|
||||
```
|
||||
|
||||
### Better API for function parameter annotations
|
||||
Instances of the `Parameter` and `ParameterDefinition` class now have a `getAnnotation` method that returns the corresponding parameter annotation, if one exists.
|
||||
|
||||
### Improvements to the Value API
|
||||
- The Value API has been extended with classes representing functions, classes, tuples, and other types.
|
||||
|
||||
- `Value::forInt(int x)` and `Value::forString(string s)` have been added to make it easier to refer to the `Value` entities for common constants.
|
||||
|
||||
### Other improvements
|
||||
|
||||
- Short flags for regexes (for example, `re.M` for multiline regexes) are now handled correctly.
|
||||
- Modules with multiple import roots no longer get multiple names.
|
||||
- A new `NegativeIntegerLiteral` class has been added as a subtype of `ImmutableLiteral`, so that `-1` is treated as an `ImmutableLiteral`. This means that queries looking for the use of constant integers will automatically handle negative numbers.
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------|----------|-------------|
|
||||
| Arbitrary file write during tarfile extraction (`py/tarslip`) | security, external/cwe/cwe-022 | Finds instances where extracting from a tar archive can result in arbitrary file writes. Results are not shown on LGTM by default. |
|
||||
|
||||
52
change-notes/1.23/analysis-cpp.md
Normal file
52
change-notes/1.23/analysis-cpp.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Improvements to C/C++ analysis
|
||||
|
||||
The following changes in version 1.23 affect C/C++ analysis in all applications.
|
||||
|
||||
## General improvements
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | reliability, japanese-era | This query is a combination of two old queries that were identical in purpose but separate as an implementation detail. This new query replaces Hard-coded Japanese era start date in call (`cpp/japanese-era/constructor-or-method-with-exact-era-date`) and Hard-coded Japanese era start date in struct (`cpp/japanese-era/struct-with-exact-era-date`). |
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|----------------------------|------------------------|------------------------------------------------------------------|
|
||||
| Query name (`query id`) | Expected impact | Message. |
|
||||
| Hard-coded Japanese era start date in call (`cpp/japanese-era/constructor-or-method-with-exact-era-date`) | Deprecated | This query has been deprecated. Use the new combined query Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) instead. |
|
||||
| Hard-coded Japanese era start date in struct (`cpp/japanese-era/struct-with-exact-era-date`) | Deprecated | This query has been deprecated. Use the new combined query Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) instead. |
|
||||
| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | More correct results | This query now checks for the beginning date of the Reiwa era (1st May 2019). |
|
||||
| Sign check of bitwise operation (`cpp/bitwise-sign-check`) | Fewer false positive results | Results involving `>=` or `<=` are no longer reported. |
|
||||
| Too few arguments to formatting function (`cpp/wrong-number-format-arguments`) | Fewer false positive results | Fixed false positives resulting from mistmatching declarations of a formatting function. |
|
||||
| Too many arguments to formatting function (`cpp/too-many-format-arguments`) | Fewer false positive results | Fixed false positives resulting from mistmatching declarations of a formatting function. |
|
||||
| Unclear comparison precedence (`cpp/comparison-precedence`) | Fewer false positive results | False positives involving template classes and functions have been fixed. |
|
||||
| Comparison of narrow type with wide type in loop condition (`cpp/comparison-with-wider-type`) | Higher precision | The precision of this query has been increased to "high" as the alerts from this query have proved to be valuable on real-world projects. With this precision, results are now displayed by default in LGTM. |
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
* The data-flow library has been extended with a new feature to aid debugging.
|
||||
Instead of specifying `isSink(Node n) { any() }` on a configuration to
|
||||
explore the possible flow from a source, it is recommended to use the new
|
||||
`Configuration::hasPartialFlow` predicate, as this gives a more complete
|
||||
picture of the partial flow paths from a given source. The feature is
|
||||
disabled by default and can be enabled for individual configurations by
|
||||
overriding `int explorationLimit()`.
|
||||
* The data-flow library now supports flow out of C++ reference parameters.
|
||||
* The data-flow library now allows flow through the address-of operator (`&`).
|
||||
* The `DataFlow::DefinitionByReferenceNode` class now considers `f(x)` to be a
|
||||
definition of `x` when `x` is a variable of pointer type. It no longer
|
||||
considers deep paths such as `f(&x.myField)` to be definitions of `x`. These
|
||||
changes are in line with the user expectations we've observed.
|
||||
* There is now a `DataFlow::localExprFlow` predicate and a
|
||||
`TaintTracking::localExprTaint` predicate to make it easy to use the most
|
||||
common case of local data flow and taint: from one `Expr` to another.
|
||||
* The member predicates of the `FunctionInput` and `FunctionOutput` classes have been renamed for
|
||||
clarity (e.g. `isOutReturnPointer()` to `isReturnValueDeref()`). The existing member predicates
|
||||
have been deprecated, and will be removed in a future release. Code that uses the old member
|
||||
predicates should be updated to use the corresponding new member predicate.
|
||||
* The control-flow graph is now computed in QL, not in the extractor. This can
|
||||
lead to regressions (or improvements) in how queries are optimized because
|
||||
optimization in QL relies on static size estimates, and the control-flow edge
|
||||
relations will now have different size estimates than before.
|
||||
47
change-notes/1.23/analysis-csharp.md
Normal file
47
change-notes/1.23/analysis-csharp.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# Improvements to C# analysis
|
||||
|
||||
The following changes in version 1.23 affect C# analysis in all applications.
|
||||
|
||||
## New queries
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
| Unsafe year argument for 'DateTime' constructor (`cs/unsafe-year-construction`) | reliability, date-time | Finds incorrect manipulation of `DateTime` values, which could lead to invalid dates. |
|
||||
| Mishandling the Japanese era start date (`cs/mishandling-japanese-era`) | reliability, date-time | Finds hard-coded Japanese era start dates that could be invalid. |
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|------------------------------|------------------------|-----------------------------------|
|
||||
| Dereferenced variable may be null (`cs/dereferenced-value-may-be-null`) | Fewer false positive results | More `null` checks are now taken into account, including `null` checks for `dynamic` expressions and `null` checks such as `object alwaysNull = null; if (x != alwaysNull) ...`. |
|
||||
| Missing Dispose call on local IDisposable (`cs/local-not-disposed`) | Fewer false positive results | The query has been rewritten in order to identify more dispose patterns. For example, a local `IDisposable` that is disposed of by passing through a fluent API is no longer reported. |
|
||||
|
||||
## Removal of old queries
|
||||
|
||||
## Changes to code extraction
|
||||
|
||||
* `nameof` expressions are now extracted correctly when the name is a namespace.
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
* The new class `NamespaceAccess` models accesses to namespaces, for example in `nameof` expressions.
|
||||
* The data-flow library now makes it easier to specify barriers/sanitizers
|
||||
arising from guards by overriding the predicate
|
||||
`isBarrierGuard`/`isSanitizerGuard` on data-flow and taint-tracking
|
||||
configurations respectively.
|
||||
* The data-flow library has been extended with a new feature to aid debugging.
|
||||
Instead of specifying `isSink(Node n) { any() }` on a configuration to
|
||||
explore the possible flow from a source, it is recommended to use the new
|
||||
`Configuration::hasPartialFlow` predicate, as this gives a more complete
|
||||
picture of the partial flow paths from a given source. The feature is
|
||||
disabled by default and can be enabled for individual configurations by
|
||||
overriding `int explorationLimit()`.
|
||||
* `foreach` statements where the body is guaranteed to be executed at least once, such as `foreach (var x in new string[]{ "a", "b", "c" }) { ... }`, are now recognized by all analyses based on the control flow graph (such as SSA, data flow and taint tracking).
|
||||
* Fixed the control flow graph for `switch` statements where the `default` case was not the last case. This had caused the remaining cases to be unreachable. `SwitchStmt.getCase(int i)` now puts the `default` case last.
|
||||
* There is now a `DataFlow::localExprFlow` predicate and a
|
||||
`TaintTracking::localExprTaint` predicate to make it easy to use the most
|
||||
common case of local data flow and taint: from one `Expr` to another.
|
||||
|
||||
## Changes to autobuilder
|
||||
30
change-notes/1.23/analysis-java.md
Normal file
30
change-notes/1.23/analysis-java.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Improvements to Java analysis
|
||||
|
||||
The following changes in version 1.23 affect Java analysis in all applications.
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
| Continue statement that does not continue (`java/continue-in-false-loop`) | correctness | Finds `continue` statements in `do { ... } while (false)` loops. |
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|------------------------------|------------------------|-----------------------------------|
|
||||
| Dereferenced variable may be null (`java/dereferenced-value-may-be-null`) | Fewer false positives | Certain indirect null guards involving two auxiliary variables known to be equal can now be detected. |
|
||||
| Non-synchronized override of synchronized method (`java/non-sync-override`) | Fewer false positives | Results are now only reported if the immediately overridden method is synchronized. |
|
||||
| Query built from user-controlled sources (`java/sql-injection`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as SQL expressions sinks. |
|
||||
| Query built from local-user-controlled sources (`java/sql-injection-local`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as SQL expressions sinks. |
|
||||
| Query built without neutralizing special characters (`java/concatenated-sql-query`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as SQL expressions sinks. |
|
||||
| Useless comparison test (`java/constant-comparison`) | Fewer false positives | Additional overflow check patterns are now recognized and no longer reported. |
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
* The data-flow library has been extended with a new feature to aid debugging.
|
||||
Instead of specifying `isSink(Node n) { any() }` on a configuration to
|
||||
explore the possible flow from a source, it is recommended to use the new
|
||||
`Configuration::hasPartialFlow` predicate, as this gives a more complete
|
||||
picture of the partial flow paths from a given source. The feature is
|
||||
disabled by default and can be enabled for individual configurations by
|
||||
overriding `int explorationLimit()`.
|
||||
67
change-notes/1.23/analysis-javascript.md
Normal file
67
change-notes/1.23/analysis-javascript.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Improvements to JavaScript analysis
|
||||
|
||||
## General improvements
|
||||
|
||||
* Suppor for `globalThis` has been added.
|
||||
|
||||
* Support for the following frameworks and libraries has been improved:
|
||||
- [firebase](https://www.npmjs.com/package/firebase)
|
||||
- [mongodb](https://www.npmjs.com/package/mongodb)
|
||||
- [mongoose](https://www.npmjs.com/package/mongoose)
|
||||
- [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible)
|
||||
|
||||
* The call graph has been improved to resolve method calls in more cases. This may produce more security alerts.
|
||||
|
||||
* TypeScript 3.6 features are supported.
|
||||
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|---------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Unused index variable (`js/unused-index-variable`) | correctness | Highlights loops that iterate over an array, but do not use the index variable to access array elements, indicating a possible typo or logic error. Results are shown on LGTM by default. |
|
||||
| Loop bound injection (`js/loop-bound-injection`) | security, external/cwe/cwe-834 | Highlights loops where a user-controlled object with an arbitrary .length value can trick the server to loop indefinitely. Results are not shown on LGTM by default. |
|
||||
| Suspicious method name (`js/suspicious-method-name-declaration`) | correctness, typescript, methods | Highlights suspiciously named methods where the developer likely meant to write a constructor or function. Results are shown on LGTM by default. |
|
||||
| Shell command built from environment values (`js/shell-command-injection-from-environment`) | correctness, security, external/cwe/cwe-078, external/cwe/cwe-088 | Highlights shell commands that may change behavior inadvertently depending on the execution environment, indicating a possible violation of [CWE-78](https://cwe.mitre.org/data/definitions/78.html). Results are shown on LGTM by default.|
|
||||
| Use of returnless function (`js/use-of-returnless-function`) | maintainability, correctness | Highlights calls where the return value is used, but the callee never returns a value. Results are shown on LGTM by default. |
|
||||
| Useless regular expression character escape (`js/useless-regexp-character-escape`) | correctness, security, external/cwe/cwe-20 | Highlights regular expression strings with useless character escapes, indicating a possible violation of [CWE-20](https://cwe.mitre.org/data/definitions/20.html). Results are shown on LGTM by default. |
|
||||
| Unreachable method overloads (`js/unreachable-method-overloads`) | correctness, typescript | Highlights method overloads that are impossible to use from client code. Results are shown on LGTM by default. |
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
|
||||
| Incomplete string escaping or encoding (`js/incomplete-sanitization`) | Fewer false-positive results | This rule now recognizes additional ways delimiters can be stripped away. |
|
||||
| Client-side cross-site scripting (`js/xss`) | More results, fewer false-positive results | More potential vulnerabilities involving functions that manipulate DOM attributes are now recognized, and more sanitizers are detected. |
|
||||
| Code injection (`js/code-injection`) | More results | More potential vulnerabilities involving functions that manipulate DOM event handler attributes are now recognized. |
|
||||
| Hard-coded credentials (`js/hardcoded-credentials`) | Fewer false-positive results | This rule now flags fewer password examples. |
|
||||
| Illegal invocation (`js/illegal-invocation`) | Fewer false-positive results | This rule now correctly handles methods named `call` and `apply`. |
|
||||
| Incorrect suffix check (`js/incorrect-suffix-check`) | Fewer false-positive results | The query recognizes valid checks in more cases. |
|
||||
| Network data written to file (`js/http-to-file-access`) | Fewer false-positive results | This query has been renamed to better match its intended purpose, and now only considers network data untrusted. |
|
||||
| Password in configuration file (`js/password-in-configuration-file`) | Fewer false-positive results | This rule now flags fewer password examples. |
|
||||
| Prototype pollution (`js/prototype-pollution`) | More results | The query now highlights vulnerable uses of jQuery and Angular, and the results are shown on LGTM by default. |
|
||||
| Reflected cross-site scripting (`js/reflected-xss`) | Fewer false-positive results | The query now recognizes more sanitizers. |
|
||||
| Stored cross-site scripting (`js/stored-xss`) | Fewer false-positive results | The query now recognizes more sanitizers. |
|
||||
| Uncontrolled command line (`js/command-line-injection`) | More results | This query now treats responses from servers as untrusted. |
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
* `Expr.getDocumentation()` now handles chain assignments.
|
||||
|
||||
## Removal of deprecated queries
|
||||
|
||||
The following queries (deprecated since 1.17) are no longer available in the distribution:
|
||||
|
||||
* Builtin redefined (js/builtin-redefinition)
|
||||
* Inefficient method definition (js/method-definition-in-constructor)
|
||||
* Bad parity check (js/incomplete-parity-check)
|
||||
* Potentially misspelled property or variable name (js/wrong-capitalization)
|
||||
* Unknown JSDoc tag (js/jsdoc/unknown-tag-type)
|
||||
* Invalid JSLint directive (js/jslint/invalid-directive)
|
||||
* Malformed JSLint directive (js/jslint/malformed-directive)
|
||||
* Use of HTML comments (js/html-comment)
|
||||
* Multi-line string literal (js/multi-line-string)
|
||||
* Octal literal (js/octal-literal)
|
||||
* Reserved word used as variable name (js/use-of-reserved-word)
|
||||
* Trailing comma in array or object expressions (js/trailing-comma-in-array-or-object)
|
||||
* Call to parseInt without radix (js/parseint-without-radix)
|
||||
22
change-notes/1.23/analysis-python.md
Normal file
22
change-notes/1.23/analysis-python.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Improvements to Python analysis
|
||||
|
||||
|
||||
## General improvements
|
||||
|
||||
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------|----------|-------------|
|
||||
| Clear-text logging of sensitive information (`py/clear-text-logging-sensitive-data`) | security, external/cwe/cwe-312 | Finds instances where sensitive information is logged without encryption or hashing. Results are shown on LGTM by default. |
|
||||
| Clear-text storage of sensitive information (`py/clear-text-storage-sensitive-data`) | security, external/cwe/cwe-312 | Finds instances where sensitive information is stored without encryption or hashing. Results are shown on LGTM by default. |
|
||||
| Binding a socket to all network interfaces (`py/bind-socket-all-network-interfaces`) | security | Finds instances where a socket is bound to all network interfaces. Results are shown on LGTM by default. |
|
||||
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|----------------------------|------------------------|------------|
|
||||
| Unreachable code | Fewer false positives | Analysis now accounts for uses of `contextlib.suppress` to suppress exceptions. |
|
||||
| `__iter__` method returns a non-iterator | Better alert message | Alert now highlights which class is expected to be an iterator. |
|
||||
12
change-notes/1.23/extractor-javascript.md
Normal file
12
change-notes/1.23/extractor-javascript.md
Normal file
@@ -0,0 +1,12 @@
|
||||
[[ condition: enterprise-only ]]
|
||||
|
||||
# Improvements to JavaScript analysis
|
||||
|
||||
## Changes to code extraction
|
||||
|
||||
* Asynchronous generator methods are now parsed correctly and no longer cause a spurious syntax error.
|
||||
* Recognition of CommonJS modules has improved. As a result, some files that were previously extracted as
|
||||
global scripts are now extracted as modules.
|
||||
* Top-level `await` is now supported.
|
||||
* A bug was fixed in how the TypeScript extractor handles default-exported anonymous classes.
|
||||
* A bug was fixed in how the TypeScript extractor handles computed instance field names.
|
||||
6
change-notes/support/README.md
Normal file
6
change-notes/support/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Files moved to ``docs`` directory
|
||||
|
||||
Now that all of the QL documentation is in this repository,
|
||||
notes on the languages, compilers, and frameworks supported have moved.
|
||||
They're now stored as part of the Sphinx ``support`` project with the other documentation:
|
||||
``docs/language/support``.
|
||||
@@ -1,22 +0,0 @@
|
||||
Languages and compilers
|
||||
#######################
|
||||
|
||||
QL and LGTM version |version| support analysis of the following languages compiled by the following compilers.
|
||||
|
||||
Note that where there are several versions or dialects of a language, the supported variants are listed.
|
||||
|
||||
.. csv-table::
|
||||
:file: versions-compilers.csv
|
||||
:header-rows: 1
|
||||
:widths: auto
|
||||
:stub-columns: 1
|
||||
|
||||
.. container:: footnote-group
|
||||
|
||||
.. [1] Support for the Arm Compiler (armcc) is preliminary.
|
||||
.. [2] In addition, support is included for the preview features of C# 8.0 and .NET Core 3.0.
|
||||
.. [3] The best results are achieved with COBOL code that stays close to the ANSI 85 standard.
|
||||
.. [4] Builds that execute on Java 6 to 12 can be analyzed. The analysis understands Java 12 language features.
|
||||
.. [5] ECJ is supported when the build invokes it via the Maven Compiler plugin or the Takari Lifecycle plugin.
|
||||
.. [6] JSX and Flow code, YAML, JSON, HTML, and XML files may also be analyzed with JavaScript files.
|
||||
.. [7] TypeScript analysis is performed by running the JavaScript extractor with TypeScript enabled. This is the default for LGTM.
|
||||
@@ -1,83 +1,211 @@
|
||||
{
|
||||
"DataFlow Java/C++": [
|
||||
"DataFlow Java/C++/C#": [
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplDepr.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll"
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll"
|
||||
],
|
||||
"DataFlow Java/C++ Common": [
|
||||
"DataFlow Java/C++/C# Common": [
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll"
|
||||
],
|
||||
"C++ IR Instruction": [
|
||||
"TaintTracking::Configuration Java/C++/C#": [
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll"
|
||||
],
|
||||
"C++ SubBasicBlocks": [
|
||||
"cpp/ql/src/semmle/code/cpp/controlflow/SubBasicBlocks.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll"
|
||||
],
|
||||
"IR Instruction": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll"
|
||||
],
|
||||
"C++ IR IRBlock": [
|
||||
"IR IRBlock": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRBlock.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRBlock.qll"
|
||||
],
|
||||
"C++ IR IRVariable": [
|
||||
"IR IRVariable": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll"
|
||||
],
|
||||
"C++ IR IRFunction": [
|
||||
"IR IRFunction": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRFunction.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRFunction.qll"
|
||||
],
|
||||
"C++ IR Operand": [
|
||||
"IR Operand": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Operand.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Operand.qll"
|
||||
],
|
||||
"C++ IR IRImpl": [
|
||||
"IR IRType": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/IRType.qll"
|
||||
],
|
||||
"IR Operand Tag": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/internal/OperandTag.qll"
|
||||
],
|
||||
"IR TIRVariable":[
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TIRVariable.qll"
|
||||
],
|
||||
"IR IR": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IR.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IR.qll"
|
||||
],
|
||||
"C++ IR IRSanityImpl": [
|
||||
"IR IRSanity": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRSanity.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRSanity.qll"
|
||||
],
|
||||
"C++ IR PrintIRImpl": [
|
||||
"IR PrintIR": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/PrintIR.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/PrintIR.qll"
|
||||
],
|
||||
"IR IntegerConstant": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/internal/IntegerConstant.qll"
|
||||
],
|
||||
"IR IntegerInteval": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerInterval.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/internal/IntegerInterval.qll"
|
||||
],
|
||||
"IR IntegerPartial": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerPartial.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/internal/IntegerPartial.qll"
|
||||
],
|
||||
"IR Overlap": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/internal/Overlap.qll"
|
||||
],
|
||||
"IR EdgeKind": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/EdgeKind.qll"
|
||||
],
|
||||
"IR MemoryAccessKind": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/MemoryAccessKind.qll"
|
||||
],
|
||||
"IR TempVariableTag": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/TempVariableTag.qll"
|
||||
],
|
||||
"IR Opcode": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/Opcode.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",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/InstructionImports.qll"
|
||||
],
|
||||
"C++ IR IRImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRImports.qll"
|
||||
],
|
||||
"C++ IR IRBlockImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRBlockImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRBlockImports.qll"
|
||||
],
|
||||
"C++ IR IRVariableImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRVariableImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRVariableImports.qll"
|
||||
],
|
||||
"C++ IR OperandImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/OperandImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/OperandImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/OperandImports.qll"
|
||||
],
|
||||
"C++ IR PrintIRImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/PrintIRImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintIRImports.qll"
|
||||
],
|
||||
"C++ SSA SSAConstructionImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll"
|
||||
],
|
||||
"C++ SSA AliasAnalysis": [
|
||||
"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"
|
||||
],
|
||||
"C++ SSA SSAConstruction": [
|
||||
"C++ IR ValueNumberingImports": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingImports.qll"
|
||||
],
|
||||
"IR SSA SimpleSSA": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll"
|
||||
],
|
||||
"IR SSA SSAConstruction": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll"
|
||||
],
|
||||
"C++ SSA PrintSSA": [
|
||||
"IR SSA PrintSSA": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll"
|
||||
],
|
||||
"C++ IR ValueNumber": [
|
||||
"IR ValueNumber": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll"
|
||||
],
|
||||
"C++ IR ConstantAnalysis": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll",
|
||||
@@ -104,5 +232,33 @@
|
||||
"C++ IR PrintDominance": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintDominance.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
|
||||
],
|
||||
"C# IR InstructionImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionImports.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll"
|
||||
],
|
||||
"C# IR IRImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRImports.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRImports.qll"
|
||||
],
|
||||
"C# IR IRBlockImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRBlockImports.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll"
|
||||
],
|
||||
"C# IR IRVariableImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRVariableImports.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll"
|
||||
],
|
||||
"C# IR OperandImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/OperandImports.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/OperandImports.qll"
|
||||
],
|
||||
"C# IR PrintIRImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/PrintIRImports.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll"
|
||||
],
|
||||
"C# IR ValueNumberingImports": [
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll"
|
||||
]
|
||||
}
|
||||
|
||||
1
cpp/ql/examples/queries.xml
Normal file
1
cpp/ql/examples/queries.xml
Normal file
@@ -0,0 +1 @@
|
||||
<queries language="cpp"/>
|
||||
16
cpp/ql/examples/snippets/addressof.ql
Normal file
16
cpp/ql/examples/snippets/addressof.ql
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @id cpp/examples/addressof
|
||||
* @name Address of reference variable
|
||||
* @description Finds address-of expressions (`&`) that take the address
|
||||
* of a reference variable
|
||||
* @tags addressof
|
||||
* reference
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from AddressOfExpr addr, VariableAccess access
|
||||
where
|
||||
access = addr.getOperand() and
|
||||
access.getTarget().getType() instanceof ReferenceType
|
||||
select addr
|
||||
17
cpp/ql/examples/snippets/arrayaccess.ql
Normal file
17
cpp/ql/examples/snippets/arrayaccess.ql
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @id cpp/examples/arrayaccess
|
||||
* @name Array access
|
||||
* @description Finds array access expressions with an index expression
|
||||
* consisting of a postfix increment (`++`) expression.
|
||||
* @tags array
|
||||
* access
|
||||
* index
|
||||
* postfix
|
||||
* increment
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from ArrayExpr a
|
||||
where a.getArrayOffset() instanceof PostfixIncrExpr
|
||||
select a
|
||||
17
cpp/ql/examples/snippets/castexpr.ql
Normal file
17
cpp/ql/examples/snippets/castexpr.ql
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @id cpp/examples/castexpr
|
||||
* @name Cast expressions
|
||||
* @description Finds casts from a floating point type to an integer type
|
||||
* @tags cast
|
||||
* integer
|
||||
* float
|
||||
* type
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Cast c
|
||||
where
|
||||
c.getExpr().getType() instanceof FloatingPointType and
|
||||
c.getType() instanceof IntegralType
|
||||
select c
|
||||
15
cpp/ql/examples/snippets/catch_exception.ql
Normal file
15
cpp/ql/examples/snippets/catch_exception.ql
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @id cpp/examples/catch-exception
|
||||
* @name Catch exception
|
||||
* @description Finds places where we catch exceptions of type `parse_error`
|
||||
* @tags catch
|
||||
* try
|
||||
* exception
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from CatchBlock catch
|
||||
// `stripType` converts `const parse_error &` to `parse_error`.
|
||||
where catch.getParameter().getType().stripType().hasName("parse_error")
|
||||
select catch
|
||||
16
cpp/ql/examples/snippets/constructor_call.ql
Normal file
16
cpp/ql/examples/snippets/constructor_call.ql
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @id cpp/examples/constructor-call
|
||||
* @name Call to constructor
|
||||
* @description Finds places where we call `new MyClass(...)`
|
||||
* @tags call
|
||||
* constructor
|
||||
* new
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from NewExpr new, Constructor c
|
||||
where
|
||||
c = new.getInitializer().(ConstructorCall).getTarget() and
|
||||
c.getName() = "MyClass"
|
||||
select new
|
||||
20
cpp/ql/examples/snippets/derives_from_class.ql
Normal file
20
cpp/ql/examples/snippets/derives_from_class.ql
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @id cpp/examples/derives-from-class
|
||||
* @name Class derives from
|
||||
* @description Finds classes that derive from `std::exception`
|
||||
* @tags base
|
||||
* class
|
||||
* derive
|
||||
* inherit
|
||||
* override
|
||||
* subtype
|
||||
* supertype
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Class type
|
||||
where
|
||||
type.getABaseClass+().hasName("exception") and
|
||||
type.getNamespace().getName() = "std"
|
||||
select type
|
||||
14
cpp/ql/examples/snippets/emptyblock.ql
Normal file
14
cpp/ql/examples/snippets/emptyblock.ql
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @id cpp/examples/emptyblock
|
||||
* @name Empty blocks
|
||||
* @description Finds empty block statements
|
||||
* @tags empty
|
||||
* block
|
||||
* statement
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Block blk
|
||||
where blk.getNumStmt() = 0
|
||||
select blk
|
||||
17
cpp/ql/examples/snippets/emptythen.ql
Normal file
17
cpp/ql/examples/snippets/emptythen.ql
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @id cpp/examples/emptythen
|
||||
* @name If statements with empty then branch
|
||||
* @description Finds `if` statements where the `then` branch is
|
||||
* an empty block statement
|
||||
* @tags if
|
||||
* then
|
||||
* empty
|
||||
* conditional
|
||||
* branch
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from IfStmt i
|
||||
where i.getThen().(Block).getNumStmt() = 0
|
||||
select i
|
||||
18
cpp/ql/examples/snippets/eq_true.ql
Normal file
18
cpp/ql/examples/snippets/eq_true.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @id cpp/examples/eq-true
|
||||
* @name Equality test on boolean
|
||||
* @description Finds tests like `==true`, `!=true`
|
||||
* @tags equal
|
||||
* comparison
|
||||
* test
|
||||
* boolean
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from EqualityOperation eq, Expr trueExpr
|
||||
where
|
||||
trueExpr = eq.getAnOperand() and
|
||||
trueExpr.getType() instanceof BoolType and
|
||||
trueExpr.getValue().toInt() = 1
|
||||
select eq
|
||||
17
cpp/ql/examples/snippets/field_access.ql
Normal file
17
cpp/ql/examples/snippets/field_access.ql
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @id cpp/examples/field-access
|
||||
* @name Access of field
|
||||
* @description Finds reads of `aDate` (defined on class `Order`)
|
||||
* @tags access
|
||||
* field
|
||||
* read
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Field f, FieldAccess access
|
||||
where
|
||||
f.hasName("aDate") and
|
||||
f.getDeclaringType().hasName("Order") and
|
||||
f = access.getTarget()
|
||||
select access
|
||||
18
cpp/ql/examples/snippets/function_call.ql
Normal file
18
cpp/ql/examples/snippets/function_call.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @id cpp/examples/function-call
|
||||
* @name Call to function
|
||||
* @description Finds calls to `std::map<...>::find()`
|
||||
* @tags call
|
||||
* function
|
||||
* method
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from FunctionCall call, Function fcn
|
||||
where
|
||||
call.getTarget() = fcn and
|
||||
fcn.getDeclaringType().getSimpleName() = "map" and
|
||||
fcn.getDeclaringType().getNamespace().getName() = "std" and
|
||||
fcn.hasName("find")
|
||||
select call
|
||||
15
cpp/ql/examples/snippets/integer_literal.ql
Normal file
15
cpp/ql/examples/snippets/integer_literal.ql
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @id cpp/examples/integer-literal
|
||||
* @name Integer literal
|
||||
* @description Finds places where we use the integer literal `2`
|
||||
* @tags integer
|
||||
* literal
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Literal literal
|
||||
where
|
||||
literal.getType() instanceof IntType and
|
||||
literal.getValue().toInt() = 2
|
||||
select literal
|
||||
17
cpp/ql/examples/snippets/mutualrecursion.ql
Normal file
17
cpp/ql/examples/snippets/mutualrecursion.ql
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @id cpp/examples/mutualrecursion
|
||||
* @name Mutual recursion
|
||||
* @description Finds pairs of functions that call each other
|
||||
* @tags function
|
||||
* method
|
||||
* recursion
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Function m, Function n
|
||||
where
|
||||
exists(FunctionCall c | c.getEnclosingFunction() = m and c.getTarget() = n) and
|
||||
exists(FunctionCall c | c.getEnclosingFunction() = n and c.getTarget() = m) and
|
||||
m != n
|
||||
select m, n
|
||||
18
cpp/ql/examples/snippets/override_method.ql
Normal file
18
cpp/ql/examples/snippets/override_method.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @id cpp/examples/override-method
|
||||
* @name Override of method
|
||||
* @description Finds methods that override `std::exception::what()`
|
||||
* @tags function
|
||||
* method
|
||||
* override
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from MemberFunction override, MemberFunction base
|
||||
where
|
||||
base.getName() = "what" and
|
||||
base.getDeclaringType().getName() = "exception" and
|
||||
base.getDeclaringType().getNamespace().getName() = "std" and
|
||||
override.overrides+(base)
|
||||
select override
|
||||
14
cpp/ql/examples/snippets/returnstatement.ql
Normal file
14
cpp/ql/examples/snippets/returnstatement.ql
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @id cpp/examples/returnstatement
|
||||
* @name Return statements
|
||||
* @description Finds return statements that return `0`
|
||||
* @tags return
|
||||
* statement
|
||||
* literal
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from ReturnStmt r
|
||||
where r.getExpr().(Literal).getValue().toInt() = 0
|
||||
select r
|
||||
13
cpp/ql/examples/snippets/singletonblock.ql
Normal file
13
cpp/ql/examples/snippets/singletonblock.ql
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @id cpp/examples/singletonblock
|
||||
* @name Singleton blocks
|
||||
* @description Finds block statements containing a single statement
|
||||
* @tags block
|
||||
* statement
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Block b
|
||||
where b.getNumStmt() = 1
|
||||
select b
|
||||
17
cpp/ql/examples/snippets/switchcase.ql
Normal file
17
cpp/ql/examples/snippets/switchcase.ql
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @id cpp/examples/switchcase
|
||||
* @name Switch statement case missing
|
||||
* @description Finds switch statements with a missing enum constant case
|
||||
* and no default case
|
||||
* @tags switch
|
||||
* case
|
||||
* enum
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from EnumSwitch es, EnumConstant ec
|
||||
where
|
||||
ec = es.getAMissingCase() and
|
||||
not es.hasDefaultCase()
|
||||
select es, ec
|
||||
15
cpp/ql/examples/snippets/ternaryconditional.ql
Normal file
15
cpp/ql/examples/snippets/ternaryconditional.ql
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @id cpp/examples/ternaryconditional
|
||||
* @name Conditional expressions
|
||||
* @description Finds conditional expressions of the form `... ? ... : ...`
|
||||
* where the types of the resulting expressions differ
|
||||
* @tags conditional
|
||||
* ternary
|
||||
* type
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from ConditionalExpr e
|
||||
where e.getThen().getType() != e.getElse().getType()
|
||||
select e
|
||||
15
cpp/ql/examples/snippets/throw_exception.ql
Normal file
15
cpp/ql/examples/snippets/throw_exception.ql
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @id cpp/examples/throw-exception
|
||||
* @name Throw exception of type
|
||||
* @description Finds places where we throw `parse_error` or one of its sub-types
|
||||
* @tags base
|
||||
* class
|
||||
* throw
|
||||
* exception
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from ThrowExpr throw
|
||||
where throw.getType().(Class).getABaseClass*().getName() = "parse_error"
|
||||
select throw
|
||||
14
cpp/ql/examples/snippets/todocomment.ql
Normal file
14
cpp/ql/examples/snippets/todocomment.ql
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @id cpp/examples/todocomment
|
||||
* @name TODO comments
|
||||
* @description Finds comments containing the word "TODO"
|
||||
* @tags comment
|
||||
* matches
|
||||
* TODO
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Comment c
|
||||
where c.getContents().matches("%TODO%")
|
||||
select c
|
||||
15
cpp/ql/examples/snippets/toomanyparams.ql
Normal file
15
cpp/ql/examples/snippets/toomanyparams.ql
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @id cpp/examples/toomanyparams
|
||||
* @name Functions with many parameters
|
||||
* @description Finds functions or methods with more than 10 parameters
|
||||
* @tags function
|
||||
* method
|
||||
* parameter
|
||||
* argument
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Function fcn
|
||||
where fcn.getNumberOfParameters() > 10
|
||||
select fcn
|
||||
16
cpp/ql/examples/snippets/unusedlocalvar.ql
Normal file
16
cpp/ql/examples/snippets/unusedlocalvar.ql
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @id cpp/examples/unusedlocalvar
|
||||
* @name Unused local variable
|
||||
* @description Finds local variables that are not accessed
|
||||
* @tags variable
|
||||
* local
|
||||
* access
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from LocalScopeVariable v
|
||||
where
|
||||
not v instanceof Parameter and
|
||||
not exists(v.getAnAccess())
|
||||
select v
|
||||
18
cpp/ql/examples/snippets/unusedmethod.ql
Normal file
18
cpp/ql/examples/snippets/unusedmethod.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @id cpp/examples/unusedmethod
|
||||
* @name Unused private method
|
||||
* @description Finds private non-virtual methods that are not accessed
|
||||
* @tags method
|
||||
* access
|
||||
* private
|
||||
* virtual
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from MemberFunction fcn
|
||||
where
|
||||
fcn.isPrivate() and
|
||||
not fcn.isVirtual() and
|
||||
not exists(FunctionCall call | fcn = call.getTarget())
|
||||
select fcn.getDefinition()
|
||||
13
cpp/ql/examples/snippets/unusedparam.ql
Normal file
13
cpp/ql/examples/snippets/unusedparam.ql
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @id cpp/examples/unusedparam
|
||||
* @name Unused parameter
|
||||
* @description Finds parameters that are not accessed
|
||||
* @tags parameter
|
||||
* access
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Parameter p
|
||||
where p.isNamed() and not exists(p.getAnAccess())
|
||||
select p
|
||||
21
cpp/ql/examples/snippets/voidreturntype.ql
Normal file
21
cpp/ql/examples/snippets/voidreturntype.ql
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @id cpp/examples/voidreturntype
|
||||
* @name Const method without return type
|
||||
* @description Finds const methods whose return type is `void`
|
||||
* @tags const
|
||||
* function
|
||||
* method
|
||||
* modifier
|
||||
* specifier
|
||||
* return
|
||||
* type
|
||||
* void
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from MemberFunction m
|
||||
where
|
||||
m.hasSpecifier("const") and
|
||||
m.getType() instanceof VoidType
|
||||
select m
|
||||
13
cpp/ql/examples/snippets/volatilevariable.ql
Normal file
13
cpp/ql/examples/snippets/volatilevariable.ql
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @id cpp/examples/volatilevariable
|
||||
* @name Variable declared volatile
|
||||
* @description Finds variables with a `volatile` modifier
|
||||
* @tags variable
|
||||
* volatile
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Variable f
|
||||
where f.isVolatile()
|
||||
select f
|
||||
@@ -16,64 +16,56 @@ class SuppressionComment extends CppStyleComment {
|
||||
|
||||
SuppressionComment() {
|
||||
text = getContents().suffix(2) and
|
||||
( // match `lgtm[...]` anywhere in the comment
|
||||
(
|
||||
// match `lgtm[...]` anywhere in the comment
|
||||
annotation = text.regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _)
|
||||
or
|
||||
// match `lgtm` at the start of the comment and after semicolon
|
||||
annotation = text.regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _)
|
||||
.trim()
|
||||
annotation = text.regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _).trim()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/** Gets the text in this comment, excluding the leading //. */
|
||||
string getText() {
|
||||
result = text
|
||||
}
|
||||
string getText() { result = text }
|
||||
|
||||
/** Gets the suppression annotation in this comment. */
|
||||
string getAnnotation() {
|
||||
result = annotation
|
||||
}
|
||||
string getAnnotation() { result = annotation }
|
||||
|
||||
/**
|
||||
* Holds if this comment applies to the range from column `startcolumn` of line `startline`
|
||||
* to column `endcolumn` of line `endline` in file `filepath`.
|
||||
*/
|
||||
* Holds if this comment applies to the range from column `startcolumn` of line `startline`
|
||||
* to column `endcolumn` of line `endline` in file `filepath`.
|
||||
*/
|
||||
predicate covers(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
|
||||
this.getLocation().hasLocationInfo(filepath, startline, _, endline, endcolumn) and
|
||||
startcolumn = 1
|
||||
}
|
||||
|
||||
/** Gets the scope of this suppression. */
|
||||
SuppressionScope getScope() {
|
||||
result = this
|
||||
}
|
||||
SuppressionScope getScope() { result = this }
|
||||
}
|
||||
|
||||
/**
|
||||
* The scope of an alert suppression comment.
|
||||
*/
|
||||
class SuppressionScope extends ElementBase {
|
||||
SuppressionScope() {
|
||||
this instanceof SuppressionComment
|
||||
}
|
||||
SuppressionScope() { this instanceof SuppressionComment }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.(SuppressionComment).covers(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
from SuppressionComment c
|
||||
select c, // suppression comment
|
||||
c.getText(), // text of suppression comment (excluding delimiters)
|
||||
c.getAnnotation(), // text of suppression annotation
|
||||
c.getScope() // scope of suppression
|
||||
|
||||
select c, // suppression comment
|
||||
c.getText(), // text of suppression comment (excluding delimiters)
|
||||
c.getAnnotation(), // text of suppression annotation
|
||||
c.getScope() // scope of suppression
|
||||
|
||||
@@ -42,9 +42,7 @@ newtype TVariableDeclarationInfo =
|
||||
*/
|
||||
class VariableDeclarationLine extends TVariableDeclarationInfo {
|
||||
Class c;
|
||||
|
||||
File f;
|
||||
|
||||
int line;
|
||||
|
||||
VariableDeclarationLine() {
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
|
||||
import cpp
|
||||
|
||||
//see http://www.cs.ualberta.ca/~hoover/Courses/201/201-New-Notes/lectures/section/slice.htm
|
||||
//Does not find anything in rivers (unfortunately)
|
||||
from AssignExpr e, Class lhsType, Class rhsType
|
||||
where
|
||||
e.getLValue().getType() = lhsType and
|
||||
@@ -22,6 +20,6 @@ where
|
||||
exists(Declaration m |
|
||||
rhsType.getAMember() = m and
|
||||
not m.(VirtualFunction).isPure()
|
||||
) //add additional checks for concrete members in in-between supertypes
|
||||
) // add additional checks for concrete members in in-between supertypes
|
||||
select e, "This assignment expression slices from type $@ to $@", rhsType, rhsType.getName(),
|
||||
lhsType, lhsType.getName()
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
When eras change, date and time conversions that rely on a hard-coded era start date need to be reviewed. Conversions relying on Japanese dates in the current era can produce an ambiguous date.
|
||||
The values for the current Japanese era dates should be read from a source that will be updated, such as the Windows registry.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
<a href="https://blogs.msdn.microsoft.com/shawnste/2018/04/12/the-japanese-calendars-y2k-moment/">The Japanese Calendar's Y2K Moment</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
72
cpp/ql/src/Best Practices/Magic Constants/JapaneseEraDate.ql
Normal file
72
cpp/ql/src/Best Practices/Magic Constants/JapaneseEraDate.ql
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* @name Hard-coded Japanese era start date
|
||||
* @description Japanese era changes can lead to code behaving differently. Avoid hard-coding Japanese era start dates.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id cpp/japanese-era/exact-era-date
|
||||
* @precision medium
|
||||
* @tags reliability
|
||||
* japanese-era
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.DateTime
|
||||
|
||||
predicate assignedYear(Struct s, YearFieldAccess year, int value) {
|
||||
exists(Operation yearAssignment |
|
||||
s.getAField().getAnAccess() = year and
|
||||
yearAssignment.getAnOperand() = year and
|
||||
yearAssignment.getAnOperand().getValue().toInt() = value
|
||||
)
|
||||
}
|
||||
|
||||
predicate assignedMonth(Struct s, MonthFieldAccess month, int value) {
|
||||
exists(Operation monthAssignment |
|
||||
s.getAField().getAnAccess() = month and
|
||||
monthAssignment.getAnOperand() = month and
|
||||
monthAssignment.getAnOperand().getValue().toInt() = value
|
||||
)
|
||||
}
|
||||
|
||||
predicate assignedDay(Struct s, DayFieldAccess day, int value) {
|
||||
exists(Operation dayAssignment |
|
||||
s.getAField().getAnAccess() = day and
|
||||
dayAssignment.getAnOperand() = day and
|
||||
dayAssignment.getAnOperand().getValue().toInt() = value
|
||||
)
|
||||
}
|
||||
|
||||
predicate eraDate(int year, int month, int day) {
|
||||
year = 1989 and month = 1 and day = 8
|
||||
or
|
||||
year = 2019 and month = 5 and day = 1
|
||||
}
|
||||
|
||||
predicate badStructInitialization(Element target, string message) {
|
||||
exists(
|
||||
StructLikeClass s, YearFieldAccess year, MonthFieldAccess month, DayFieldAccess day,
|
||||
int yearValue, int monthValue, int dayValue
|
||||
|
|
||||
eraDate(yearValue, monthValue, dayValue) and
|
||||
assignedYear(s, year, yearValue) and
|
||||
assignedMonth(s, month, monthValue) and
|
||||
assignedDay(s, day, dayValue) and
|
||||
target = year and
|
||||
message = "A time struct that is initialized with exact Japanese calendar era start date."
|
||||
)
|
||||
}
|
||||
|
||||
predicate badCall(Element target, string message) {
|
||||
exists(Call cc, int i |
|
||||
eraDate(cc.getArgument(i).getValue().toInt(), cc.getArgument(i + 1).getValue().toInt(),
|
||||
cc.getArgument(i + 2).getValue().toInt()) and
|
||||
target = cc and
|
||||
message = "Call that appears to have hard-coded Japanese era start date as parameter."
|
||||
)
|
||||
}
|
||||
|
||||
from Element target, string message
|
||||
where
|
||||
badStructInitialization(target, message) or
|
||||
badCall(target, message)
|
||||
select target, message
|
||||
@@ -14,9 +14,7 @@
|
||||
import cpp
|
||||
|
||||
predicate declarationHasSideEffects(Variable v) {
|
||||
exists(Class c | c = v.getUnspecifiedType() |
|
||||
c.hasConstructor() or c.hasDestructor()
|
||||
)
|
||||
exists(Class c | c = v.getUnspecifiedType() | c.hasConstructor() or c.hasDestructor())
|
||||
}
|
||||
|
||||
from Variable v
|
||||
|
||||
@@ -26,20 +26,18 @@ class MinusOne extends NullValue {
|
||||
*/
|
||||
predicate mayCallFunction(Expr call, Function f) {
|
||||
call.(FunctionCall).getTarget() = f or
|
||||
call.(VariableCall).getVariable().getAnAssignedValue().
|
||||
getAChild*().(FunctionAccess).getTarget() = f
|
||||
call.(VariableCall).getVariable().getAnAssignedValue().getAChild*().(FunctionAccess).getTarget() =
|
||||
f
|
||||
}
|
||||
|
||||
predicate fopenCallOrIndirect(Expr e) {
|
||||
// direct fopen call
|
||||
fopenCall(e) and
|
||||
|
||||
// We are only interested in fopen calls that are
|
||||
// actually closed somehow, as FileNeverClosed
|
||||
// will catch those that aren't.
|
||||
fopenCallMayBeClosed(e)
|
||||
or
|
||||
|
||||
exists(ReturnStmt rtn |
|
||||
// indirect fopen call
|
||||
mayCallFunction(e, rtn.getEnclosingFunction()) and
|
||||
@@ -86,7 +84,6 @@ class FOpenVariableReachability extends LocalScopeVariableReachabilityWithReassi
|
||||
exists(node.(AnalysedExpr).getNullSuccessor(v)) or
|
||||
fcloseCallOrIndirect(node, v) or
|
||||
assignedToFieldOrGlobal(v, node) or
|
||||
|
||||
// node may be used directly in query
|
||||
v.getFunction() = node.(ReturnStmt).getEnclosingFunction()
|
||||
}
|
||||
@@ -122,12 +119,10 @@ class FOpenReachability extends LocalScopeVariableReachabilityExt {
|
||||
}
|
||||
|
||||
override predicate isBarrier(
|
||||
ControlFlowNode source, ControlFlowNode node, ControlFlowNode next,
|
||||
LocalScopeVariable v)
|
||||
{
|
||||
ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, LocalScopeVariable 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) |
|
||||
@@ -172,6 +167,4 @@ where
|
||||
fopenVariableReaches(v, def, ret) and
|
||||
ret.getAChild*() = v.getAnAccess()
|
||||
)
|
||||
select
|
||||
def, "The file opened here may not be closed at $@.",
|
||||
ret, "this exit point"
|
||||
select def, "The file opened here may not be closed at $@.", ret, "this exit point"
|
||||
|
||||
@@ -18,20 +18,18 @@ import semmle.code.cpp.controlflow.LocalScopeVariableReachability
|
||||
*/
|
||||
predicate mayCallFunction(Expr call, Function f) {
|
||||
call.(FunctionCall).getTarget() = f or
|
||||
call.(VariableCall).getVariable().getAnAssignedValue().
|
||||
getAChild*().(FunctionAccess).getTarget() = f
|
||||
call.(VariableCall).getVariable().getAnAssignedValue().getAChild*().(FunctionAccess).getTarget() =
|
||||
f
|
||||
}
|
||||
|
||||
predicate allocCallOrIndirect(Expr e) {
|
||||
// direct alloc call
|
||||
isAllocationExpr(e) and
|
||||
|
||||
// We are only interested in alloc calls that are
|
||||
// actually freed somehow, as MemoryNeverFreed
|
||||
// will catch those that aren't.
|
||||
allocMayBeFreed(e)
|
||||
or
|
||||
|
||||
exists(ReturnStmt rtn |
|
||||
// indirect alloc call
|
||||
mayCallFunction(e, rtn.getEnclosingFunction()) and
|
||||
@@ -64,7 +62,6 @@ predicate verifiedRealloc(FunctionCall reallocCall, Variable v, ControlFlowNode
|
||||
newV.getAnAssignedValue() = reallocCall and
|
||||
node.(AnalysedExpr).getNonNullSuccessor(newV) = verified and
|
||||
// note: this case uses naive flow logic (getAnAssignedValue).
|
||||
|
||||
// special case: if the result of the 'realloc' is assigned to the
|
||||
// same variable, we don't descriminate properly between the old
|
||||
// and the new allocation; better to not consider this a free at
|
||||
@@ -116,7 +113,6 @@ class AllocVariableReachability extends LocalScopeVariableReachabilityWithReassi
|
||||
exists(node.(AnalysedExpr).getNullSuccessor(v)) or
|
||||
freeCallOrIndirect(node, v) or
|
||||
assignedToFieldOrGlobal(v, node) or
|
||||
|
||||
// node may be used directly in query
|
||||
v.getFunction() = node.(ReturnStmt).getEnclosingFunction()
|
||||
}
|
||||
@@ -152,12 +148,10 @@ class AllocReachability extends LocalScopeVariableReachabilityExt {
|
||||
}
|
||||
|
||||
override predicate isBarrier(
|
||||
ControlFlowNode source, ControlFlowNode node, ControlFlowNode next,
|
||||
LocalScopeVariable v)
|
||||
{
|
||||
ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, LocalScopeVariable 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) |
|
||||
@@ -202,6 +196,4 @@ where
|
||||
allocatedVariableReaches(v, def, ret) and
|
||||
ret.getAChild*() = v.getAnAccess()
|
||||
)
|
||||
select
|
||||
def, "The memory allocated here may not be released at $@.",
|
||||
ret, "this exit point"
|
||||
select def, "The memory allocated here may not be released at $@.", ret, "this exit point"
|
||||
|
||||
@@ -47,7 +47,7 @@ predicate allocExprOrIndirect(Expr alloc, string kind) {
|
||||
or
|
||||
exists(Expr e |
|
||||
allocExprOrIndirect(e, kind) and
|
||||
DataFlow::localFlow(DataFlow::exprNode(e), DataFlow::exprNode(rtn.getExpr()))
|
||||
DataFlow::localExprFlow(e, rtn.getExpr())
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -8,9 +8,11 @@
|
||||
* external/cwe/cwe-457
|
||||
*/
|
||||
|
||||
import cpp
|
||||
/*
|
||||
* See also InitialisationNotRun.ql and GlobalUseBeforeInit.ql
|
||||
*/
|
||||
|
||||
// See also InitialisationNotRun.ql and GlobalUseBeforeInit.ql
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Holds if `s` defines variable `v` (conservative).
|
||||
|
||||
@@ -33,12 +33,10 @@ predicate sourceSized(FunctionCall fc, Expr src) {
|
||||
fc.getArgument(2) = size and
|
||||
src = v.getAnAccess() and
|
||||
size.getAChild+() = v.getAnAccess() and
|
||||
|
||||
// exception: `dest` is also referenced in the size argument
|
||||
not exists(Variable other |
|
||||
dest = other.getAnAccess() and size.getAChild+() = other.getAnAccess()
|
||||
) and
|
||||
|
||||
// exception: `src` and `dest` are both arrays of the same type and size
|
||||
not exists(ArrayType srctype, ArrayType desttype |
|
||||
dest.getType().getUnderlyingType() = desttype and
|
||||
|
||||
@@ -33,7 +33,6 @@ class BufferAccess extends ArrayExpr {
|
||||
staticBuffer(this.getArrayBase(), _, size) and
|
||||
size != 0
|
||||
) and
|
||||
|
||||
// exclude accesses in macro implementation of `strcmp`,
|
||||
// which are carefully controlled but can look dangerous.
|
||||
not exists(Macro m |
|
||||
@@ -95,7 +94,7 @@ class CallWithBufferSize extends FunctionCall {
|
||||
|
||||
int statedSizeValue() {
|
||||
exists(Expr statedSizeSrc |
|
||||
DataFlow::localFlow(DataFlow::exprNode(statedSizeSrc), DataFlow::exprNode(statedSizeExpr())) and
|
||||
DataFlow::localExprFlow(statedSizeSrc, statedSizeExpr()) and
|
||||
result = statedSizeSrc.getValue().toInt()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,14 +13,11 @@ private import Options as CustomOptions
|
||||
|
||||
/**
|
||||
* Default predicates that specify information about the behavior of
|
||||
* the program being analyzed.
|
||||
* the program being analyzed.
|
||||
*/
|
||||
class Options extends string
|
||||
{
|
||||
Options() {
|
||||
this = "Options"
|
||||
}
|
||||
|
||||
class Options extends string {
|
||||
Options() { this = "Options" }
|
||||
|
||||
/**
|
||||
* Holds if we wish to override the "may return NULL" inference for this
|
||||
* call. If this holds, then rather than trying to infer whether this
|
||||
@@ -60,7 +57,8 @@ class Options extends string
|
||||
* `noreturn` attribute.
|
||||
*/
|
||||
predicate exits(Function f) {
|
||||
f.getAnAttribute().hasName("noreturn") or
|
||||
f.getAnAttribute().hasName("noreturn")
|
||||
or
|
||||
exists(string name | f.hasGlobalName(name) |
|
||||
name = "exit" or
|
||||
name = "_exit" or
|
||||
@@ -68,7 +66,8 @@ class Options extends string
|
||||
name = "__assert_fail" or
|
||||
name = "longjmp" or
|
||||
name = "__builtin_unreachable"
|
||||
) or
|
||||
)
|
||||
or
|
||||
CustomOptions::exits(f) // old Options.qll
|
||||
}
|
||||
|
||||
@@ -108,14 +107,11 @@ class Options extends string
|
||||
fc.isInMacroExpansion()
|
||||
or
|
||||
// common way of sleeping using select:
|
||||
(fc.getTarget().hasGlobalName("select") and
|
||||
fc.getArgument(0).getValue() = "0")
|
||||
fc.getTarget().hasGlobalName("select") and
|
||||
fc.getArgument(0).getValue() = "0"
|
||||
or
|
||||
CustomOptions::okToIgnoreReturnValue(fc) // old Options.qll
|
||||
}
|
||||
}
|
||||
|
||||
Options getOptions()
|
||||
{
|
||||
any()
|
||||
}
|
||||
Options getOptions() { any() }
|
||||
|
||||
@@ -1,26 +1,36 @@
|
||||
/**
|
||||
* Provides heuristics to find "todo" and "fixme" comments (in all caps).
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
string getCommentTextCaptioned(Comment c, string caption) {
|
||||
(caption = "TODO" or caption = "FIXME") and
|
||||
exists (string commentContents, string commentBody, int offset, string interestingSuffix, int endOfLine, string dontCare, string captionedLine, string followingLine
|
||||
| commentContents = c.getContents()
|
||||
and commentContents.matches("%" + caption + "%")
|
||||
and // Add some '\n's so that any interesting line, and its
|
||||
// following line, will definitely begin and end with '\n'.
|
||||
commentBody = commentContents.regexpReplaceAll("(?s)^/\\*(.*)\\*/$|^//(.*)$", "\n$1$2\n\n")
|
||||
and 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.prefix(endOfLine).regexpReplaceAll("^[/*\\s]*" + caption + "\\s*:?", "").trim()
|
||||
and followingLine = interestingSuffix.prefix(interestingSuffix.indexOf("\n", 2, 0)).suffix(endOfLine).trim()
|
||||
and if captionedLine = ""
|
||||
then result = caption + " comment"
|
||||
else if followingLine = ""
|
||||
then result = caption + " comment: " + captionedLine
|
||||
else result = caption + " comment: " + captionedLine + " [...]"
|
||||
)
|
||||
(caption = "TODO" or caption = "FIXME") and
|
||||
exists(
|
||||
string commentContents, string commentBody, int offset, string interestingSuffix, int endOfLine,
|
||||
string dontCare, string captionedLine, string followingLine
|
||||
|
|
||||
commentContents = c.getContents() and
|
||||
commentContents.matches("%" + caption + "%") and
|
||||
// Add some '\n's so that any interesting line, and its
|
||||
// following line, will definitely begin and end with '\n'.
|
||||
commentBody = commentContents.regexpReplaceAll("(?s)^/\\*(.*)\\*/$|^//(.*)$", "\n$1$2\n\n") and
|
||||
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
|
||||
.prefix(endOfLine)
|
||||
.regexpReplaceAll("^[/*\\s]*" + caption + "\\s*:?", "")
|
||||
.trim() and
|
||||
followingLine = interestingSuffix
|
||||
.prefix(interestingSuffix.indexOf("\n", 2, 0))
|
||||
.suffix(endOfLine)
|
||||
.trim() and
|
||||
if captionedLine = ""
|
||||
then result = caption + " comment"
|
||||
else
|
||||
if followingLine = ""
|
||||
then result = caption + " comment: " + captionedLine
|
||||
else result = caption + " comment: " + captionedLine + " [...]"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,44 +6,42 @@ import cpp
|
||||
bindingset[line]
|
||||
private predicate looksLikeCode(string line) {
|
||||
exists(string trimmed |
|
||||
// trim leading and trailing whitespace, and HTML codes:
|
||||
// trim leading and trailing whitespace, and HTML codes:
|
||||
// * HTML entities in common notation (e.g. &gt; and &eacute;)
|
||||
// * HTML entities in decimal notation (e.g. a&#768;)
|
||||
// * HTML entities in hexadecimal notation (e.g. &#x705F;)
|
||||
trimmed = line.regexpReplaceAll("(?i)(^\\s+|&#?[a-z0-9]{1,31};|\\s+$)", "")
|
||||
|
|
||||
(
|
||||
// Match comment lines ending with '{', '}' or ';'
|
||||
trimmed.regexpMatch(".*[{};]") and
|
||||
(
|
||||
// Match comment lines ending with '{', '}' or ';'
|
||||
trimmed.regexpMatch(".*[{};]") and
|
||||
(
|
||||
// If this line looks like code because it ends with a closing
|
||||
// brace that's preceded by something other than whitespace ...
|
||||
trimmed.regexpMatch(".*.\\}")
|
||||
implies
|
||||
// ... then there has to be ") {" (or some variation)
|
||||
// on the line, suggesting it's a statement like `if`
|
||||
// or a function definition. Otherwise it's likely to be a
|
||||
// benign use of braces such as a JSON example or explanatory
|
||||
// pseudocode.
|
||||
trimmed.regexpMatch(".*(\\)|const|volatile|override|final|noexcept|&)\\s*\\{.*")
|
||||
)
|
||||
) or (
|
||||
// Match comment lines that look like preprocessor code
|
||||
trimmed.regexpMatch("#\\s*(include|define|undef|if|ifdef|ifndef|elif|else|endif|error|pragma)\\b.*")
|
||||
// If this line looks like code because it ends with a closing
|
||||
// brace that's preceded by something other than whitespace ...
|
||||
trimmed.regexpMatch(".*.\\}")
|
||||
implies
|
||||
// ... then there has to be ") {" (or some variation)
|
||||
// on the line, suggesting it's a statement like `if`
|
||||
// or a function definition. Otherwise it's likely to be a
|
||||
// benign use of braces such as a JSON example or explanatory
|
||||
// pseudocode.
|
||||
trimmed.regexpMatch(".*(\\)|const|volatile|override|final|noexcept|&)\\s*\\{.*")
|
||||
)
|
||||
) and (
|
||||
// Exclude lines that start with '>' or contain '@{' or '@}'.
|
||||
// To account for the code generated by protobuf, we also insist that the comment
|
||||
// does not begin with `optional` or `repeated` and end with a `;`, which would
|
||||
// normally be a quoted bit of literal `.proto` specification above the associated
|
||||
// declaration.
|
||||
// To account for emacs folding markers, we ignore any line containing
|
||||
// `{{{` or `}}}`.
|
||||
// Finally, some code tends to embed GUIDs in comments, so we also exclude those.
|
||||
not trimmed
|
||||
or
|
||||
// Match comment lines that look like preprocessor code
|
||||
trimmed
|
||||
.regexpMatch("#\\s*(include|define|undef|if|ifdef|ifndef|elif|else|endif|error|pragma)\\b.*")
|
||||
) and
|
||||
// Exclude lines that start with '>' or contain '@{' or '@}'.
|
||||
// To account for the code generated by protobuf, we also insist that the comment
|
||||
// does not begin with `optional` or `repeated` and end with a `;`, which would
|
||||
// normally be a quoted bit of literal `.proto` specification above the associated
|
||||
// declaration.
|
||||
// To account for emacs folding markers, we ignore any line containing
|
||||
// `{{{` or `}}}`.
|
||||
// Finally, some code tends to embed GUIDs in comments, so we also exclude those.
|
||||
not trimmed
|
||||
.regexpMatch("(>.*|.*[\\\\@][{}].*|(optional|repeated) .*;|.*(\\{\\{\\{|\\}\\}\\}).*|\\{[-0-9a-zA-Z]+\\})")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -76,7 +74,6 @@ private predicate preprocLine(File f, int line) {
|
||||
private int lineInFile(CppStyleComment c, File f) {
|
||||
f = c.getFile() and
|
||||
result = c.getLocation().getStartLine() and
|
||||
|
||||
// Ignore comments on the same line as a preprocessor directive.
|
||||
not preprocLine(f, result)
|
||||
}
|
||||
@@ -119,12 +116,11 @@ class CommentBlock extends Comment {
|
||||
this instanceof CppStyleComment
|
||||
implies
|
||||
not exists(CppStyleComment pred, File f | lineInFile(pred, f) + 1 = lineInFile(this, f))
|
||||
) and (
|
||||
// Ignore comments on the same line as a preprocessor directive.
|
||||
not exists(Location l |
|
||||
l = this.getLocation() and
|
||||
preprocLine(l.getFile(), l.getStartLine())
|
||||
)
|
||||
) and
|
||||
// Ignore comments on the same line as a preprocessor directive.
|
||||
not exists(Location l |
|
||||
l = this.getLocation() and
|
||||
preprocLine(l.getFile(), l.getStartLine())
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,26 +11,26 @@
|
||||
* @tags maintainability
|
||||
* documentation
|
||||
*/
|
||||
import cpp
|
||||
|
||||
import cpp
|
||||
|
||||
predicate isCommented(FunctionDeclarationEntry f) {
|
||||
exists(Comment c | c.getCommentedElement() = f)
|
||||
}
|
||||
|
||||
// Uses of 'f' in 'other'
|
||||
Call uses(File other, Function f) {
|
||||
result.getTarget() = f and result.getFile() = other
|
||||
}
|
||||
Call uses(File other, Function f) { result.getTarget() = f and result.getFile() = other }
|
||||
|
||||
from File callerFile, Function f, Call use, int numCalls
|
||||
where numCalls = strictcount(File other | exists(uses(other, f)) and other != f.getFile())
|
||||
and not isCommented(f.getADeclarationEntry())
|
||||
and not f instanceof Constructor
|
||||
and not f instanceof Destructor
|
||||
and not f.hasName("operator=")
|
||||
and f.getMetrics().getNumberOfLinesOfCode() >= 5
|
||||
and numCalls > 1
|
||||
and use = uses(callerFile, f)
|
||||
and callerFile != f.getFile()
|
||||
select f, "Functions called from other files should be documented (called from $@).", use, use.getFile().getRelativePath()
|
||||
where
|
||||
numCalls = strictcount(File other | exists(uses(other, f)) and other != f.getFile()) and
|
||||
not isCommented(f.getADeclarationEntry()) and
|
||||
not f instanceof Constructor and
|
||||
not f instanceof Destructor and
|
||||
not f.hasName("operator=") and
|
||||
f.getMetrics().getNumberOfLinesOfCode() >= 5 and
|
||||
numCalls > 1 and
|
||||
use = uses(callerFile, f) and
|
||||
callerFile != f.getFile()
|
||||
select f, "Functions called from other files should be documented (called from $@).", use,
|
||||
use.getFile().getRelativePath()
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* documentation
|
||||
* external/cwe/cwe-546
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import Documentation.CaptionedComments
|
||||
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
* documentation
|
||||
* external/cwe/cwe-546
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import Documentation.CaptionedComments
|
||||
|
||||
from Comment c, string message
|
||||
where message = getCommentTextCaptioned(c, "TODO")
|
||||
select c, message
|
||||
|
||||
|
||||
@@ -10,10 +10,14 @@
|
||||
* statistical
|
||||
* non-attributable
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from MetricFunction f, int n
|
||||
where n = f.getNumberOfLines() and n > 100 and
|
||||
f.getCommentRatio() <= 0.02 and
|
||||
not f.isMultiplyDefined()
|
||||
select f, "Poorly documented function: fewer than 2% comments for a function of " + n.toString() + " lines."
|
||||
where
|
||||
n = f.getNumberOfLines() and
|
||||
n > 100 and
|
||||
f.getCommentRatio() <= 0.02 and
|
||||
not f.isMultiplyDefined()
|
||||
select f,
|
||||
"Poorly documented function: fewer than 2% comments for a function of " + n.toString() + " lines."
|
||||
|
||||
@@ -11,20 +11,20 @@
|
||||
import cpp
|
||||
|
||||
predicate markedAsNonterminating(Loop l) {
|
||||
exists(Comment c | c.getContents().matches("%@non-terminating@%") |
|
||||
c.getCommentedElement() = l
|
||||
)
|
||||
exists(Comment c | c.getContents().matches("%@non-terminating@%") | c.getCommentedElement() = l)
|
||||
}
|
||||
|
||||
Stmt exitFrom(Loop l) {
|
||||
l.getAChild+() = result and
|
||||
(result instanceof ReturnStmt or
|
||||
exists(BreakStmt break | break = result |
|
||||
not l.getAChild*() = break.getTarget())
|
||||
(
|
||||
result instanceof ReturnStmt
|
||||
or
|
||||
exists(BreakStmt break | break = result | not l.getAChild*() = break.getTarget())
|
||||
)
|
||||
}
|
||||
|
||||
from Loop l, Stmt exit
|
||||
where markedAsNonterminating(l) and
|
||||
exit = exitFrom(l)
|
||||
where
|
||||
markedAsNonterminating(l) and
|
||||
exit = exitFrom(l)
|
||||
select exit, "$@ should not be exited.", l, "This permanent loop"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name Unbounded loop
|
||||
* @description All loops should have a fixed upper bound; the counter should also be incremented along all paths within the loop.
|
||||
This check excludes loops that are meant to be nonterminating (like schedulers).
|
||||
* This check excludes loops that are meant to be nonterminating (like schedulers).
|
||||
* @kind problem
|
||||
* @id cpp/jpl-c/loop-bounds
|
||||
* @problem.severity warning
|
||||
@@ -30,7 +30,8 @@ predicate upperBoundCheck(Loop loop, VariableAccess checked) {
|
||||
rop.getGreaterOperand().(VariableAccess).getTarget().isConst() or
|
||||
validVarForBound(loop, rop.getGreaterOperand().(VariableAccess).getTarget())
|
||||
) and
|
||||
not rop.getGreaterOperand() instanceof CharLiteral)
|
||||
not rop.getGreaterOperand() instanceof CharLiteral
|
||||
)
|
||||
}
|
||||
|
||||
predicate lowerBoundCheck(Loop loop, VariableAccess checked) {
|
||||
@@ -43,20 +44,23 @@ predicate lowerBoundCheck(Loop loop, VariableAccess checked) {
|
||||
rop.getLesserOperand().(VariableAccess).getTarget().isConst() or
|
||||
validVarForBound(loop, rop.getLesserOperand().(VariableAccess).getTarget())
|
||||
) and
|
||||
not rop.getLesserOperand() instanceof CharLiteral)
|
||||
not rop.getLesserOperand() instanceof CharLiteral
|
||||
)
|
||||
}
|
||||
|
||||
VariableAccess getAnIncrement(Variable var) {
|
||||
result.getTarget() = var and
|
||||
(
|
||||
result.getParent() instanceof IncrementOperation
|
||||
or
|
||||
or
|
||||
exists(AssignAddExpr a | a.getLValue() = result and a.getRValue().getValue().toInt() > 0)
|
||||
or
|
||||
or
|
||||
exists(AssignExpr a | a.getLValue() = result |
|
||||
a.getRValue() =
|
||||
any(AddExpr ae | ae.getAnOperand() = var.getAnAccess() and
|
||||
ae.getAnOperand().getValue().toInt() > 0))
|
||||
a.getRValue() = any(AddExpr ae |
|
||||
ae.getAnOperand() = var.getAnAccess() and
|
||||
ae.getAnOperand().getValue().toInt() > 0
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -64,62 +68,75 @@ VariableAccess getADecrement(Variable var) {
|
||||
result.getTarget() = var and
|
||||
(
|
||||
result.getParent() instanceof DecrementOperation
|
||||
or
|
||||
or
|
||||
exists(AssignSubExpr a | a.getLValue() = result and a.getRValue().getValue().toInt() > 0)
|
||||
or
|
||||
or
|
||||
exists(AssignExpr a | a.getLValue() = result |
|
||||
a.getRValue() =
|
||||
any(SubExpr ae | ae.getLeftOperand() = var.getAnAccess() and
|
||||
ae.getRightOperand().getValue().toInt() > 0))
|
||||
a.getRValue() = any(SubExpr ae |
|
||||
ae.getLeftOperand() = var.getAnAccess() and
|
||||
ae.getRightOperand().getValue().toInt() > 0
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate inScope(Loop l, Stmt s) {
|
||||
l.getAChild*() = s
|
||||
}
|
||||
predicate inScope(Loop l, Stmt s) { l.getAChild*() = s }
|
||||
|
||||
predicate reachesNoInc(VariableAccess source, ControlFlowNode target) {
|
||||
(upperBoundCheck(_, source) and source.getASuccessor() = target) or
|
||||
exists(ControlFlowNode mid | reachesNoInc(source, mid) and not mid = getAnIncrement(source.getTarget()) |
|
||||
target = mid.getASuccessor() and
|
||||
inScope(source.getEnclosingStmt(), target.getEnclosingStmt()))
|
||||
upperBoundCheck(_, source) and source.getASuccessor() = target
|
||||
or
|
||||
exists(ControlFlowNode mid |
|
||||
reachesNoInc(source, mid) and not mid = getAnIncrement(source.getTarget())
|
||||
|
|
||||
target = mid.getASuccessor() and
|
||||
inScope(source.getEnclosingStmt(), target.getEnclosingStmt())
|
||||
)
|
||||
}
|
||||
|
||||
predicate reachesNoDec(VariableAccess source, ControlFlowNode target) {
|
||||
(lowerBoundCheck(_, source) and source.getASuccessor() = target) or
|
||||
exists(ControlFlowNode mid | reachesNoDec(source, mid) and not mid = getADecrement(source.getTarget()) |
|
||||
target = mid.getASuccessor() and
|
||||
inScope(source.getEnclosingStmt(), target.getEnclosingStmt()))
|
||||
}
|
||||
|
||||
predicate hasSafeBound(Loop l) {
|
||||
exists(VariableAccess bound | upperBoundCheck(l, bound) |
|
||||
not reachesNoInc(bound, bound)
|
||||
) or exists(VariableAccess bound | lowerBoundCheck(l, bound) |
|
||||
not reachesNoDec(bound, bound)
|
||||
) or exists(l.getControllingExpr().getValue())
|
||||
}
|
||||
|
||||
predicate markedAsNonterminating(Loop l) {
|
||||
exists(Comment c | c.getContents().matches("%@non-terminating@%") |
|
||||
c.getCommentedElement() = l
|
||||
lowerBoundCheck(_, source) and source.getASuccessor() = target
|
||||
or
|
||||
exists(ControlFlowNode mid |
|
||||
reachesNoDec(source, mid) and not mid = getADecrement(source.getTarget())
|
||||
|
|
||||
target = mid.getASuccessor() and
|
||||
inScope(source.getEnclosingStmt(), target.getEnclosingStmt())
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasSafeBound(Loop l) {
|
||||
exists(VariableAccess bound | upperBoundCheck(l, bound) | not reachesNoInc(bound, bound))
|
||||
or
|
||||
exists(VariableAccess bound | lowerBoundCheck(l, bound) | not reachesNoDec(bound, bound))
|
||||
or
|
||||
exists(l.getControllingExpr().getValue())
|
||||
}
|
||||
|
||||
predicate markedAsNonterminating(Loop l) {
|
||||
exists(Comment c | c.getContents().matches("%@non-terminating@%") | c.getCommentedElement() = l)
|
||||
}
|
||||
|
||||
from Loop loop, string msg
|
||||
where not hasSafeBound(loop) and
|
||||
not markedAsNonterminating(loop) and
|
||||
(
|
||||
(
|
||||
not upperBoundCheck(loop, _) and
|
||||
not lowerBoundCheck(loop, _) and
|
||||
msg = "This loop does not have a fixed bound."
|
||||
) or exists(VariableAccess bound | upperBoundCheck(loop, bound) and
|
||||
reachesNoInc(bound, bound) and
|
||||
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() + " is not always decremented in the loop body."
|
||||
)
|
||||
where
|
||||
not hasSafeBound(loop) and
|
||||
not markedAsNonterminating(loop) and
|
||||
(
|
||||
not upperBoundCheck(loop, _) and
|
||||
not lowerBoundCheck(loop, _) and
|
||||
msg = "This loop does not have a fixed bound."
|
||||
or
|
||||
exists(VariableAccess bound |
|
||||
upperBoundCheck(loop, bound) and
|
||||
reachesNoInc(bound, bound) and
|
||||
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() +
|
||||
" is not always decremented in the loop body."
|
||||
)
|
||||
)
|
||||
select loop, msg
|
||||
|
||||
@@ -13,14 +13,14 @@
|
||||
import cpp
|
||||
|
||||
class RecursiveCall extends FunctionCall {
|
||||
RecursiveCall() {
|
||||
this.getTarget().calls*(this.getEnclosingFunction())
|
||||
}
|
||||
RecursiveCall() { this.getTarget().calls*(this.getEnclosingFunction()) }
|
||||
}
|
||||
|
||||
from RecursiveCall call, string msg
|
||||
where if (call.getTarget() = call.getEnclosingFunction()) then
|
||||
msg = "This call directly invokes its containing function $@."
|
||||
else
|
||||
msg = "The function " + call.getEnclosingFunction() + " is indirectly recursive via this call to $@."
|
||||
where
|
||||
if call.getTarget() = call.getEnclosingFunction()
|
||||
then msg = "This call directly invokes its containing function $@."
|
||||
else
|
||||
msg = "The function " + call.getEnclosingFunction() +
|
||||
" is indirectly recursive via this call to $@."
|
||||
select call, msg, call.getTarget(), call.getTarget().getName()
|
||||
|
||||
@@ -23,12 +23,17 @@ class Initialization extends Function {
|
||||
class Allocation extends FunctionCall {
|
||||
Allocation() {
|
||||
exists(string name | name = this.getTarget().getName() |
|
||||
name = "malloc" or name = "calloc" or name = "alloca" or
|
||||
name = "sbrk" or name = "valloc")
|
||||
name = "malloc" or
|
||||
name = "calloc" or
|
||||
name = "alloca" or
|
||||
name = "sbrk" or
|
||||
name = "valloc"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from Function f, Allocation a
|
||||
where not f instanceof Initialization and
|
||||
a.getEnclosingFunction() = f
|
||||
where
|
||||
not f instanceof Initialization and
|
||||
a.getEnclosingFunction() = f
|
||||
select a, "Dynamic memory allocation is only allowed during initialization."
|
||||
|
||||
@@ -14,8 +14,10 @@ import cpp
|
||||
class ForbiddenCall extends FunctionCall {
|
||||
ForbiddenCall() {
|
||||
exists(string name | name = this.getTarget().getName() |
|
||||
name = "task_delay" or name = "taskDelay" or
|
||||
name = "sleep" or name = "nanosleep" or
|
||||
name = "task_delay" or
|
||||
name = "taskDelay" or
|
||||
name = "sleep" or
|
||||
name = "nanosleep" or
|
||||
name = "clock_nanosleep"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,20 +12,22 @@
|
||||
import Semaphores
|
||||
|
||||
LockOperation maybeLocked(Function f) {
|
||||
result.getEnclosingFunction() = f or
|
||||
exists(Function g | f.calls(g) |
|
||||
result = maybeLocked(g)
|
||||
)
|
||||
result.getEnclosingFunction() = f
|
||||
or
|
||||
exists(Function g | f.calls(g) | result = maybeLocked(g))
|
||||
}
|
||||
|
||||
predicate intraproc(LockOperation inner, string msg, LockOperation outer) {
|
||||
inner = outer.getAReachedNode() and outer.getLocked() != inner.getLocked() and
|
||||
inner = outer.getAReachedNode() and
|
||||
outer.getLocked() != inner.getLocked() and
|
||||
msg = "This lock operation is nested in a $@."
|
||||
}
|
||||
|
||||
predicate interproc(FunctionCall inner, string msg, LockOperation outer) {
|
||||
inner = outer.getAReachedNode() and
|
||||
exists(LockOperation lock | lock = maybeLocked(inner.getTarget()) and lock.getLocked() != outer.getLocked() |
|
||||
exists(LockOperation lock |
|
||||
lock = maybeLocked(inner.getTarget()) and lock.getLocked() != outer.getLocked()
|
||||
|
|
||||
msg = "This call may perform a " + lock.say() + " while under the effect of a $@."
|
||||
)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
import Semaphores
|
||||
|
||||
from FunctionCall call, string kind
|
||||
where (call instanceof SemaphoreCreation and kind = "semaphores") or
|
||||
(call instanceof LockingPrimitive and kind = "locking primitives")
|
||||
where
|
||||
call instanceof SemaphoreCreation and kind = "semaphores"
|
||||
or
|
||||
call instanceof LockingPrimitive and kind = "locking primitives"
|
||||
select call, "Use of " + kind + " should be avoided."
|
||||
|
||||
@@ -18,11 +18,15 @@ predicate lockOrder(LockOperation outer, LockOperation inner) {
|
||||
|
||||
int orderCount(Declaration outerLock, Declaration innerLock) {
|
||||
result = strictcount(LockOperation outer, LockOperation inner |
|
||||
outer.getLocked() = outerLock and inner.getLocked() = innerLock and
|
||||
lockOrder(outer, inner))
|
||||
outer.getLocked() = outerLock and
|
||||
inner.getLocked() = innerLock and
|
||||
lockOrder(outer, inner)
|
||||
)
|
||||
}
|
||||
|
||||
from LockOperation outer, LockOperation inner
|
||||
where lockOrder(outer, inner)
|
||||
and orderCount(outer.getLocked(), inner.getLocked()) <= orderCount(inner.getLocked(), outer.getLocked())
|
||||
where
|
||||
lockOrder(outer, inner) and
|
||||
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()
|
||||
|
||||
@@ -4,29 +4,31 @@
|
||||
|
||||
import cpp
|
||||
|
||||
|
||||
class SemaphoreCreation extends FunctionCall {
|
||||
SemaphoreCreation() {
|
||||
exists(string name | name = this.getTarget().getName() |
|
||||
name = "semBCreate" or name = "semMCreate" or name = "semCCreate" or
|
||||
name = "semBCreate" or
|
||||
name = "semMCreate" or
|
||||
name = "semCCreate" or
|
||||
name = "semRWCreate"
|
||||
)
|
||||
}
|
||||
|
||||
Variable getSemaphore() {
|
||||
result.getAnAccess() = this.getParent().(Assignment).getLValue()
|
||||
}
|
||||
Variable getSemaphore() { result.getAnAccess() = this.getParent().(Assignment).getLValue() }
|
||||
}
|
||||
|
||||
abstract class LockOperation extends FunctionCall {
|
||||
abstract UnlockOperation getMatchingUnlock();
|
||||
|
||||
abstract Declaration getLocked();
|
||||
|
||||
abstract string say();
|
||||
|
||||
ControlFlowNode getAReachedNode() {
|
||||
result = this or
|
||||
result = this
|
||||
or
|
||||
exists(ControlFlowNode mid | mid = getAReachedNode() |
|
||||
not(mid != this.getMatchingUnlock()) and
|
||||
not mid != this.getMatchingUnlock() and
|
||||
result = mid.getASuccessor()
|
||||
)
|
||||
}
|
||||
@@ -39,24 +41,21 @@ abstract class UnlockOperation extends FunctionCall {
|
||||
class SemaphoreTake extends LockOperation {
|
||||
SemaphoreTake() {
|
||||
exists(string name | name = this.getTarget().getName() |
|
||||
name = "semTake" or
|
||||
name = "semTake"
|
||||
or
|
||||
// '_' is a wildcard, so this matches calls like
|
||||
// semBTakeScalable or semMTake_inline.
|
||||
name.matches("sem_Take%")
|
||||
)
|
||||
}
|
||||
|
||||
override Variable getLocked() {
|
||||
result.getAnAccess() = this.getArgument(0)
|
||||
}
|
||||
override Variable getLocked() { result.getAnAccess() = this.getArgument(0) }
|
||||
|
||||
override UnlockOperation getMatchingUnlock() {
|
||||
result.(SemaphoreGive).getLocked() = this.getLocked()
|
||||
}
|
||||
|
||||
override string say() {
|
||||
result = "semaphore take of " + getLocked().getName()
|
||||
}
|
||||
override string say() { result = "semaphore take of " + getLocked().getName() }
|
||||
}
|
||||
|
||||
class SemaphoreGive extends UnlockOperation {
|
||||
@@ -67,14 +66,9 @@ class SemaphoreGive extends UnlockOperation {
|
||||
)
|
||||
}
|
||||
|
||||
Variable getLocked() {
|
||||
result.getAnAccess() = this.getArgument(0)
|
||||
}
|
||||
|
||||
override LockOperation getMatchingLock() {
|
||||
this = result.getMatchingUnlock()
|
||||
}
|
||||
Variable getLocked() { result.getAnAccess() = this.getArgument(0) }
|
||||
|
||||
override LockOperation getMatchingLock() { this = result.getMatchingUnlock() }
|
||||
}
|
||||
|
||||
class LockingPrimitive extends FunctionCall, LockOperation {
|
||||
@@ -84,18 +78,16 @@ class LockingPrimitive extends FunctionCall, LockOperation {
|
||||
)
|
||||
}
|
||||
|
||||
override Function getLocked() {
|
||||
result = this.getTarget()
|
||||
}
|
||||
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()
|
||||
}
|
||||
override string say() { result = "call to " + getLocked().getName() }
|
||||
}
|
||||
|
||||
class UnlockingPrimitive extends FunctionCall, UnlockOperation {
|
||||
@@ -105,11 +97,7 @@ class UnlockingPrimitive extends FunctionCall, UnlockOperation {
|
||||
)
|
||||
}
|
||||
|
||||
Function getLocked() {
|
||||
result = getMatchingLock().getLocked()
|
||||
}
|
||||
Function getLocked() { result = getMatchingLock().getLocked() }
|
||||
|
||||
override LockOperation getMatchingLock() {
|
||||
this = result.getMatchingUnlock()
|
||||
}
|
||||
override LockOperation getMatchingLock() { this = result.getMatchingUnlock() }
|
||||
}
|
||||
|
||||
@@ -15,8 +15,11 @@ import cpp
|
||||
class ForbiddenFunction extends Function {
|
||||
ForbiddenFunction() {
|
||||
exists(string name | name = this.getName() |
|
||||
name = "setjmp" or name = "longjmp" or
|
||||
name = "sigsetjmp" or name = "siglongjmp")
|
||||
name = "setjmp" or
|
||||
name = "longjmp" or
|
||||
name = "sigsetjmp" or
|
||||
name = "siglongjmp"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,15 +8,14 @@
|
||||
* readability
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
predicate hasInitializer(EnumConstant c) {
|
||||
c.getInitializer().fromSource()
|
||||
}
|
||||
predicate hasInitializer(EnumConstant c) { c.getInitializer().fromSource() }
|
||||
|
||||
/** Does this have an initializer that is not just a ref to another constant in the same enum? */
|
||||
predicate hasNonReferenceInitializer(EnumConstant c) {
|
||||
exists (Initializer init |
|
||||
exists(Initializer init |
|
||||
init = c.getInitializer() and
|
||||
init.fromSource() and
|
||||
not init.getExpr().(EnumConstantAccess).getTarget().getDeclaringEnum() = c.getDeclaringEnum()
|
||||
@@ -24,14 +23,13 @@ predicate hasNonReferenceInitializer(EnumConstant c) {
|
||||
}
|
||||
|
||||
predicate hasReferenceInitializer(EnumConstant c) {
|
||||
exists (Initializer init |
|
||||
exists(Initializer init |
|
||||
init = c.getInitializer() and
|
||||
init.fromSource() and
|
||||
init.getExpr().(EnumConstantAccess).getTarget().getDeclaringEnum() = c.getDeclaringEnum()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// There exists another constant whose value is implicit, but it's
|
||||
// not the last one: the last value is okay to use to get the highest
|
||||
// enum value automatically. It can be followed by aliases though.
|
||||
@@ -48,15 +46,16 @@ predicate enumThatHasConstantWithImplicitValue(Enum e) {
|
||||
}
|
||||
|
||||
from Enum e, int i
|
||||
where // e is at position i, and has an explicit value in the source - but
|
||||
// not just a reference to another enum constant
|
||||
hasNonReferenceInitializer(e.getEnumConstant(i)) and
|
||||
// but e is not the first or the last constant of the enum
|
||||
i != 0 and
|
||||
exists(e.getEnumConstant(i+1)) and
|
||||
// and there exists another constant whose value is implicit, but it's
|
||||
// not the last one: the last value is okay to use to get the highest
|
||||
// enum value automatically. It can be followed by aliases though.
|
||||
enumThatHasConstantWithImplicitValue(e)
|
||||
|
||||
select e, "In an enumerator list, the = construct should not be used to explicitly initialize members other than the first, unless all items are explicitly initialized."
|
||||
where
|
||||
// e is at position i, and has an explicit value in the source - but
|
||||
// not just a reference to another enum constant
|
||||
hasNonReferenceInitializer(e.getEnumConstant(i)) and
|
||||
// but e is not the first or the last constant of the enum
|
||||
i != 0 and
|
||||
exists(e.getEnumConstant(i + 1)) and
|
||||
// and there exists another constant whose value is implicit, but it's
|
||||
// not the last one: the last value is okay to use to get the highest
|
||||
// enum value automatically. It can be followed by aliases though.
|
||||
enumThatHasConstantWithImplicitValue(e)
|
||||
select e,
|
||||
"In an enumerator list, the = construct should not be used to explicitly initialize members other than the first, unless all items are explicitly initialized."
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
import cpp
|
||||
|
||||
from VariableDeclarationEntry v
|
||||
where v.getVariable() instanceof GlobalVariable and
|
||||
where
|
||||
v.getVariable() instanceof GlobalVariable and
|
||||
v.hasSpecifier("extern") and
|
||||
not v.getFile() instanceof HeaderFile
|
||||
select v, v.getName() + " should be declared only in a header file that is included as needed."
|
||||
|
||||
@@ -13,9 +13,11 @@
|
||||
import cpp
|
||||
|
||||
from GlobalVariable v
|
||||
where forex(VariableAccess va | va.getTarget() = v | va.getFile() = v.getDefinitionLocation().getFile())
|
||||
and not v.hasSpecifier("static")
|
||||
and strictcount(v.getAnAccess().getEnclosingFunction()) > 1 // If = 1, variable should be function-scope.
|
||||
and not v.getADeclarationEntry().getFile() instanceof HeaderFile // intended to be accessed elsewhere
|
||||
select v, "The global variable " + v.getName() + " is not accessed outside of " + v.getFile().getBaseName()
|
||||
+ " and could be made static."
|
||||
where
|
||||
forex(VariableAccess va | va.getTarget() = v | va.getFile() = v.getDefinitionLocation().getFile()) and
|
||||
not v.hasSpecifier("static") and
|
||||
strictcount(v.getAnAccess().getEnclosingFunction()) > 1 and // If = 1, variable should be function-scope.
|
||||
not v.getADeclarationEntry().getFile() instanceof HeaderFile // intended to be accessed elsewhere
|
||||
select v,
|
||||
"The global variable " + v.getName() + " is not accessed outside of " + v.getFile().getBaseName() +
|
||||
" and could be made static."
|
||||
|
||||
@@ -12,8 +12,11 @@
|
||||
import cpp
|
||||
|
||||
from GlobalVariable v, Function f
|
||||
where v.getAnAccess().getEnclosingFunction() = f and
|
||||
strictcount(v.getAnAccess().getEnclosingFunction()) = 1 and
|
||||
forall(VariableAccess a | a = v.getAnAccess() | exists(a.getEnclosingFunction())) and
|
||||
not v.getADeclarationEntry().getFile() instanceof HeaderFile // intended to be accessed elsewhere
|
||||
select v, "The variable " + v.getName() + " is only accessed in $@ and should be scoped accordingly.", f, f.getName()
|
||||
where
|
||||
v.getAnAccess().getEnclosingFunction() = f and
|
||||
strictcount(v.getAnAccess().getEnclosingFunction()) = 1 and
|
||||
forall(VariableAccess a | a = v.getAnAccess() | exists(a.getEnclosingFunction())) and
|
||||
not v.getADeclarationEntry().getFile() instanceof HeaderFile // intended to be accessed elsewhere
|
||||
select v,
|
||||
"The variable " + v.getName() + " is only accessed in $@ and should be scoped accordingly.", f,
|
||||
f.getName()
|
||||
|
||||
@@ -8,27 +8,29 @@
|
||||
* readability
|
||||
* external/jpl
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
class LocalVariableOrParameter extends Variable {
|
||||
LocalVariableOrParameter() {
|
||||
this instanceof LocalVariable or
|
||||
this instanceof LocalVariable
|
||||
or
|
||||
// A function declaration (i.e. "int foo(int bar);") doesn't usefully
|
||||
// shadow globals; the parameter should be on the version of the function
|
||||
// that has a body.
|
||||
exists(Parameter p | p = this |
|
||||
p.getFunction().getDefinitionLocation().getFile() = this.getFile() and
|
||||
exists(p.getFunction().getBlock()))
|
||||
p.getFunction().getDefinitionLocation().getFile() = this.getFile() and
|
||||
exists(p.getFunction().getBlock())
|
||||
)
|
||||
}
|
||||
|
||||
string type() {
|
||||
if this instanceof Parameter
|
||||
then result = "Parameter "
|
||||
else result = "Local variable "
|
||||
if this instanceof Parameter then result = "Parameter " else result = "Local variable "
|
||||
}
|
||||
}
|
||||
|
||||
from LocalVariableOrParameter lv, GlobalVariable gv
|
||||
where lv.getName() = gv.getName() and
|
||||
lv.getFile() = gv.getFile()
|
||||
where
|
||||
lv.getName() = gv.getName() and
|
||||
lv.getFile() = gv.getFile()
|
||||
select lv, lv.type() + lv.getName() + " hides the global variable $@.", gv, gv.getName()
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
|
||||
import cpp
|
||||
|
||||
/** In its full generality, the rule applies to all functions that
|
||||
/**
|
||||
* In its full generality, the rule applies to all functions that
|
||||
* return non-void, including things like 'printf' and 'close',
|
||||
* which are routinely not checked because the behavior on success
|
||||
* is the same as the behavior on failure. The recommendation is
|
||||
@@ -27,13 +28,15 @@ predicate whitelist(Function f) {
|
||||
}
|
||||
|
||||
from FunctionCall c, string msg
|
||||
where not c.getTarget().getType() instanceof VoidType
|
||||
and not whitelist(c.getTarget())
|
||||
and
|
||||
(
|
||||
(c instanceof ExprInVoidContext and msg = "The return value of non-void function $@ is not checked.")
|
||||
or
|
||||
(definition(_, c.getParent()) and not definitionUsePair(_, c.getParent(), _) and
|
||||
msg = "$@'s return value is stored but not checked.")
|
||||
)
|
||||
where
|
||||
not c.getTarget().getType() instanceof VoidType and
|
||||
not whitelist(c.getTarget()) and
|
||||
(
|
||||
c instanceof ExprInVoidContext and
|
||||
msg = "The return value of non-void function $@ is not checked."
|
||||
or
|
||||
definition(_, c.getParent()) and
|
||||
not definitionUsePair(_, c.getParent(), _) and
|
||||
msg = "$@'s return value is stored but not checked."
|
||||
)
|
||||
select c, msg, c.getTarget() as f, f.getName()
|
||||
|
||||
@@ -12,16 +12,18 @@
|
||||
import JPL_C.Tasks
|
||||
|
||||
predicate flow(Parameter p, ControlFlowNode n) {
|
||||
(exists(p.getAnAccess()) and n = p.getFunction().getBlock()) or
|
||||
exists(ControlFlowNode mid | flow(p, mid) and not mid = p.getAnAccess() and n = mid.getASuccessor())
|
||||
exists(p.getAnAccess()) and n = p.getFunction().getBlock()
|
||||
or
|
||||
exists(ControlFlowNode mid |
|
||||
flow(p, mid) and not mid = p.getAnAccess() and n = mid.getASuccessor()
|
||||
)
|
||||
}
|
||||
|
||||
VariableAccess firstAccess(Parameter p) {
|
||||
flow(p, result) and result = p.getAnAccess()
|
||||
}
|
||||
VariableAccess firstAccess(Parameter p) { flow(p, result) and result = p.getAnAccess() }
|
||||
|
||||
from Parameter p, VariableAccess va
|
||||
where va = firstAccess(p) and p.getFunction() instanceof PublicFunction and
|
||||
not exists(Expr e | e.isCondition() | e.getAChild*() = va)
|
||||
where
|
||||
va = firstAccess(p) and
|
||||
p.getFunction() instanceof PublicFunction and
|
||||
not exists(Expr e | e.isCondition() | e.getAChild*() = va)
|
||||
select va, "This use of parameter " + p.getName() + " has not been checked."
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
import semmle.code.cpp.commons.Assertions
|
||||
|
||||
from Assertion a, string value, string msg
|
||||
where value = a.getAsserted().getValue() and
|
||||
if value.toInt() = 0 then
|
||||
msg = "This assertion is always false."
|
||||
else
|
||||
msg = "This assertion is always true."
|
||||
where
|
||||
value = a.getAsserted().getValue() and
|
||||
if value.toInt() = 0
|
||||
then msg = "This assertion is always false."
|
||||
else msg = "This assertion is always true."
|
||||
select a.getAsserted(), msg
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
import semmle.code.cpp.commons.Assertions
|
||||
|
||||
from Function f
|
||||
where f.getMetrics().getNumberOfLinesOfCode() > 10 and
|
||||
where
|
||||
f.getMetrics().getNumberOfLinesOfCode() > 10 and
|
||||
not exists(Assertion a | a.getAsserted().getEnclosingFunction() = f)
|
||||
select f, "All functions of more than 10 lines should have at least one assertion."
|
||||
|
||||
@@ -13,11 +13,16 @@ import cpp
|
||||
|
||||
predicate allowedTypedefs(TypedefType t) {
|
||||
exists(string name | name = t.getName() |
|
||||
name = "I64" or name = "U64" or
|
||||
name = "I32" or name = "U32" or
|
||||
name = "I16" or name = "U16" or
|
||||
name = "I8" or name = "U8" or
|
||||
name = "F64" or name = "F32"
|
||||
name = "I64" or
|
||||
name = "U64" or
|
||||
name = "I32" or
|
||||
name = "U32" or
|
||||
name = "I16" or
|
||||
name = "U16" or
|
||||
name = "I8" or
|
||||
name = "U8" or
|
||||
name = "F64" or
|
||||
name = "F32"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -25,7 +30,8 @@ predicate allowedTypedefs(TypedefType t) {
|
||||
* Gets a type which appears literally in the declaration of `d`.
|
||||
*/
|
||||
Type getAnImmediateUsedType(Declaration d) {
|
||||
d.isDefined() and (
|
||||
d.isDefined() and
|
||||
(
|
||||
result = d.(Function).getType() or
|
||||
result = d.(Variable).getType()
|
||||
)
|
||||
@@ -48,7 +54,11 @@ predicate problematic(IntegralType t) {
|
||||
}
|
||||
|
||||
from Declaration d, Type usedType
|
||||
where usedType = getAUsedType*(getAnImmediateUsedType(d)) and problematic(usedType)
|
||||
where
|
||||
usedType = getAUsedType*(getAnImmediateUsedType(d)) and
|
||||
problematic(usedType) and
|
||||
// Ignore violations for which we do not have a valid location.
|
||||
and not(d.getLocation() instanceof UnknownLocation)
|
||||
select d, d.getName() + " uses the basic integral type " + usedType.getName() + " rather than a typedef with size and signedness."
|
||||
not d.getLocation() instanceof UnknownLocation
|
||||
select d,
|
||||
d.getName() + " uses the basic integral type " + usedType.getName() +
|
||||
" rather than a typedef with size and signedness."
|
||||
|
||||
@@ -12,7 +12,9 @@
|
||||
import cpp
|
||||
|
||||
from BinaryOperation parent, BinaryOperation child
|
||||
where parent.getAnOperand() = child and not child.isParenthesised() and
|
||||
where
|
||||
parent.getAnOperand() = child and
|
||||
not child.isParenthesised() and
|
||||
(parent instanceof BinaryBitwiseOperation or child instanceof BinaryBitwiseOperation) and
|
||||
// Some benign cases...
|
||||
not (parent instanceof BitwiseAndExpr and child instanceof BitwiseAndExpr) and
|
||||
|
||||
@@ -46,10 +46,9 @@ predicate inherentlyUnsafe(Function f) {
|
||||
exists(Variable v | v.getAnAssignedValue().getEnclosingFunction() = f |
|
||||
v instanceof GlobalVariable or
|
||||
v.isStatic()
|
||||
) or
|
||||
exists(FunctionCall c | c.getEnclosingFunction() = f |
|
||||
inherentlyUnsafe(c.getTarget())
|
||||
)
|
||||
or
|
||||
exists(FunctionCall c | c.getEnclosingFunction() = f | inherentlyUnsafe(c.getTarget()))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,7 +58,9 @@ predicate inherentlyUnsafe(Function f) {
|
||||
* not inherently unsafe.
|
||||
*/
|
||||
predicate safeToCall(Function f) {
|
||||
forall(PointerType paramPointerType | paramPointerType = getAPointerType(f.getAParameter().getType()) |
|
||||
forall(PointerType paramPointerType |
|
||||
paramPointerType = getAPointerType(f.getAParameter().getType())
|
||||
|
|
||||
paramPointerType.getBaseType().isConst()
|
||||
) and
|
||||
not inherentlyUnsafe(f)
|
||||
@@ -78,12 +79,16 @@ class BooleanExpression extends Expr {
|
||||
}
|
||||
|
||||
predicate hasSideEffect(Expr e) {
|
||||
e instanceof Assignment or
|
||||
e instanceof CrementOperation or
|
||||
e instanceof ExprCall or
|
||||
e instanceof Assignment
|
||||
or
|
||||
e instanceof CrementOperation
|
||||
or
|
||||
e instanceof ExprCall
|
||||
or
|
||||
exists(Function f | f = e.(FunctionCall).getTarget() and not safeFunctionWhitelist(f) |
|
||||
inherentlyUnsafe(f) or not safeToCall(f)
|
||||
) or
|
||||
)
|
||||
or
|
||||
hasSideEffect(e.getAChild())
|
||||
}
|
||||
|
||||
|
||||
@@ -12,12 +12,13 @@
|
||||
import cpp
|
||||
|
||||
from PreprocessorDirective p
|
||||
where not p instanceof Include and
|
||||
not p instanceof Macro and
|
||||
not p instanceof PreprocessorIf and
|
||||
not p instanceof PreprocessorElif and
|
||||
not p instanceof PreprocessorElse and
|
||||
not p instanceof PreprocessorIfdef and
|
||||
not p instanceof PreprocessorIfndef and
|
||||
not p instanceof PreprocessorEndif
|
||||
where
|
||||
not p instanceof Include and
|
||||
not p instanceof Macro and
|
||||
not p instanceof PreprocessorIf and
|
||||
not p instanceof PreprocessorElif and
|
||||
not p instanceof PreprocessorElse and
|
||||
not p instanceof PreprocessorIfdef and
|
||||
not p instanceof PreprocessorIfndef and
|
||||
not p instanceof PreprocessorEndif
|
||||
select p, "This preprocessor directive is not allowed."
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
import cpp
|
||||
|
||||
from PreprocessorDirective i
|
||||
where (i instanceof PreprocessorIf or i instanceof PreprocessorIfdef or i instanceof PreprocessorIfndef)
|
||||
and not i.getFile() instanceof HeaderFile
|
||||
where
|
||||
(i instanceof PreprocessorIf or i instanceof PreprocessorIfdef or i instanceof PreprocessorIfndef) and
|
||||
not i.getFile() instanceof HeaderFile
|
||||
select i, "Use of conditional compilation must be kept to a minimum."
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
import cpp
|
||||
|
||||
from Macro m, string msg
|
||||
where (m.getHead().matches("%...%") and msg = "The macro " + m.getHead() + " is variadic, and hence not allowed.") or
|
||||
(m.getBody().matches("%##%") and msg = "The macro " + m.getHead() + " uses token pasting and is not allowed.")
|
||||
where
|
||||
m.getHead().matches("%...%") and
|
||||
msg = "The macro " + m.getHead() + " is variadic, and hence not allowed."
|
||||
or
|
||||
m.getBody().matches("%##%") and
|
||||
msg = "The macro " + m.getHead() + " uses token pasting and is not allowed."
|
||||
select m, msg
|
||||
|
||||
@@ -12,8 +12,10 @@
|
||||
import cpp
|
||||
|
||||
int lineInBlock(File f) {
|
||||
exists(Block block, Location blockLocation | block.getFile() = f and blockLocation = block.getLocation()|
|
||||
result in [blockLocation.getStartLine()..blockLocation.getEndLine()]
|
||||
exists(Block block, Location blockLocation |
|
||||
block.getFile() = f and blockLocation = block.getLocation()
|
||||
|
|
||||
result in [blockLocation.getStartLine() .. blockLocation.getEndLine()]
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -12,43 +12,35 @@
|
||||
import cpp
|
||||
|
||||
class FileWithDirectives extends File {
|
||||
FileWithDirectives() {
|
||||
exists(Directive d | d.getFile() = this)
|
||||
}
|
||||
FileWithDirectives() { exists(Directive d | d.getFile() = this) }
|
||||
|
||||
int getDirectiveLine(Directive d) {
|
||||
d.getFile() = this and d.getLocation().getStartLine() = result
|
||||
}
|
||||
|
||||
int getDirectiveIndex(Directive d) {
|
||||
exists(int line | line = getDirectiveLine(d) |
|
||||
line = rank[result](getDirectiveLine(_))
|
||||
)
|
||||
exists(int line | line = getDirectiveLine(d) | line = rank[result](getDirectiveLine(_)))
|
||||
}
|
||||
|
||||
int depth(Directive d) {
|
||||
exists(int index | index = getDirectiveIndex(d) |
|
||||
(index = 1 and result = d.depthChange()) or
|
||||
exists(Directive prev | getDirectiveIndex(prev) = index-1 |
|
||||
index = 1 and result = d.depthChange()
|
||||
or
|
||||
exists(Directive prev | getDirectiveIndex(prev) = index - 1 |
|
||||
result = d.depthChange() + depth(prev)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Directive lastDirective() {
|
||||
getDirectiveIndex(result) = max(getDirectiveIndex(_))
|
||||
}
|
||||
Directive lastDirective() { getDirectiveIndex(result) = max(getDirectiveIndex(_)) }
|
||||
}
|
||||
|
||||
abstract class Directive extends PreprocessorDirective {
|
||||
abstract int depthChange();
|
||||
|
||||
abstract predicate mismatched();
|
||||
|
||||
int depth() {
|
||||
exists(FileWithDirectives f |
|
||||
f.depth(this) = result
|
||||
)
|
||||
}
|
||||
int depth() { exists(FileWithDirectives f | f.depth(this) = result) }
|
||||
}
|
||||
|
||||
class IfDirective extends Directive {
|
||||
@@ -59,6 +51,7 @@ class IfDirective extends Directive {
|
||||
}
|
||||
|
||||
override int depthChange() { result = 1 }
|
||||
|
||||
override predicate mismatched() { none() }
|
||||
}
|
||||
|
||||
@@ -69,24 +62,26 @@ class ElseDirective extends Directive {
|
||||
}
|
||||
|
||||
override int depthChange() { result = 0 }
|
||||
|
||||
override predicate mismatched() { depth() < 1 }
|
||||
}
|
||||
|
||||
class EndifDirective extends Directive {
|
||||
EndifDirective() {
|
||||
this instanceof PreprocessorEndif
|
||||
}
|
||||
EndifDirective() { this instanceof PreprocessorEndif }
|
||||
|
||||
override int depthChange() { result = -1 }
|
||||
|
||||
override predicate mismatched() { depth() < 0 }
|
||||
}
|
||||
|
||||
from FileWithDirectives f, Directive d, string msg
|
||||
where d.getFile() = f and
|
||||
if d.mismatched() then (
|
||||
msg = "'" + d + "' has no matching #if in file " + f.getBaseName() + "."
|
||||
) else (
|
||||
d = f.lastDirective() and d.depth() > 0 and msg = "File " + f.getBaseName() +
|
||||
" ends with " + d.depth() + " unterminated #if directives."
|
||||
where
|
||||
d.getFile() = f and
|
||||
if d.mismatched()
|
||||
then msg = "'" + d + "' has no matching #if in file " + f.getBaseName() + "."
|
||||
else (
|
||||
d = f.lastDirective() and
|
||||
d.depth() > 0 and
|
||||
msg = "File " + f.getBaseName() + " ends with " + d.depth() + " unterminated #if directives."
|
||||
)
|
||||
select d, msg
|
||||
|
||||
@@ -22,16 +22,16 @@ class OneLineStmt extends Stmt {
|
||||
}
|
||||
}
|
||||
|
||||
int numStmt(File f, int line) {
|
||||
result = strictcount(OneLineStmt o | o.onLine(f, line))
|
||||
}
|
||||
int numStmt(File f, int line) { result = strictcount(OneLineStmt o | o.onLine(f, line)) }
|
||||
|
||||
from File f, int line, OneLineStmt o, int cnt
|
||||
where numStmt(f, line) = cnt
|
||||
and cnt > 1
|
||||
and o.onLine(f, line)
|
||||
and o.getLocation().getStartColumn() =
|
||||
min(OneLineStmt other, int toMin
|
||||
| other.onLine(f, line) and toMin = other.getLocation().getStartColumn()
|
||||
| toMin)
|
||||
where
|
||||
numStmt(f, line) = cnt and
|
||||
cnt > 1 and
|
||||
o.onLine(f, line) and
|
||||
o.getLocation().getStartColumn() = min(OneLineStmt other, int toMin |
|
||||
other.onLine(f, line) and toMin = other.getLocation().getStartColumn()
|
||||
|
|
||||
toMin
|
||||
)
|
||||
select o, "This line contains " + cnt + " statements; only one is allowed."
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
import cpp
|
||||
|
||||
from DeclStmt d
|
||||
where exists(Variable v1, Variable v2 | v1 = d.getADeclaration() and v2 = d.getADeclaration() |
|
||||
where
|
||||
exists(Variable v1, Variable v2 | v1 = d.getADeclaration() and v2 = d.getADeclaration() |
|
||||
v1 != v2 and
|
||||
v1.getLocation().getStartLine() = v2.getLocation().getStartLine()
|
||||
)
|
||||
|
||||
@@ -26,6 +26,7 @@ string paramWarning(Function f) {
|
||||
}
|
||||
|
||||
from Function f, string msg
|
||||
where msg = lengthWarning(f) or
|
||||
msg = paramWarning(f)
|
||||
where
|
||||
msg = lengthWarning(f) or
|
||||
msg = paramWarning(f)
|
||||
select f, msg
|
||||
|
||||
@@ -15,7 +15,7 @@ string var(Variable v) {
|
||||
exists(int level | level = v.getType().getPointerIndirectionLevel() |
|
||||
level > 2 and
|
||||
result = "The type of " + v.getName() + " uses " + level +
|
||||
" levels of pointer indirection -- maximum allowed is 2."
|
||||
" levels of pointer indirection -- maximum allowed is 2."
|
||||
)
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ string fun(Function f) {
|
||||
exists(int level | level = f.getType().getPointerIndirectionLevel() |
|
||||
level > 2 and
|
||||
result = "The return type of " + f.getName() + " uses " + level +
|
||||
" levels of pointer indirection -- maximum allowed is 2."
|
||||
" levels of pointer indirection -- maximum allowed is 2."
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
import cpp
|
||||
|
||||
from PointerDereferenceExpr e, int n
|
||||
where not e.getParent+() instanceof PointerDereferenceExpr
|
||||
and n = strictcount(PointerDereferenceExpr child | child.getParent+() = e)
|
||||
and n > 1
|
||||
where
|
||||
not e.getParent+() instanceof PointerDereferenceExpr and
|
||||
n = strictcount(PointerDereferenceExpr child | child.getParent+() = e) and
|
||||
n > 1
|
||||
select e, "This expression involves " + n + " levels of pointer dereference; 2 are allowed."
|
||||
|
||||
@@ -12,9 +12,11 @@
|
||||
import cpp
|
||||
|
||||
from Macro m
|
||||
where forex(MacroInvocation mi | mi.getMacro() = m |
|
||||
where
|
||||
forex(MacroInvocation mi | mi.getMacro() = m |
|
||||
exists(PointerDereferenceExpr e, Location miLoc, Location eLoc | e = mi.getAGeneratedElement() |
|
||||
miLoc = mi.getLocation() and eLoc = e.getLocation() and
|
||||
miLoc = mi.getLocation() and
|
||||
eLoc = e.getLocation() and
|
||||
eLoc.getStartColumn() = miLoc.getStartColumn() and
|
||||
eLoc.getStartLine() = miLoc.getStartLine()
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user