Merge remote-tracking branch 'upstream/master' into infinite-loops-visible

Moved the change note to 1.23.
This commit is contained in:
Jonas Jensen
2019-10-25 15:43:30 +02:00
4016 changed files with 266423 additions and 91530 deletions

3
.codeqlmanifest.json Normal file
View File

@@ -0,0 +1,3 @@
{ "provide": [ "*/ql/src/qlpack.yml",
"misc/legacy-support/*/qlpack.yml",
"misc/suite-helpers/qlpack.yml" ] }

View 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
View File

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

3
.gitignore vendored
View File

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

View File

@@ -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

View File

@@ -14,7 +14,7 @@ Our community strives to:
* Posting, or threatening to post, peoples 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.

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View 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. |

View 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.

View 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

View 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()`.

View 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)

View 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. |

View 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.

View 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``.

View File

@@ -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.

View File

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

View File

@@ -0,0 +1 @@
<queries language="cpp"/>

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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()

View 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

View 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

View 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

View File

@@ -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

View File

@@ -42,9 +42,7 @@ newtype TVariableDeclarationInfo =
*/
class VariableDeclarationLine extends TVariableDeclarationInfo {
Class c;
File f;
int line;
VariableDeclarationLine() {

View File

@@ -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()

View File

@@ -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>

View 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

View File

@@ -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

View File

@@ -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"

View File

@@ -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"

View File

@@ -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())
)
)
)

View File

@@ -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).

View File

@@ -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

View File

@@ -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()
)
}

View File

@@ -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() }

View File

@@ -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 + " [...]"
)
}

View File

@@ -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. &amp;gt; and &amp;eacute;)
// * HTML entities in decimal notation (e.g. a&amp;#768;)
// * HTML entities in hexadecimal notation (e.g. &amp;#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())
)
}

View File

@@ -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()

View File

@@ -9,6 +9,7 @@
* documentation
* external/cwe/cwe-546
*/
import cpp
import Documentation.CaptionedComments

View File

@@ -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

View File

@@ -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."

View File

@@ -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"

View File

@@ -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

View File

@@ -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()

View File

@@ -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."

View File

@@ -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"
)
}

View File

@@ -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 $@."
)
}

View File

@@ -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."

View File

@@ -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()

View File

@@ -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() }
}

View File

@@ -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"
)
}
}

View File

@@ -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."

View File

@@ -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."

View File

@@ -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."

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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."

View File

@@ -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

View File

@@ -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."

View File

@@ -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."

View File

@@ -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

View File

@@ -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())
}

View File

@@ -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."

View File

@@ -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."

View File

@@ -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

View File

@@ -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()]
)
}

View File

@@ -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

View File

@@ -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."

View File

@@ -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()
)

View File

@@ -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

View File

@@ -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."
)
}

View File

@@ -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."

View File

@@ -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