mirror of
https://github.com/github/codeql.git
synced 2025-12-20 18:56:32 +01:00
Merge remote-tracking branch 'upstream/master' into dbartol/VarArgIR
This commit is contained in:
@@ -1,62 +1,65 @@
|
|||||||
# Contributing to CodeQL
|
# Contributing to CodeQL
|
||||||
|
|
||||||
We welcome contributions to our standard library and standard checks. Got an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request!
|
We welcome contributions to our CodeQL libraries and queries. 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 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.
|
There is lots of useful documentation to help you write queries, ranging from information about query file structure to tutorials for specific target languages. For more information on the documentation available, see [Writing CodeQL queries](https://help.semmle.com/QL/learn-ql/writing-queries/writing-queries.html) on [help.semmle.com](https://help.semmle.com).
|
||||||
|
|
||||||
## Adding a new query
|
|
||||||
|
|
||||||
If you have an idea for a query that you would like to share with other CodeQL users, please open a pull request to add it to this repository.
|
## Submitting a new experimental query
|
||||||
Follow the steps below to help other users understand what your query does, and to ensure that your query is consistent with the other CodeQL queries.
|
|
||||||
|
|
||||||
1. **Consult the documentation for query writers**
|
If you have an idea for a query that you would like to share with other CodeQL users, please open a pull request to add it to this repository. New queries start out in a `<language>/ql/src/experimental` directory, to which they can be merged when they meet the following requirements.
|
||||||
|
|
||||||
There is lots of useful documentation to help you write queries, ranging from information about query file structure to tutorials for specific target languages. For more information on the documentation available, see [Writing CodeQL queries](https://help.semmle.com/QL/learn-ql/writing-queries/writing-queries.html) on [help.semmle.com](https://help.semmle.com).
|
1. **Directory structure**
|
||||||
|
|
||||||
2. **Format your code correctly**
|
There are five language-specific query directories in this repository:
|
||||||
|
|
||||||
All CodeQL standard queries and libraries are uniformly formatted for clarity and consistency, so we strongly recommend that all contributions follow the same formatting guidelines. If you use CodeQL for VS Code, you can autoformat your query in the [Editor](https://help.semmle.com/codeql/codeql-for-vscode/reference/editor.html#autoformatting). For more information, see the [CodeQL style guide](https://github.com/Semmle/ql/blob/master/docs/ql-style-guide.md).
|
* C/C++: `cpp/ql/src`
|
||||||
|
* C#: `csharp/ql/src`
|
||||||
|
* Java: `java/ql/src`
|
||||||
|
* JavaScript: `javascript/ql/src`
|
||||||
|
* Python: `python/ql/src`
|
||||||
|
|
||||||
3. **Make sure your query has the correct metadata**
|
Each language-specific directory contains further subdirectories that group queries based on their `@tags` or purpose.
|
||||||
|
- Experimental queries and libraries are stored in the `experimental` subdirectory within each language-specific directory in the [CodeQL repository](https://github.com/Semmle/ql). For example, experimental Java queries and libraries are stored in `java/ql/src/experimental` and any corresponding tests in `java/ql/test/experimental`.
|
||||||
|
- The structure of an `experimental` subdirectory mirrors the structure of its parent directory.
|
||||||
|
- Select or create an appropriate directory in `experimental` based on the existing directory structure of `experimental` or its parent directory.
|
||||||
|
|
||||||
Query metadata is used to identify your query and make sure the query results are displayed properly.
|
2. **Query metadata**
|
||||||
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 GitHub 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 query `@id` must conform to all the requirements in the [guide on query metadata](docs/query-metadata-style-guide.md#query-id-id). In particular, it must not clash with any other queries in the repository, and it must start with the appropriate language-specific prefix.
|
||||||
|
- The query must have a `@name` and `@description` to explain its purpose.
|
||||||
|
- The query must have a `@kind` and `@problem.severity` as required by CodeQL tools.
|
||||||
|
|
||||||
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 CodeQL for VS Code.
|
For details, see the [guide on query metadata](docs/query-metadata-style-guide.md).
|
||||||
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 the correct language directory in this repository**
|
Make sure the `select` statement is compatible with the query `@kind`. See [Introduction to query files](https://help.semmle.com/QL/learn-ql/writing-queries/introduction-to-queries.html#select-clause) on help.semmle.com.
|
||||||
|
|
||||||
There are five language-specific directories in this repository:
|
3. **Formatting**
|
||||||
|
|
||||||
* C/C++: `ql/cpp/ql/src`
|
- The queries and libraries must be [autoformatted](https://help.semmle.com/codeql/codeql-for-vscode/reference/editor.html#autoformatting).
|
||||||
* 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.
|
4. **Compilation**
|
||||||
|
|
||||||
6. **Write a query help file**
|
- Compilation of the query and any associated libraries and tests must be resilient to future development of the [supported](docs/supported-queries.md) libraries. This means that the functionality cannot use internal libraries, cannot depend on the output of `getAQlClass`, and cannot make use of regexp matching on `toString`.
|
||||||
|
- The query and any associated libraries and tests must not cause any compiler warnings to be emitted (such as use of deprecated functionality or missing `override` annotations).
|
||||||
|
|
||||||
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.
|
5. **Results**
|
||||||
For more information on writing query help, see the [Query help style guide](https://github.com/Semmle/ql/blob/master/docs/query-help-style-guide.md).
|
|
||||||
|
|
||||||
7. **Maintain backwards compatibility**
|
- The query must have at least one true positive result on some revision of a real project.
|
||||||
|
|
||||||
The standard CodeQL libraries must evolve in a backwards compatible manner. If any backwards incompatible changes need to be made, the existing API must first be marked as deprecated. This is done by adding a `deprecated` annotation along with a QLDoc reference to the replacement API. Only after at least one full release cycle has elapsed may the old API be removed.
|
6. **Contributor License Agreement**
|
||||||
|
|
||||||
In addition to contributions to our standard queries and libraries, we also welcome contributions of a more experimental nature, which do not need to fulfill all the requirements listed above. See the guidelines for [experimental queries and libraries](docs/experimental.md) for details.
|
- The contributor can satisfy the [CLA](#contributor-license-agreement).
|
||||||
|
|
||||||
|
Experimental queries and libraries may not be actively maintained as the [supported](docs/supported-queries.md) libraries evolve. They may also be changed in backwards-incompatible ways or may be removed entirely in the future without deprecation warnings.
|
||||||
|
|
||||||
|
After the experimental query is merged, we welcome pull requests to improve it. Before a query can be moved out of the `experimental` subdirectory, it must satisfy [the requirements for being a supported query](docs/supported-queries.md).
|
||||||
|
|
||||||
## Using your personal data
|
## Using your personal data
|
||||||
|
|
||||||
If you contribute to this project, we will record your name and email
|
If you contribute to this project, we will record your name and email
|
||||||
address (as provided by you with your contributions) as part of the code
|
address (as provided by you with your contributions) as part of the code
|
||||||
repositories, which might be made public. We might also use this information
|
repositories, which are public. We might also use this information
|
||||||
to contact you in relation to your contributions, as well as in the
|
to contact you in relation to your contributions, as well as in the
|
||||||
normal course of software development. We also store records of your
|
normal course of software development. We also store records of your
|
||||||
CLA agreements. Under GDPR legislation, we do this
|
CLA agreements. Under GDPR legislation, we do this
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
|
|||||||
| No space for zero terminator (`cpp/no-space-for-terminator`) | Fewer false positive results | This query has been modified to be more conservative when identifying which pointers point to null-terminated strings. This approach produces fewer, more accurate results. |
|
| No space for zero terminator (`cpp/no-space-for-terminator`) | Fewer false positive results | This query has been modified to be more conservative when identifying which pointers point to null-terminated strings. This approach produces fewer, more accurate results. |
|
||||||
| Overloaded assignment does not return 'this' (`cpp/assignment-does-not-return-this`) | Fewer false positive results | This query no longer reports incorrect results in template classes. |
|
| Overloaded assignment does not return 'this' (`cpp/assignment-does-not-return-this`) | Fewer false positive results | This query no longer reports incorrect results in template classes. |
|
||||||
| Unsafe array for days of the year (`cpp/leap-year/unsafe-array-for-days-of-the-year`) | | This query is no longer run on LGTM. |
|
| Unsafe array for days of the year (`cpp/leap-year/unsafe-array-for-days-of-the-year`) | | This query is no longer run on LGTM. |
|
||||||
|
| Unsigned comparison to zero (`cpp/unsigned-comparison-zero`) | More correct results | This query now also looks for comparisons of the form `0 <= x`. |
|
||||||
|
|
||||||
## Changes to libraries
|
## Changes to libraries
|
||||||
|
|
||||||
@@ -43,6 +44,7 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
|
|||||||
* The `LocalScopeVariableReachability` library is deprecated in favor of
|
* The `LocalScopeVariableReachability` library is deprecated in favor of
|
||||||
`StackVariableReachability`. The functionality is the same.
|
`StackVariableReachability`. The functionality is the same.
|
||||||
* The models library models `strlen` in more detail, and includes common variations such as `wcslen`.
|
* The models library models `strlen` in more detail, and includes common variations such as `wcslen`.
|
||||||
|
* The models library models `gets` and similar functions.
|
||||||
* The taint tracking library (`semmle.code.cpp.dataflow.TaintTracking`) has had
|
* The taint tracking library (`semmle.code.cpp.dataflow.TaintTracking`) has had
|
||||||
the following improvements:
|
the following improvements:
|
||||||
* The library now models data flow through `strdup` and similar functions.
|
* The library now models data flow through `strdup` and similar functions.
|
||||||
|
|||||||
@@ -23,7 +23,9 @@
|
|||||||
|
|
||||||
* Support for the following frameworks and libraries has been improved:
|
* Support for the following frameworks and libraries has been improved:
|
||||||
- [Electron](https://electronjs.org/)
|
- [Electron](https://electronjs.org/)
|
||||||
|
- [fstream](https://www.npmjs.com/package/fstream)
|
||||||
- [Handlebars](https://www.npmjs.com/package/handlebars)
|
- [Handlebars](https://www.npmjs.com/package/handlebars)
|
||||||
|
- [jsonfile](https://www.npmjs.com/package/jsonfile)
|
||||||
- [Koa](https://www.npmjs.com/package/koa)
|
- [Koa](https://www.npmjs.com/package/koa)
|
||||||
- [Node.js](https://nodejs.org/)
|
- [Node.js](https://nodejs.org/)
|
||||||
- [Socket.IO](https://socket.io/)
|
- [Socket.IO](https://socket.io/)
|
||||||
@@ -35,10 +37,17 @@
|
|||||||
- [jQuery](https://jquery.com/)
|
- [jQuery](https://jquery.com/)
|
||||||
- [lazy-cache](https://www.npmjs.com/package/lazy-cache)
|
- [lazy-cache](https://www.npmjs.com/package/lazy-cache)
|
||||||
- [mongodb](https://www.npmjs.com/package/mongodb)
|
- [mongodb](https://www.npmjs.com/package/mongodb)
|
||||||
|
- [ncp](https://www.npmjs.com/package/ncp)
|
||||||
|
- [node-dir](https://www.npmjs.com/package/node-dir)
|
||||||
|
- [path-exists](https://www.npmjs.com/package/path-exists)
|
||||||
- [react](https://www.npmjs.com/package/react)
|
- [react](https://www.npmjs.com/package/react)
|
||||||
|
- [recursive-readdir](https://www.npmjs.com/package/recursive-readdir)
|
||||||
- [request](https://www.npmjs.com/package/request)
|
- [request](https://www.npmjs.com/package/request)
|
||||||
|
- [rimraf](https://www.npmjs.com/package/rimraf)
|
||||||
- [send](https://www.npmjs.com/package/send)
|
- [send](https://www.npmjs.com/package/send)
|
||||||
- [typeahead.js](https://www.npmjs.com/package/typeahead.js)
|
- [typeahead.js](https://www.npmjs.com/package/typeahead.js)
|
||||||
|
- [vinyl-fs](https://www.npmjs.com/package/vinyl-fs)
|
||||||
|
- [write-file-atomic](https://www.npmjs.com/package/write-file-atomic)
|
||||||
- [ws](https://github.com/websockets/ws)
|
- [ws](https://github.com/websockets/ws)
|
||||||
|
|
||||||
## New queries
|
## New queries
|
||||||
|
|||||||
@@ -19,15 +19,19 @@ class ConstantZero extends Expr {
|
|||||||
* Holds if `candidate` is an expression such that if it's unsigned then we
|
* Holds if `candidate` is an expression such that if it's unsigned then we
|
||||||
* want an alert at `ge`.
|
* want an alert at `ge`.
|
||||||
*/
|
*/
|
||||||
private predicate lookForUnsignedAt(GEExpr ge, Expr candidate) {
|
private predicate lookForUnsignedAt(RelationalOperation ge, Expr candidate) {
|
||||||
// Base case: `candidate >= 0`
|
// Base case: `candidate >= 0` (or `0 <= candidate`)
|
||||||
ge.getRightOperand() instanceof ConstantZero and
|
(
|
||||||
candidate = ge.getLeftOperand().getFullyConverted() and
|
ge instanceof GEExpr or
|
||||||
// left operand was a signed or unsigned IntegralType before conversions
|
ge instanceof LEExpr
|
||||||
|
) and
|
||||||
|
ge.getLesserOperand() instanceof ConstantZero and
|
||||||
|
candidate = ge.getGreaterOperand().getFullyConverted() and
|
||||||
|
// left/greater operand was a signed or unsigned IntegralType before conversions
|
||||||
// (not a pointer, checking a pointer >= 0 is an entirely different mistake)
|
// (not a pointer, checking a pointer >= 0 is an entirely different mistake)
|
||||||
// (not an enum, as the fully converted type of an enum is compiler dependent
|
// (not an enum, as the fully converted type of an enum is compiler dependent
|
||||||
// so checking an enum >= 0 is always reasonable)
|
// so checking an enum >= 0 is always reasonable)
|
||||||
ge.getLeftOperand().getUnderlyingType() instanceof IntegralType
|
ge.getGreaterOperand().getUnderlyingType() instanceof IntegralType
|
||||||
or
|
or
|
||||||
// Recursive case: `...(largerType)candidate >= 0`
|
// Recursive case: `...(largerType)candidate >= 0`
|
||||||
exists(Conversion conversion |
|
exists(Conversion conversion |
|
||||||
@@ -37,7 +41,7 @@ private predicate lookForUnsignedAt(GEExpr ge, Expr candidate) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
class UnsignedGEZero extends GEExpr {
|
class UnsignedGEZero extends ComparisonOperation {
|
||||||
UnsignedGEZero() {
|
UnsignedGEZero() {
|
||||||
exists(Expr ue |
|
exists(Expr ue |
|
||||||
lookForUnsignedAt(this, ue) and
|
lookForUnsignedAt(this, ue) and
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
|
||||||
|
void callSetuidAndCheck(int uid) {
|
||||||
|
if (setuid(uid) != 0) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void callSetgidAndCheck(int gid) {
|
||||||
|
if (setgid(gid) != 0) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Correct ways to drop priv.
|
||||||
|
|
||||||
|
void correctDropPrivInline() {
|
||||||
|
if (setgroups(0, NULL)) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setgid(-2) != 0) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setuid(-2) != 0) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void correctDropPrivInScope() {
|
||||||
|
{
|
||||||
|
if (setgroups(0, NULL)) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
if (setgid(-2) != 0) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
if (setuid(-2) != 0) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void correctOrderForInitgroups() {
|
||||||
|
struct passwd *pw = getpwuid(0);
|
||||||
|
if (pw) {
|
||||||
|
if (initgroups(pw->pw_name, -2)) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Unhandled.
|
||||||
|
}
|
||||||
|
int rc = setuid(-2);
|
||||||
|
if (rc) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void correctDropPrivInScopeParent() {
|
||||||
|
{
|
||||||
|
callSetgidAndCheck(-2);
|
||||||
|
}
|
||||||
|
correctOrderForInitgroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
void incorrectNoReturnCodeCheck() {
|
||||||
|
int user = -2;
|
||||||
|
if (user) {
|
||||||
|
if (user) {
|
||||||
|
int rc = setgid(user);
|
||||||
|
(void)rc;
|
||||||
|
initgroups("nobody", user);
|
||||||
|
}
|
||||||
|
if (user) {
|
||||||
|
setuid(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void correctDropPrivInFunctionCall() {
|
||||||
|
if (setgroups(0, NULL)) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
callSetgidAndCheck(-2);
|
||||||
|
callSetuidAndCheck(-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Incorrect, out of order gid and uid.
|
||||||
|
/// Calling uid before gid will fail.
|
||||||
|
|
||||||
|
void incorrectDropPrivOutOfOrderInline() {
|
||||||
|
if (setuid(-2) != 0) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setgid(-2) != 0) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void incorrectDropPrivOutOfOrderInScope() {
|
||||||
|
{
|
||||||
|
if (setuid(-2) != 0) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setgid(-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void incorrectDropPrivOutOfOrderWithFunction() {
|
||||||
|
callSetuidAndCheck(-2);
|
||||||
|
|
||||||
|
if (setgid(-2) != 0) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void incorrectDropPrivOutOfOrderWithFunction2() {
|
||||||
|
callSetuidAndCheck(-2);
|
||||||
|
callSetgidAndCheck(-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void incorrectDropPrivNoCheck() {
|
||||||
|
setgid(-2);
|
||||||
|
setuid(-2);
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
<overview>
|
||||||
|
<p>The code attempts to drop privilege in an incorrect order by
|
||||||
|
erroneous dropping user privilege before groups. This has security
|
||||||
|
impact if the return codes are not checked.</p>
|
||||||
|
|
||||||
|
<p>False positives include code performing negative checks, making
|
||||||
|
sure that setgid or setgroups does not work, meaning permissions are
|
||||||
|
dropped. Additionally, other forms of sandboxing may be present removing
|
||||||
|
any residual risk, for example a dedicated user namespace.</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>Set the new group ID, then set the target user's intended groups by
|
||||||
|
dropping previous supplemental source groups and initializing target
|
||||||
|
groups, and finally set the target user.</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<example>
|
||||||
|
<p>The following example demonstrates out of order calls.</p>
|
||||||
|
<sample src="PrivilegeDroppingOutoforder.c" />
|
||||||
|
|
||||||
|
</example>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
<li>CERT C Coding Standard:
|
||||||
|
<a href="https://wiki.sei.cmu.edu/confluence/display/c/POS37-C.+Ensure+that+privilege+relinquishment+is+successful">POS37-C. Ensure that privilege relinquishment is successful</a>.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
/**
|
||||||
|
* @name LinuxPrivilegeDroppingOutoforder
|
||||||
|
* @description A syscall commonly associated with privilege dropping is being called out of order.
|
||||||
|
* Normally a process drops group ID and sets supplimental groups for the target user
|
||||||
|
* before setting the target user ID. This can have security impact if the return code
|
||||||
|
* from these methods is not checked.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @id cpp/drop-linux-privileges-outoforder
|
||||||
|
* @tags security
|
||||||
|
* external/cwe/cwe-273
|
||||||
|
* @precision medium
|
||||||
|
*/
|
||||||
|
|
||||||
|
import cpp
|
||||||
|
|
||||||
|
predicate argumentMayBeRoot(Expr e) {
|
||||||
|
e.getValue() = "0" or
|
||||||
|
e.(VariableAccess).getTarget().getName().toLowerCase().matches("%root%")
|
||||||
|
}
|
||||||
|
|
||||||
|
class SetuidLikeFunctionCall extends FunctionCall {
|
||||||
|
SetuidLikeFunctionCall() {
|
||||||
|
(getTarget().hasGlobalName("setuid") or getTarget().hasGlobalName("setresuid")) and
|
||||||
|
// setuid/setresuid with the root user are false positives.
|
||||||
|
not argumentMayBeRoot(getArgument(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SetuidLikeWrapperCall extends FunctionCall {
|
||||||
|
SetuidLikeFunctionCall baseCall;
|
||||||
|
|
||||||
|
SetuidLikeWrapperCall() {
|
||||||
|
this = baseCall
|
||||||
|
or
|
||||||
|
exists(SetuidLikeWrapperCall fc |
|
||||||
|
this.getTarget() = fc.getEnclosingFunction() and
|
||||||
|
baseCall = fc.getBaseCall()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
SetuidLikeFunctionCall getBaseCall() { result = baseCall }
|
||||||
|
}
|
||||||
|
|
||||||
|
class CallBeforeSetuidFunctionCall extends FunctionCall {
|
||||||
|
CallBeforeSetuidFunctionCall() {
|
||||||
|
(
|
||||||
|
getTarget().hasGlobalName("setgid") or
|
||||||
|
getTarget().hasGlobalName("setresgid") or
|
||||||
|
// Compatibility may require skipping initgroups and setgroups return checks.
|
||||||
|
// A stricter best practice is to check the result and errnor for EPERM.
|
||||||
|
getTarget().hasGlobalName("initgroups") or
|
||||||
|
getTarget().hasGlobalName("setgroups")
|
||||||
|
) and
|
||||||
|
// setgid/setresgid/etc with the root group are false positives.
|
||||||
|
not argumentMayBeRoot(getArgument(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CallBeforeSetuidWrapperCall extends FunctionCall {
|
||||||
|
CallBeforeSetuidFunctionCall baseCall;
|
||||||
|
|
||||||
|
CallBeforeSetuidWrapperCall() {
|
||||||
|
this = baseCall
|
||||||
|
or
|
||||||
|
exists(CallBeforeSetuidWrapperCall fc |
|
||||||
|
this.getTarget() = fc.getEnclosingFunction() and
|
||||||
|
baseCall = fc.getBaseCall()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
CallBeforeSetuidFunctionCall getBaseCall() { result = baseCall }
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate setuidBeforeSetgid(
|
||||||
|
SetuidLikeWrapperCall setuidWrapper, CallBeforeSetuidWrapperCall setgidWrapper
|
||||||
|
) {
|
||||||
|
setgidWrapper.getAPredecessor+() = setuidWrapper
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate isAccessed(FunctionCall fc) {
|
||||||
|
exists(Variable v | v.getAnAssignedValue() = fc)
|
||||||
|
or
|
||||||
|
exists(Operation c | fc = c.getAChild() | c.isCondition())
|
||||||
|
or
|
||||||
|
// ignore pattern where result is intentionally ignored by a cast to void.
|
||||||
|
fc.hasExplicitConversion()
|
||||||
|
}
|
||||||
|
|
||||||
|
from Function func, CallBeforeSetuidFunctionCall fc, SetuidLikeFunctionCall setuid
|
||||||
|
where
|
||||||
|
setuidBeforeSetgid(setuid, fc) and
|
||||||
|
// Require the call return code to be used in a condition or assigned.
|
||||||
|
// This introduces false negatives where the return is checked but then
|
||||||
|
// errno == EPERM allows execution to continue.
|
||||||
|
not isAccessed(fc) and
|
||||||
|
func = fc.getEnclosingFunction()
|
||||||
|
select fc,
|
||||||
|
"This function is called within " + func + ", and potentially after " +
|
||||||
|
"$@, and may not succeed. Be sure to check the return code and errno, otherwise permissions " +
|
||||||
|
"may not be dropped.", setuid, setuid.getTarget().getName()
|
||||||
@@ -381,10 +381,10 @@ class StaticStorageDurationVariable extends Variable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if the initializer for this variable is evaluated at compile time.
|
* Holds if the initializer for this variable is evaluated at runtime.
|
||||||
*/
|
*/
|
||||||
predicate hasConstantInitialization() {
|
predicate hasDynamicInitialization() {
|
||||||
not runtimeExprInStaticInitializer(this.getInitializer().getExpr())
|
runtimeExprInStaticInitializer(this.getInitializer().getExpr())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,6 +409,11 @@ private predicate inStaticInitializer(Expr e) {
|
|||||||
inStaticInitializer(e.getParent())
|
inStaticInitializer(e.getParent())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A C++ local variable declared as `static`.
|
||||||
|
*/
|
||||||
|
class StaticLocalVariable extends LocalVariable, StaticStorageDurationVariable { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A C/C++ variable which has global scope or namespace scope. For example the
|
* A C/C++ variable which has global scope or namespace scope. For example the
|
||||||
* variables `a` and `b` in the following code:
|
* variables `a` and `b` in the following code:
|
||||||
|
|||||||
@@ -441,9 +441,9 @@ private Node getControlOrderChildSparse(Node n, int i) {
|
|||||||
* thus should not have control flow computed.
|
* thus should not have control flow computed.
|
||||||
*/
|
*/
|
||||||
private predicate skipInitializer(Initializer init) {
|
private predicate skipInitializer(Initializer init) {
|
||||||
exists(LocalVariable local |
|
exists(StaticLocalVariable local |
|
||||||
init = local.getInitializer() and
|
init = local.getInitializer() and
|
||||||
local.(StaticStorageDurationVariable).hasConstantInitialization()
|
not local.hasDynamicInitialization()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
|
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||||
|
|
||||||
|
override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
|
private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
|
||||||
@@ -96,6 +98,8 @@ private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
|
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||||
|
|
||||||
|
override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FromGlobalVarTaintTrackingCfg extends DataFlow2::Configuration {
|
private class FromGlobalVarTaintTrackingCfg extends DataFlow2::Configuration {
|
||||||
@@ -119,6 +123,8 @@ private class FromGlobalVarTaintTrackingCfg extends DataFlow2::Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
|
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||||
|
|
||||||
|
override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate readsVariable(LoadInstruction load, Variable var) {
|
private predicate readsVariable(LoadInstruction load, Variable var) {
|
||||||
@@ -153,6 +159,11 @@ private predicate nodeIsBarrier(DataFlow::Node node) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private predicate nodeIsBarrierIn(DataFlow::Node node) {
|
||||||
|
// don't use dataflow into taint sources, as this leads to duplicate results.
|
||||||
|
node = getNodeForSource(any(Expr e))
|
||||||
|
}
|
||||||
|
|
||||||
private predicate instructionTaintStep(Instruction i1, Instruction i2) {
|
private predicate instructionTaintStep(Instruction i1, Instruction i2) {
|
||||||
// Expressions computed from tainted data are also tainted
|
// Expressions computed from tainted data are also tainted
|
||||||
exists(CallInstruction call, int argIndex | call = i2 |
|
exists(CallInstruction call, int argIndex | call = i2 |
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ class IRVariable extends TIRVariable {
|
|||||||
IRVariable() {
|
IRVariable() {
|
||||||
this = TIRUserVariable(_, _, func) or
|
this = TIRUserVariable(_, _, func) or
|
||||||
this = TIRTempVariable(func, _, _, _) or
|
this = TIRTempVariable(func, _, _, _) or
|
||||||
this = TIRStringLiteral(func, _, _, _)
|
this = TIRStringLiteral(func, _, _, _) or
|
||||||
|
this = TIRDynamicInitializationFlag(func, _, _)
|
||||||
}
|
}
|
||||||
|
|
||||||
string toString() { none() }
|
string toString() { none() }
|
||||||
@@ -149,7 +150,8 @@ class IRGeneratedVariable extends IRVariable {
|
|||||||
|
|
||||||
IRGeneratedVariable() {
|
IRGeneratedVariable() {
|
||||||
this = TIRTempVariable(func, ast, _, type) or
|
this = TIRTempVariable(func, ast, _, type) or
|
||||||
this = TIRStringLiteral(func, ast, type, _)
|
this = TIRStringLiteral(func, ast, type, _) or
|
||||||
|
this = TIRDynamicInitializationFlag(func, ast, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
final override Language::LanguageType getLanguageType() { result = type }
|
final override Language::LanguageType getLanguageType() { result = type }
|
||||||
@@ -208,7 +210,7 @@ class IRReturnVariable extends IRTempVariable {
|
|||||||
class IRThrowVariable extends IRTempVariable {
|
class IRThrowVariable extends IRTempVariable {
|
||||||
IRThrowVariable() { tag = ThrowTempVar() }
|
IRThrowVariable() { tag = ThrowTempVar() }
|
||||||
|
|
||||||
override string getBaseString() { result = "#throw" }
|
final override string getBaseString() { result = "#throw" }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -236,7 +238,30 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
|||||||
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
|
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
|
||||||
}
|
}
|
||||||
|
|
||||||
override string getBaseString() { result = "#string" }
|
final override string getBaseString() { result = "#string" }
|
||||||
|
|
||||||
final Language::StringLiteral getLiteral() { result = literal }
|
final Language::StringLiteral getLiteral() { result = literal }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A variable generated to track whether a specific non-stack variable has been initialized. This is
|
||||||
|
* used to model the runtime initialization of static local variables in C++, as well as static
|
||||||
|
* fields in C#.
|
||||||
|
*/
|
||||||
|
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
|
||||||
|
Language::Variable var;
|
||||||
|
|
||||||
|
IRDynamicInitializationFlag() {
|
||||||
|
this = TIRDynamicInitializationFlag(func, var, type) and ast = var
|
||||||
|
}
|
||||||
|
|
||||||
|
final override string toString() { result = var.toString() + "#init" }
|
||||||
|
|
||||||
|
final Language::Variable getVariable() { result = var }
|
||||||
|
|
||||||
|
final override string getUniqueId() {
|
||||||
|
result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ newtype TIRVariable =
|
|||||||
) {
|
) {
|
||||||
Construction::hasTempVariable(func, ast, tag, type)
|
Construction::hasTempVariable(func, ast, tag, type)
|
||||||
} or
|
} or
|
||||||
|
TIRDynamicInitializationFlag(
|
||||||
|
Language::Function func, Language::Variable var, Language::LanguageType type
|
||||||
|
) {
|
||||||
|
Construction::hasDynamicInitializationFlag(func, var, type)
|
||||||
|
} or
|
||||||
TIRStringLiteral(
|
TIRStringLiteral(
|
||||||
Language::Function func, Language::AST ast, Language::LanguageType type,
|
Language::Function func, Language::AST ast, Language::LanguageType type,
|
||||||
Language::StringLiteral literal
|
Language::StringLiteral literal
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ class IRVariable extends TIRVariable {
|
|||||||
IRVariable() {
|
IRVariable() {
|
||||||
this = TIRUserVariable(_, _, func) or
|
this = TIRUserVariable(_, _, func) or
|
||||||
this = TIRTempVariable(func, _, _, _) or
|
this = TIRTempVariable(func, _, _, _) or
|
||||||
this = TIRStringLiteral(func, _, _, _)
|
this = TIRStringLiteral(func, _, _, _) or
|
||||||
|
this = TIRDynamicInitializationFlag(func, _, _)
|
||||||
}
|
}
|
||||||
|
|
||||||
string toString() { none() }
|
string toString() { none() }
|
||||||
@@ -149,7 +150,8 @@ class IRGeneratedVariable extends IRVariable {
|
|||||||
|
|
||||||
IRGeneratedVariable() {
|
IRGeneratedVariable() {
|
||||||
this = TIRTempVariable(func, ast, _, type) or
|
this = TIRTempVariable(func, ast, _, type) or
|
||||||
this = TIRStringLiteral(func, ast, type, _)
|
this = TIRStringLiteral(func, ast, type, _) or
|
||||||
|
this = TIRDynamicInitializationFlag(func, ast, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
final override Language::LanguageType getLanguageType() { result = type }
|
final override Language::LanguageType getLanguageType() { result = type }
|
||||||
@@ -208,7 +210,7 @@ class IRReturnVariable extends IRTempVariable {
|
|||||||
class IRThrowVariable extends IRTempVariable {
|
class IRThrowVariable extends IRTempVariable {
|
||||||
IRThrowVariable() { tag = ThrowTempVar() }
|
IRThrowVariable() { tag = ThrowTempVar() }
|
||||||
|
|
||||||
override string getBaseString() { result = "#throw" }
|
final override string getBaseString() { result = "#throw" }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -236,7 +238,30 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
|||||||
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
|
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
|
||||||
}
|
}
|
||||||
|
|
||||||
override string getBaseString() { result = "#string" }
|
final override string getBaseString() { result = "#string" }
|
||||||
|
|
||||||
final Language::StringLiteral getLiteral() { result = literal }
|
final Language::StringLiteral getLiteral() { result = literal }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A variable generated to track whether a specific non-stack variable has been initialized. This is
|
||||||
|
* used to model the runtime initialization of static local variables in C++, as well as static
|
||||||
|
* fields in C#.
|
||||||
|
*/
|
||||||
|
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
|
||||||
|
Language::Variable var;
|
||||||
|
|
||||||
|
IRDynamicInitializationFlag() {
|
||||||
|
this = TIRDynamicInitializationFlag(func, var, type) and ast = var
|
||||||
|
}
|
||||||
|
|
||||||
|
final override string toString() { result = var.toString() + "#init" }
|
||||||
|
|
||||||
|
final Language::Variable getVariable() { result = var }
|
||||||
|
|
||||||
|
final override string getUniqueId() {
|
||||||
|
result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
|
||||||
|
}
|
||||||
|
|||||||
@@ -51,6 +51,13 @@ private module Cached {
|
|||||||
getTypeForPRValue(literal.getType()) = type
|
getTypeForPRValue(literal.getType()) = type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cached
|
||||||
|
predicate hasDynamicInitializationFlag(Function func, StaticLocalVariable var, CppType type) {
|
||||||
|
var.getFunction() = func and
|
||||||
|
var.hasDynamicInitialization() and
|
||||||
|
type = getBoolType()
|
||||||
|
}
|
||||||
|
|
||||||
cached
|
cached
|
||||||
predicate hasModeledMemoryResult(Instruction instruction) { none() }
|
predicate hasModeledMemoryResult(Instruction instruction) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ newtype TInstructionTag =
|
|||||||
InitializerStoreTag() or
|
InitializerStoreTag() or
|
||||||
InitializerIndirectAddressTag() or
|
InitializerIndirectAddressTag() or
|
||||||
InitializerIndirectStoreTag() or
|
InitializerIndirectStoreTag() or
|
||||||
|
DynamicInitializationFlagAddressTag() or
|
||||||
|
DynamicInitializationFlagLoadTag() or
|
||||||
|
DynamicInitializationConditionalBranchTag() or
|
||||||
|
DynamicInitializationFlagConstantTag() or
|
||||||
|
DynamicInitializationFlagStoreTag() or
|
||||||
ZeroPadStringConstantTag() or
|
ZeroPadStringConstantTag() or
|
||||||
ZeroPadStringElementIndexTag() or
|
ZeroPadStringElementIndexTag() or
|
||||||
ZeroPadStringElementAddressTag() or
|
ZeroPadStringElementAddressTag() or
|
||||||
@@ -186,4 +191,14 @@ string getInstructionTagId(TInstructionTag tag) {
|
|||||||
tag = AsmTag() and result = "Asm"
|
tag = AsmTag() and result = "Asm"
|
||||||
or
|
or
|
||||||
exists(int index | tag = AsmInputTag(index) and result = "AsmInputTag(" + index + ")")
|
exists(int index | tag = AsmInputTag(index) and result = "AsmInputTag(" + index + ")")
|
||||||
|
or
|
||||||
|
tag = DynamicInitializationFlagAddressTag() and result = "DynInitFlagAddr"
|
||||||
|
or
|
||||||
|
tag = DynamicInitializationFlagLoadTag() and result = "DynInitFlagLoad"
|
||||||
|
or
|
||||||
|
tag = DynamicInitializationConditionalBranchTag() and result = "DynInitCondBranch"
|
||||||
|
or
|
||||||
|
tag = DynamicInitializationFlagConstantTag() and result = "DynInitFlagConst"
|
||||||
|
or
|
||||||
|
tag = DynamicInitializationFlagStoreTag() and result = "DynInitFlagStore"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ private import semmle.code.cpp.ir.internal.IRUtilities
|
|||||||
private import InstructionTag
|
private import InstructionTag
|
||||||
private import TranslatedElement
|
private import TranslatedElement
|
||||||
private import TranslatedExpr
|
private import TranslatedExpr
|
||||||
|
private import TranslatedFunction
|
||||||
private import TranslatedInitialization
|
private import TranslatedInitialization
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,17 +67,172 @@ abstract class TranslatedLocalVariableDeclaration extends TranslatedVariableInit
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the IR translation of a local variable declaration within a declaration statement.
|
* The IR translation of a local variable declaration within a declaration statement.
|
||||||
*/
|
*/
|
||||||
class TranslatedVariableDeclarationEntry extends TranslatedLocalVariableDeclaration,
|
class TranslatedAutoVariableDeclarationEntry extends TranslatedLocalVariableDeclaration,
|
||||||
TranslatedDeclarationEntry {
|
TranslatedDeclarationEntry {
|
||||||
LocalVariable var;
|
StackVariable var;
|
||||||
|
|
||||||
TranslatedVariableDeclarationEntry() { var = entry.getDeclaration() }
|
TranslatedAutoVariableDeclarationEntry() { var = entry.getDeclaration() }
|
||||||
|
|
||||||
override LocalVariable getVariable() { result = var }
|
override LocalVariable getVariable() { result = var }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The IR translation of the declaration of a static local variable.
|
||||||
|
* This element generates the logic that determines whether or not the variable has already been
|
||||||
|
* initialized, and if not, invokes the initializer and sets the dynamic initialization flag for the
|
||||||
|
* variable. The actual initialization code is handled in
|
||||||
|
* `TranslatedStaticLocalVariableInitialization`, which is a child of this element.
|
||||||
|
*
|
||||||
|
* The generated code to do the initialization only once is:
|
||||||
|
* ```
|
||||||
|
* Block 1
|
||||||
|
* r1225_1(glval<bool>) = VariableAddress[c#init] :
|
||||||
|
* r1225_2(bool) = Load : &:r1225_1, ~mu1222_4
|
||||||
|
* v1225_3(void) = ConditionalBranch : r1225_2
|
||||||
|
* False -> Block 2
|
||||||
|
* True -> Block 3
|
||||||
|
*
|
||||||
|
* Block 2
|
||||||
|
* r1225_4(glval<int>) = VariableAddress[c] :
|
||||||
|
* <actual initialization of `c`>
|
||||||
|
* r1225_8(bool) = Constant[1] :
|
||||||
|
* mu1225_9(bool) = Store : &:r1225_1, r1225_8
|
||||||
|
* Goto -> Block 3
|
||||||
|
*
|
||||||
|
* Block 3
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Note that the flag variable, `c#init`, is assumed to be zero-initialized at program startup, just
|
||||||
|
* like any other variable with static storage duration.
|
||||||
|
*/
|
||||||
|
class TranslatedStaticLocalVariableDeclarationEntry extends TranslatedDeclarationEntry {
|
||||||
|
StaticLocalVariable var;
|
||||||
|
|
||||||
|
TranslatedStaticLocalVariableDeclarationEntry() { var = entry.getDeclaration() }
|
||||||
|
|
||||||
|
final override TranslatedElement getChild(int id) { id = 0 and result = getInitialization() }
|
||||||
|
|
||||||
|
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) {
|
||||||
|
tag = DynamicInitializationFlagAddressTag() and
|
||||||
|
opcode instanceof Opcode::VariableAddress and
|
||||||
|
type = getBoolGLValueType()
|
||||||
|
or
|
||||||
|
tag = DynamicInitializationFlagLoadTag() and
|
||||||
|
opcode instanceof Opcode::Load and
|
||||||
|
type = getBoolType()
|
||||||
|
or
|
||||||
|
tag = DynamicInitializationConditionalBranchTag() and
|
||||||
|
opcode instanceof Opcode::ConditionalBranch and
|
||||||
|
type = getVoidType()
|
||||||
|
or
|
||||||
|
tag = DynamicInitializationFlagConstantTag() and
|
||||||
|
opcode instanceof Opcode::Constant and
|
||||||
|
type = getBoolType()
|
||||||
|
or
|
||||||
|
tag = DynamicInitializationFlagStoreTag() and
|
||||||
|
opcode instanceof Opcode::Store and
|
||||||
|
type = getBoolType()
|
||||||
|
}
|
||||||
|
|
||||||
|
final override Instruction getFirstInstruction() {
|
||||||
|
result = getInstruction(DynamicInitializationFlagAddressTag())
|
||||||
|
}
|
||||||
|
|
||||||
|
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||||
|
tag = DynamicInitializationFlagAddressTag() and
|
||||||
|
kind instanceof GotoEdge and
|
||||||
|
result = getInstruction(DynamicInitializationFlagLoadTag())
|
||||||
|
or
|
||||||
|
tag = DynamicInitializationFlagLoadTag() and
|
||||||
|
kind instanceof GotoEdge and
|
||||||
|
result = getInstruction(DynamicInitializationConditionalBranchTag())
|
||||||
|
or
|
||||||
|
tag = DynamicInitializationConditionalBranchTag() and
|
||||||
|
(
|
||||||
|
kind instanceof TrueEdge and
|
||||||
|
result = getParent().getChildSuccessor(this)
|
||||||
|
or
|
||||||
|
kind instanceof FalseEdge and
|
||||||
|
result = getInitialization().getFirstInstruction()
|
||||||
|
)
|
||||||
|
or
|
||||||
|
tag = DynamicInitializationFlagConstantTag() and
|
||||||
|
kind instanceof GotoEdge and
|
||||||
|
result = getInstruction(DynamicInitializationFlagStoreTag())
|
||||||
|
or
|
||||||
|
tag = DynamicInitializationFlagStoreTag() and
|
||||||
|
kind instanceof GotoEdge and
|
||||||
|
result = getParent().getChildSuccessor(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
final override Instruction getChildSuccessor(TranslatedElement child) {
|
||||||
|
child = getInitialization() and
|
||||||
|
result = getInstruction(DynamicInitializationFlagConstantTag())
|
||||||
|
}
|
||||||
|
|
||||||
|
final override IRDynamicInitializationFlag getInstructionVariable(InstructionTag tag) {
|
||||||
|
tag = DynamicInitializationFlagAddressTag() and
|
||||||
|
result.getVariable() = var
|
||||||
|
}
|
||||||
|
|
||||||
|
final override string getInstructionConstantValue(InstructionTag tag) {
|
||||||
|
tag = DynamicInitializationFlagConstantTag() and result = "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||||
|
tag = DynamicInitializationFlagLoadTag() and
|
||||||
|
(
|
||||||
|
operandTag instanceof AddressOperandTag and
|
||||||
|
result = getInstruction(DynamicInitializationFlagAddressTag())
|
||||||
|
or
|
||||||
|
operandTag instanceof LoadOperandTag and
|
||||||
|
result = getTranslatedFunction(var.getFunction()).getUnmodeledDefinitionInstruction()
|
||||||
|
)
|
||||||
|
or
|
||||||
|
tag = DynamicInitializationConditionalBranchTag() and
|
||||||
|
operandTag instanceof ConditionOperandTag and
|
||||||
|
result = getInstruction(DynamicInitializationFlagLoadTag())
|
||||||
|
or
|
||||||
|
tag = DynamicInitializationFlagStoreTag() and
|
||||||
|
(
|
||||||
|
operandTag instanceof AddressOperandTag and
|
||||||
|
result = getInstruction(DynamicInitializationFlagAddressTag())
|
||||||
|
or
|
||||||
|
operandTag instanceof StoreValueOperandTag and
|
||||||
|
result = getInstruction(DynamicInitializationFlagConstantTag())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private TranslatedStaticLocalVariableInitialization getInitialization() {
|
||||||
|
result.getVariable() = var
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The initialization of a static local variable. This element will only exist for a static variable
|
||||||
|
* with a dynamic initializer.
|
||||||
|
*/
|
||||||
|
class TranslatedStaticLocalVariableInitialization extends TranslatedElement,
|
||||||
|
TranslatedLocalVariableDeclaration, TTranslatedStaticLocalVariableInitialization {
|
||||||
|
VariableDeclarationEntry entry;
|
||||||
|
StaticLocalVariable var;
|
||||||
|
|
||||||
|
TranslatedStaticLocalVariableInitialization() {
|
||||||
|
this = TTranslatedStaticLocalVariableInitialization(entry) and
|
||||||
|
var = entry.getDeclaration()
|
||||||
|
}
|
||||||
|
|
||||||
|
final override string toString() { result = "init: " + entry.toString() }
|
||||||
|
|
||||||
|
final override Locatable getAST() { result = entry }
|
||||||
|
|
||||||
|
final override LocalVariable getVariable() { result = var }
|
||||||
|
|
||||||
|
final override Function getFunction() { result = var.getFunction() }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the `TranslatedRangeBasedForVariableDeclaration` that represents the declaration of
|
* Gets the `TranslatedRangeBasedForVariableDeclaration` that represents the declaration of
|
||||||
* `var`.
|
* `var`.
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ private predicate ignoreExprAndDescendants(Expr expr) {
|
|||||||
// Otherwise the initializer does not run in function scope.
|
// Otherwise the initializer does not run in function scope.
|
||||||
exists(Initializer init, StaticStorageDurationVariable var |
|
exists(Initializer init, StaticStorageDurationVariable var |
|
||||||
init = var.getInitializer() and
|
init = var.getInitializer() and
|
||||||
var.hasConstantInitialization() and
|
not var.hasDynamicInitialization() and
|
||||||
expr = init.getExpr().getFullyConverted()
|
expr = init.getExpr().getFullyConverted()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -256,6 +256,26 @@ predicate hasTranslatedLoad(Expr expr) {
|
|||||||
not ignoreLoad(expr)
|
not ignoreLoad(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if the specified `DeclarationEntry` needs an IR translation. An IR translation is only
|
||||||
|
* necessary for automatic local variables, or for static local variables with dynamic
|
||||||
|
* initialization.
|
||||||
|
*/
|
||||||
|
private predicate translateDeclarationEntry(DeclarationEntry entry) {
|
||||||
|
exists(DeclStmt declStmt, LocalVariable var |
|
||||||
|
translateStmt(declStmt) and
|
||||||
|
declStmt.getADeclarationEntry() = entry and
|
||||||
|
// Only declarations of local variables need to be translated to IR.
|
||||||
|
var = entry.getDeclaration() and
|
||||||
|
(
|
||||||
|
not var.isStatic()
|
||||||
|
or
|
||||||
|
// Ignore static variables unless they have a dynamic initializer.
|
||||||
|
var.(StaticLocalVariable).hasDynamicInitialization()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
newtype TTranslatedElement =
|
newtype TTranslatedElement =
|
||||||
// An expression that is not being consumed as a condition
|
// An expression that is not being consumed as a condition
|
||||||
TTranslatedValueExpr(Expr expr) {
|
TTranslatedValueExpr(Expr expr) {
|
||||||
@@ -396,13 +416,12 @@ newtype TTranslatedElement =
|
|||||||
)
|
)
|
||||||
} or
|
} or
|
||||||
// A local declaration
|
// A local declaration
|
||||||
TTranslatedDeclarationEntry(DeclarationEntry entry) {
|
TTranslatedDeclarationEntry(DeclarationEntry entry) { translateDeclarationEntry(entry) } or
|
||||||
exists(DeclStmt declStmt |
|
// The dynamic initialization of a static local variable. This is a separate object from the
|
||||||
translateStmt(declStmt) and
|
// declaration entry.
|
||||||
declStmt.getADeclarationEntry() = entry and
|
TTranslatedStaticLocalVariableInitialization(DeclarationEntry entry) {
|
||||||
// Only declarations of local variables need to be translated to IR.
|
translateDeclarationEntry(entry) and
|
||||||
entry.getDeclaration() instanceof LocalVariable
|
entry.getDeclaration() instanceof StaticLocalVariable
|
||||||
)
|
|
||||||
} or
|
} or
|
||||||
// A compiler-generated variable to implement a range-based for loop. These don't have a
|
// A compiler-generated variable to implement a range-based for loop. These don't have a
|
||||||
// `DeclarationEntry` in the database, so we have to go by the `Variable` itself.
|
// `DeclarationEntry` in the database, so we have to go by the `Variable` itself.
|
||||||
|
|||||||
@@ -115,10 +115,14 @@ abstract class TranslatedVariableInitialization extends TranslatedElement, Initi
|
|||||||
* evaluating the initializer.
|
* evaluating the initializer.
|
||||||
*/
|
*/
|
||||||
final predicate hasUninitializedInstruction() {
|
final predicate hasUninitializedInstruction() {
|
||||||
|
(
|
||||||
not exists(getInitialization()) or
|
not exists(getInitialization()) or
|
||||||
getInitialization() instanceof TranslatedListInitialization or
|
getInitialization() instanceof TranslatedListInitialization or
|
||||||
getInitialization() instanceof TranslatedConstructorInitialization or
|
getInitialization() instanceof TranslatedConstructorInitialization or
|
||||||
getInitialization().(TranslatedStringLiteralInitialization).zeroInitRange(_, _)
|
getInitialization().(TranslatedStringLiteralInitialization).zeroInitRange(_, _)
|
||||||
|
) and
|
||||||
|
// Variables with static or thread-local storage duration are zero-initialized at program startup.
|
||||||
|
getIRVariable() instanceof IRAutomaticVariable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ class IRVariable extends TIRVariable {
|
|||||||
IRVariable() {
|
IRVariable() {
|
||||||
this = TIRUserVariable(_, _, func) or
|
this = TIRUserVariable(_, _, func) or
|
||||||
this = TIRTempVariable(func, _, _, _) or
|
this = TIRTempVariable(func, _, _, _) or
|
||||||
this = TIRStringLiteral(func, _, _, _)
|
this = TIRStringLiteral(func, _, _, _) or
|
||||||
|
this = TIRDynamicInitializationFlag(func, _, _)
|
||||||
}
|
}
|
||||||
|
|
||||||
string toString() { none() }
|
string toString() { none() }
|
||||||
@@ -149,7 +150,8 @@ class IRGeneratedVariable extends IRVariable {
|
|||||||
|
|
||||||
IRGeneratedVariable() {
|
IRGeneratedVariable() {
|
||||||
this = TIRTempVariable(func, ast, _, type) or
|
this = TIRTempVariable(func, ast, _, type) or
|
||||||
this = TIRStringLiteral(func, ast, type, _)
|
this = TIRStringLiteral(func, ast, type, _) or
|
||||||
|
this = TIRDynamicInitializationFlag(func, ast, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
final override Language::LanguageType getLanguageType() { result = type }
|
final override Language::LanguageType getLanguageType() { result = type }
|
||||||
@@ -208,7 +210,7 @@ class IRReturnVariable extends IRTempVariable {
|
|||||||
class IRThrowVariable extends IRTempVariable {
|
class IRThrowVariable extends IRTempVariable {
|
||||||
IRThrowVariable() { tag = ThrowTempVar() }
|
IRThrowVariable() { tag = ThrowTempVar() }
|
||||||
|
|
||||||
override string getBaseString() { result = "#throw" }
|
final override string getBaseString() { result = "#throw" }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -236,7 +238,30 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
|||||||
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
|
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
|
||||||
}
|
}
|
||||||
|
|
||||||
override string getBaseString() { result = "#string" }
|
final override string getBaseString() { result = "#string" }
|
||||||
|
|
||||||
final Language::StringLiteral getLiteral() { result = literal }
|
final Language::StringLiteral getLiteral() { result = literal }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A variable generated to track whether a specific non-stack variable has been initialized. This is
|
||||||
|
* used to model the runtime initialization of static local variables in C++, as well as static
|
||||||
|
* fields in C#.
|
||||||
|
*/
|
||||||
|
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
|
||||||
|
Language::Variable var;
|
||||||
|
|
||||||
|
IRDynamicInitializationFlag() {
|
||||||
|
this = TIRDynamicInitializationFlag(func, var, type) and ast = var
|
||||||
|
}
|
||||||
|
|
||||||
|
final override string toString() { result = var.toString() + "#init" }
|
||||||
|
|
||||||
|
final Language::Variable getVariable() { result = var }
|
||||||
|
|
||||||
|
final override string getUniqueId() {
|
||||||
|
result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
|
||||||
|
}
|
||||||
|
|||||||
@@ -363,6 +363,11 @@ CppPRValueType getIntType() {
|
|||||||
*/
|
*/
|
||||||
CppPRValueType getBoolType() { exists(BoolType type | result.hasType(type, false)) }
|
CppPRValueType getBoolType() { exists(BoolType type | result.hasType(type, false)) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the `CppType` that represents a glvalue of type `bool`.
|
||||||
|
*/
|
||||||
|
CppType getBoolGLValueType() { exists(BoolType type | result.hasType(type, true)) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the `CppType` that represents a glvalue of function type.
|
* Gets the `CppType` that represents a glvalue of function type.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
private import implementations.Allocation
|
private import implementations.Allocation
|
||||||
private import implementations.Deallocation
|
private import implementations.Deallocation
|
||||||
private import implementations.Fread
|
private import implementations.Fread
|
||||||
|
private import implementations.Gets
|
||||||
private import implementations.IdentityFunction
|
private import implementations.IdentityFunction
|
||||||
private import implementations.Inet
|
private import implementations.Inet
|
||||||
private import implementations.Memcpy
|
private import implementations.Memcpy
|
||||||
|
|||||||
45
cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll
Normal file
45
cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import semmle.code.cpp.models.interfaces.DataFlow
|
||||||
|
import semmle.code.cpp.models.interfaces.Taint
|
||||||
|
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||||
|
import semmle.code.cpp.models.interfaces.Alias
|
||||||
|
import semmle.code.cpp.models.interfaces.SideEffect
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The standard functions `gets` and `fgets`.
|
||||||
|
*/
|
||||||
|
class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, AliasFunction,
|
||||||
|
SideEffectFunction {
|
||||||
|
GetsFunction() {
|
||||||
|
exists(string name | hasGlobalOrStdName(name) |
|
||||||
|
name = "gets" or // gets(str)
|
||||||
|
name = "fgets" or // fgets(str, num, stream)
|
||||||
|
name = "fgetws" // fgetws(wstr, num, stream)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||||
|
input.isParameter(0) and
|
||||||
|
output.isReturnValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
|
input.isParameter(2) and
|
||||||
|
output.isParameterDeref(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate parameterNeverEscapes(int index) { index = 2 }
|
||||||
|
|
||||||
|
override predicate parameterEscapesOnlyViaReturn(int index) { index = 0 }
|
||||||
|
|
||||||
|
override predicate parameterIsAlwaysReturned(int index) { index = 0 }
|
||||||
|
|
||||||
|
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||||
|
|
||||||
|
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||||
|
|
||||||
|
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||||
|
i = 0 and
|
||||||
|
buffer = true and
|
||||||
|
mustWrite = true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -67,3 +67,9 @@
|
|||||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:7:88:27 | (bool)... | |
|
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:7:88:27 | (bool)... | |
|
||||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | (const char *)... | |
|
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | (const char *)... | |
|
||||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | copy | |
|
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | copy | |
|
||||||
|
| test.cpp:100:12:100:15 | call to gets | test.cpp:98:8:98:14 | pointer | |
|
||||||
|
| test.cpp:100:12:100:15 | call to gets | test.cpp:100:2:100:8 | pointer | |
|
||||||
|
| test.cpp:100:12:100:15 | call to gets | test.cpp:100:12:100:15 | call to gets | |
|
||||||
|
| test.cpp:100:17:100:22 | buffer | test.cpp:93:18:93:18 | s | |
|
||||||
|
| test.cpp:100:17:100:22 | buffer | test.cpp:97:7:97:12 | buffer | |
|
||||||
|
| test.cpp:100:17:100:22 | buffer | test.cpp:100:17:100:22 | buffer | |
|
||||||
|
|||||||
@@ -11,3 +11,6 @@
|
|||||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:11:20:11:21 | s1 | AST only |
|
| test.cpp:83:28:83:33 | call to getenv | test.cpp:11:20:11:21 | s1 | AST only |
|
||||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:85:8:85:11 | copy | AST only |
|
| test.cpp:83:28:83:33 | call to getenv | test.cpp:85:8:85:11 | copy | AST only |
|
||||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:9:86:12 | copy | AST only |
|
| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:9:86:12 | copy | AST only |
|
||||||
|
| test.cpp:100:12:100:15 | call to gets | test.cpp:100:2:100:8 | pointer | AST only |
|
||||||
|
| test.cpp:100:17:100:22 | buffer | test.cpp:97:7:97:12 | buffer | AST only |
|
||||||
|
| test.cpp:100:17:100:22 | buffer | test.cpp:100:17:100:22 | array to pointer conversion | IR only |
|
||||||
|
|||||||
@@ -52,3 +52,8 @@
|
|||||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:7:88:27 | (bool)... | |
|
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:7:88:27 | (bool)... | |
|
||||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | (const char *)... | |
|
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | (const char *)... | |
|
||||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | copy | |
|
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | copy | |
|
||||||
|
| test.cpp:100:12:100:15 | call to gets | test.cpp:98:8:98:14 | pointer | |
|
||||||
|
| test.cpp:100:12:100:15 | call to gets | test.cpp:100:12:100:15 | call to gets | |
|
||||||
|
| test.cpp:100:17:100:22 | buffer | test.cpp:93:18:93:18 | s | |
|
||||||
|
| test.cpp:100:17:100:22 | buffer | test.cpp:100:17:100:22 | array to pointer conversion | |
|
||||||
|
| test.cpp:100:17:100:22 | buffer | test.cpp:100:17:100:22 | buffer | |
|
||||||
|
|||||||
@@ -89,3 +89,13 @@ void mallocBuffer() {
|
|||||||
isAdmin = true;
|
isAdmin = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *gets(char *s);
|
||||||
|
|
||||||
|
void test_gets()
|
||||||
|
{
|
||||||
|
char buffer[1024];
|
||||||
|
char *pointer;
|
||||||
|
|
||||||
|
pointer = gets(buffer);
|
||||||
|
}
|
||||||
|
|||||||
@@ -8303,6 +8303,107 @@ ir.cpp:
|
|||||||
# 1219| Type = [IntType] int
|
# 1219| Type = [IntType] int
|
||||||
# 1219| ValueCategory = prvalue(load)
|
# 1219| ValueCategory = prvalue(load)
|
||||||
# 1220| 4: [ReturnStmt] return ...
|
# 1220| 4: [ReturnStmt] return ...
|
||||||
|
# 1222| [TopLevelFunction] int staticLocalInit(int)
|
||||||
|
# 1222| params:
|
||||||
|
# 1222| 0: [Parameter] x
|
||||||
|
# 1222| Type = [IntType] int
|
||||||
|
# 1222| body: [Block] { ... }
|
||||||
|
# 1223| 0: [DeclStmt] declaration
|
||||||
|
# 1223| 0: [VariableDeclarationEntry] definition of a
|
||||||
|
# 1223| Type = [IntType] int
|
||||||
|
# 1223| init: [Initializer] initializer for a
|
||||||
|
# 1223| expr: [Literal] 0
|
||||||
|
# 1223| Type = [IntType] int
|
||||||
|
# 1223| Value = [Literal] 0
|
||||||
|
# 1223| ValueCategory = prvalue
|
||||||
|
# 1224| 1: [DeclStmt] declaration
|
||||||
|
# 1224| 0: [VariableDeclarationEntry] definition of b
|
||||||
|
# 1224| Type = [IntType] int
|
||||||
|
# 1224| init: [Initializer] initializer for b
|
||||||
|
# 1224| expr: [CStyleCast] (int)...
|
||||||
|
# 1224| Conversion = [IntegralConversion] integral conversion
|
||||||
|
# 1224| Type = [IntType] int
|
||||||
|
# 1224| Value = [CStyleCast] 4
|
||||||
|
# 1224| ValueCategory = prvalue
|
||||||
|
# 1224| expr: [SizeofExprOperator] sizeof(<expr>)
|
||||||
|
# 1224| Type = [LongType] unsigned long
|
||||||
|
# 1224| Value = [SizeofExprOperator] 4
|
||||||
|
# 1224| ValueCategory = prvalue
|
||||||
|
# 1224| 0: [ParenthesisExpr] (...)
|
||||||
|
# 1224| Type = [IntType] int
|
||||||
|
# 1224| ValueCategory = lvalue
|
||||||
|
# 1224| expr: [VariableAccess] x
|
||||||
|
# 1224| Type = [IntType] int
|
||||||
|
# 1224| ValueCategory = lvalue
|
||||||
|
# 1225| 2: [DeclStmt] declaration
|
||||||
|
# 1225| 0: [VariableDeclarationEntry] definition of c
|
||||||
|
# 1225| Type = [IntType] int
|
||||||
|
# 1225| init: [Initializer] initializer for c
|
||||||
|
# 1225| expr: [VariableAccess] x
|
||||||
|
# 1225| Type = [IntType] int
|
||||||
|
# 1225| ValueCategory = prvalue(load)
|
||||||
|
# 1226| 3: [DeclStmt] declaration
|
||||||
|
# 1226| 0: [VariableDeclarationEntry] definition of d
|
||||||
|
# 1226| Type = [IntType] int
|
||||||
|
# 1228| 4: [ReturnStmt] return ...
|
||||||
|
# 1228| 0: [AddExpr] ... + ...
|
||||||
|
# 1228| Type = [IntType] int
|
||||||
|
# 1228| ValueCategory = prvalue
|
||||||
|
# 1228| 0: [AddExpr] ... + ...
|
||||||
|
# 1228| Type = [IntType] int
|
||||||
|
# 1228| ValueCategory = prvalue
|
||||||
|
# 1228| 0: [AddExpr] ... + ...
|
||||||
|
# 1228| Type = [IntType] int
|
||||||
|
# 1228| ValueCategory = prvalue
|
||||||
|
# 1228| 0: [VariableAccess] a
|
||||||
|
# 1228| Type = [IntType] int
|
||||||
|
# 1228| ValueCategory = prvalue(load)
|
||||||
|
# 1228| 1: [VariableAccess] b
|
||||||
|
# 1228| Type = [IntType] int
|
||||||
|
# 1228| ValueCategory = prvalue(load)
|
||||||
|
# 1228| 1: [VariableAccess] c
|
||||||
|
# 1228| Type = [IntType] int
|
||||||
|
# 1228| ValueCategory = prvalue(load)
|
||||||
|
# 1228| 1: [VariableAccess] d
|
||||||
|
# 1228| Type = [IntType] int
|
||||||
|
# 1228| ValueCategory = prvalue(load)
|
||||||
|
# 1231| [TopLevelFunction] void staticLocalWithConstructor(char const*)
|
||||||
|
# 1231| params:
|
||||||
|
# 1231| 0: [Parameter] dynamic
|
||||||
|
# 1231| Type = [PointerType] const char *
|
||||||
|
# 1231| body: [Block] { ... }
|
||||||
|
# 1232| 0: [DeclStmt] declaration
|
||||||
|
# 1232| 0: [VariableDeclarationEntry] definition of a
|
||||||
|
# 1232| Type = [Struct] String
|
||||||
|
#-----| init: [Initializer] initializer for a
|
||||||
|
#-----| expr: [ConstructorCall] call to String
|
||||||
|
#-----| Type = [VoidType] void
|
||||||
|
#-----| ValueCategory = prvalue
|
||||||
|
# 1233| 1: [DeclStmt] declaration
|
||||||
|
# 1233| 0: [VariableDeclarationEntry] definition of b
|
||||||
|
# 1233| Type = [Struct] String
|
||||||
|
# 1233| init: [Initializer] initializer for b
|
||||||
|
# 1233| expr: [ConstructorCall] call to String
|
||||||
|
# 1233| Type = [VoidType] void
|
||||||
|
# 1233| ValueCategory = prvalue
|
||||||
|
# 1233| 0: [ArrayToPointerConversion] array to pointer conversion
|
||||||
|
# 1233| Type = [PointerType] const char *
|
||||||
|
# 1233| ValueCategory = prvalue
|
||||||
|
# 1233| expr: static
|
||||||
|
# 1233| Type = [ArrayType] const char[7]
|
||||||
|
# 1233| Value = [StringLiteral] "static"
|
||||||
|
# 1233| ValueCategory = lvalue
|
||||||
|
# 1234| 2: [DeclStmt] declaration
|
||||||
|
# 1234| 0: [VariableDeclarationEntry] definition of c
|
||||||
|
# 1234| Type = [Struct] String
|
||||||
|
# 1234| init: [Initializer] initializer for c
|
||||||
|
# 1234| expr: [ConstructorCall] call to String
|
||||||
|
# 1234| Type = [VoidType] void
|
||||||
|
# 1234| ValueCategory = prvalue
|
||||||
|
# 1234| 0: [VariableAccess] dynamic
|
||||||
|
# 1234| Type = [PointerType] const char *
|
||||||
|
# 1234| ValueCategory = prvalue(load)
|
||||||
|
# 1235| 3: [ReturnStmt] return ...
|
||||||
perf-regression.cpp:
|
perf-regression.cpp:
|
||||||
# 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&)
|
# 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&)
|
||||||
# 4| params:
|
# 4| params:
|
||||||
|
|||||||
@@ -1219,4 +1219,19 @@ void switch2Case_default(int x) {
|
|||||||
int z = y;
|
int z = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int staticLocalInit(int x) {
|
||||||
|
static int a = 0; // Constant initialization
|
||||||
|
static int b = sizeof(x); // Constant initialization
|
||||||
|
static int c = x; // Dynamic initialization
|
||||||
|
static int d; // Zero initialization
|
||||||
|
|
||||||
|
return a + b + c + d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void staticLocalWithConstructor(const char* dynamic) {
|
||||||
|
static String a;
|
||||||
|
static String b("static");
|
||||||
|
static String c(dynamic);
|
||||||
|
}
|
||||||
|
|
||||||
// semmle-extractor-options: -std=c++17 --clang
|
// semmle-extractor-options: -std=c++17 --clang
|
||||||
|
|||||||
@@ -6232,6 +6232,125 @@ ir.cpp:
|
|||||||
# 1205| v1205_9(void) = AliasedUse : ~mu1205_4
|
# 1205| v1205_9(void) = AliasedUse : ~mu1205_4
|
||||||
# 1205| v1205_10(void) = ExitFunction :
|
# 1205| v1205_10(void) = ExitFunction :
|
||||||
|
|
||||||
|
# 1222| int staticLocalInit(int)
|
||||||
|
# 1222| Block 0
|
||||||
|
# 1222| v1222_1(void) = EnterFunction :
|
||||||
|
# 1222| mu1222_2(unknown) = AliasedDefinition :
|
||||||
|
# 1222| mu1222_3(unknown) = InitializeNonLocal :
|
||||||
|
# 1222| mu1222_4(unknown) = UnmodeledDefinition :
|
||||||
|
# 1222| r1222_5(glval<int>) = VariableAddress[x] :
|
||||||
|
# 1222| mu1222_6(int) = InitializeParameter[x] : &:r1222_5
|
||||||
|
# 1225| r1225_1(glval<bool>) = VariableAddress[c#init] :
|
||||||
|
# 1225| r1225_2(bool) = Load : &:r1225_1, ~mu1222_4
|
||||||
|
# 1225| v1225_3(void) = ConditionalBranch : r1225_2
|
||||||
|
#-----| False -> Block 2
|
||||||
|
#-----| True -> Block 1
|
||||||
|
|
||||||
|
# 1228| Block 1
|
||||||
|
# 1228| r1228_1(glval<int>) = VariableAddress[#return] :
|
||||||
|
# 1228| r1228_2(glval<int>) = VariableAddress[a] :
|
||||||
|
# 1228| r1228_3(int) = Load : &:r1228_2, ~mu1222_4
|
||||||
|
# 1228| r1228_4(glval<int>) = VariableAddress[b] :
|
||||||
|
# 1228| r1228_5(int) = Load : &:r1228_4, ~mu1222_4
|
||||||
|
# 1228| r1228_6(int) = Add : r1228_3, r1228_5
|
||||||
|
# 1228| r1228_7(glval<int>) = VariableAddress[c] :
|
||||||
|
# 1228| r1228_8(int) = Load : &:r1228_7, ~mu1222_4
|
||||||
|
# 1228| r1228_9(int) = Add : r1228_6, r1228_8
|
||||||
|
# 1228| r1228_10(glval<int>) = VariableAddress[d] :
|
||||||
|
# 1228| r1228_11(int) = Load : &:r1228_10, ~mu1222_4
|
||||||
|
# 1228| r1228_12(int) = Add : r1228_9, r1228_11
|
||||||
|
# 1228| mu1228_13(int) = Store : &:r1228_1, r1228_12
|
||||||
|
# 1222| r1222_7(glval<int>) = VariableAddress[#return] :
|
||||||
|
# 1222| v1222_8(void) = ReturnValue : &:r1222_7, ~mu1222_4
|
||||||
|
# 1222| v1222_9(void) = UnmodeledUse : mu*
|
||||||
|
# 1222| v1222_10(void) = AliasedUse : ~mu1222_4
|
||||||
|
# 1222| v1222_11(void) = ExitFunction :
|
||||||
|
|
||||||
|
# 1225| Block 2
|
||||||
|
# 1225| r1225_4(glval<int>) = VariableAddress[c] :
|
||||||
|
# 1225| r1225_5(glval<int>) = VariableAddress[x] :
|
||||||
|
# 1225| r1225_6(int) = Load : &:r1225_5, ~mu1222_4
|
||||||
|
# 1225| mu1225_7(int) = Store : &:r1225_4, r1225_6
|
||||||
|
# 1225| r1225_8(bool) = Constant[1] :
|
||||||
|
# 1225| mu1225_9(bool) = Store : &:r1225_1, r1225_8
|
||||||
|
#-----| Goto -> Block 1
|
||||||
|
|
||||||
|
# 1231| void staticLocalWithConstructor(char const*)
|
||||||
|
# 1231| Block 0
|
||||||
|
# 1231| v1231_1(void) = EnterFunction :
|
||||||
|
# 1231| mu1231_2(unknown) = AliasedDefinition :
|
||||||
|
# 1231| mu1231_3(unknown) = InitializeNonLocal :
|
||||||
|
# 1231| mu1231_4(unknown) = UnmodeledDefinition :
|
||||||
|
# 1231| r1231_5(glval<char *>) = VariableAddress[dynamic] :
|
||||||
|
# 1231| mu1231_6(char *) = InitializeParameter[dynamic] : &:r1231_5
|
||||||
|
# 1231| r1231_7(char *) = Load : &:r1231_5, ~mu1231_6
|
||||||
|
# 1231| mu1231_8(unknown) = InitializeIndirection[dynamic] : &:r1231_7
|
||||||
|
# 1232| r1232_1(glval<bool>) = VariableAddress[a#init] :
|
||||||
|
# 1232| r1232_2(bool) = Load : &:r1232_1, ~mu1231_4
|
||||||
|
# 1232| v1232_3(void) = ConditionalBranch : r1232_2
|
||||||
|
#-----| False -> Block 6
|
||||||
|
#-----| True -> Block 1
|
||||||
|
|
||||||
|
# 1233| Block 1
|
||||||
|
# 1233| r1233_1(glval<bool>) = VariableAddress[b#init] :
|
||||||
|
# 1233| r1233_2(bool) = Load : &:r1233_1, ~mu1231_4
|
||||||
|
# 1233| v1233_3(void) = ConditionalBranch : r1233_2
|
||||||
|
#-----| False -> Block 2
|
||||||
|
#-----| True -> Block 3
|
||||||
|
|
||||||
|
# 1233| Block 2
|
||||||
|
# 1233| r1233_4(glval<String>) = VariableAddress[b] :
|
||||||
|
# 1233| r1233_5(glval<unknown>) = FunctionAddress[String] :
|
||||||
|
# 1233| r1233_6(glval<char[7]>) = StringConstant["static"] :
|
||||||
|
# 1233| r1233_7(char *) = Convert : r1233_6
|
||||||
|
# 1233| v1233_8(void) = Call : func:r1233_5, this:r1233_4, 0:r1233_7
|
||||||
|
# 1233| mu1233_9(unknown) = ^CallSideEffect : ~mu1231_4
|
||||||
|
# 1233| mu1233_10(String) = ^IndirectMayWriteSideEffect[-1] : &:r1233_4
|
||||||
|
# 1233| v1233_11(void) = ^BufferReadSideEffect[0] : &:r1233_7, ~mu1231_4
|
||||||
|
# 1233| mu1233_12(unknown) = ^BufferMayWriteSideEffect[0] : &:r1233_7
|
||||||
|
# 1233| r1233_13(bool) = Constant[1] :
|
||||||
|
# 1233| mu1233_14(bool) = Store : &:r1233_1, r1233_13
|
||||||
|
#-----| Goto -> Block 3
|
||||||
|
|
||||||
|
# 1234| Block 3
|
||||||
|
# 1234| r1234_1(glval<bool>) = VariableAddress[c#init] :
|
||||||
|
# 1234| r1234_2(bool) = Load : &:r1234_1, ~mu1231_4
|
||||||
|
# 1234| v1234_3(void) = ConditionalBranch : r1234_2
|
||||||
|
#-----| False -> Block 4
|
||||||
|
#-----| True -> Block 5
|
||||||
|
|
||||||
|
# 1234| Block 4
|
||||||
|
# 1234| r1234_4(glval<String>) = VariableAddress[c] :
|
||||||
|
# 1234| r1234_5(glval<unknown>) = FunctionAddress[String] :
|
||||||
|
# 1234| r1234_6(glval<char *>) = VariableAddress[dynamic] :
|
||||||
|
# 1234| r1234_7(char *) = Load : &:r1234_6, ~mu1231_4
|
||||||
|
# 1234| v1234_8(void) = Call : func:r1234_5, this:r1234_4, 0:r1234_7
|
||||||
|
# 1234| mu1234_9(unknown) = ^CallSideEffect : ~mu1231_4
|
||||||
|
# 1234| mu1234_10(String) = ^IndirectMayWriteSideEffect[-1] : &:r1234_4
|
||||||
|
# 1234| v1234_11(void) = ^BufferReadSideEffect[0] : &:r1234_7, ~mu1231_4
|
||||||
|
# 1234| mu1234_12(unknown) = ^BufferMayWriteSideEffect[0] : &:r1234_7
|
||||||
|
# 1234| r1234_13(bool) = Constant[1] :
|
||||||
|
# 1234| mu1234_14(bool) = Store : &:r1234_1, r1234_13
|
||||||
|
#-----| Goto -> Block 5
|
||||||
|
|
||||||
|
# 1235| Block 5
|
||||||
|
# 1235| v1235_1(void) = NoOp :
|
||||||
|
# 1231| v1231_9(void) = ReturnIndirection : &:r1231_7, ~mu1231_4
|
||||||
|
# 1231| v1231_10(void) = ReturnVoid :
|
||||||
|
# 1231| v1231_11(void) = UnmodeledUse : mu*
|
||||||
|
# 1231| v1231_12(void) = AliasedUse : ~mu1231_4
|
||||||
|
# 1231| v1231_13(void) = ExitFunction :
|
||||||
|
|
||||||
|
# 1232| Block 6
|
||||||
|
# 1232| r1232_4(glval<String>) = VariableAddress[a] :
|
||||||
|
#-----| r0_1(glval<unknown>) = FunctionAddress[String] :
|
||||||
|
#-----| v0_2(void) = Call : func:r0_1, this:r1232_4
|
||||||
|
#-----| mu0_3(unknown) = ^CallSideEffect : ~mu1231_4
|
||||||
|
#-----| mu0_4(String) = ^IndirectMayWriteSideEffect[-1] : &:r1232_4
|
||||||
|
# 1232| r1232_5(bool) = Constant[1] :
|
||||||
|
# 1232| mu1232_6(bool) = Store : &:r1232_1, r1232_5
|
||||||
|
#-----| Goto -> Block 1
|
||||||
|
|
||||||
perf-regression.cpp:
|
perf-regression.cpp:
|
||||||
# 6| void Big::Big()
|
# 6| void Big::Big()
|
||||||
# 6| Block 0
|
# 6| Block 0
|
||||||
@@ -6305,8 +6424,6 @@ struct_init.cpp:
|
|||||||
# 20| mu20_2(unknown) = AliasedDefinition :
|
# 20| mu20_2(unknown) = AliasedDefinition :
|
||||||
# 20| mu20_3(unknown) = InitializeNonLocal :
|
# 20| mu20_3(unknown) = InitializeNonLocal :
|
||||||
# 20| mu20_4(unknown) = UnmodeledDefinition :
|
# 20| mu20_4(unknown) = UnmodeledDefinition :
|
||||||
# 21| r21_1(glval<Info[2]>) = VariableAddress[static_infos] :
|
|
||||||
# 21| mu21_2(Info[2]) = Uninitialized[static_infos] : &:r21_1
|
|
||||||
# 25| r25_1(glval<unknown>) = FunctionAddress[let_info_escape] :
|
# 25| r25_1(glval<unknown>) = FunctionAddress[let_info_escape] :
|
||||||
# 25| r25_2(glval<Info[2]>) = VariableAddress[static_infos] :
|
# 25| r25_2(glval<Info[2]>) = VariableAddress[static_infos] :
|
||||||
# 25| r25_3(Info *) = Convert : r25_2
|
# 25| r25_3(Info *) = Convert : r25_2
|
||||||
@@ -6370,27 +6487,13 @@ struct_init.cpp:
|
|||||||
# 36| mu36_6(char *) = InitializeParameter[name1] : &:r36_5
|
# 36| mu36_6(char *) = InitializeParameter[name1] : &:r36_5
|
||||||
# 36| r36_7(char *) = Load : &:r36_5, ~mu36_6
|
# 36| r36_7(char *) = Load : &:r36_5, ~mu36_6
|
||||||
# 36| mu36_8(unknown) = InitializeIndirection[name1] : &:r36_7
|
# 36| mu36_8(unknown) = InitializeIndirection[name1] : &:r36_7
|
||||||
# 37| r37_1(glval<Info[2]>) = VariableAddress[static_infos] :
|
# 37| r37_1(glval<bool>) = VariableAddress[static_infos#init] :
|
||||||
# 37| mu37_2(Info[2]) = Uninitialized[static_infos] : &:r37_1
|
# 37| r37_2(bool) = Load : &:r37_1, ~mu36_4
|
||||||
# 37| r37_3(int) = Constant[0] :
|
# 37| v37_3(void) = ConditionalBranch : r37_2
|
||||||
# 37| r37_4(glval<Info>) = PointerAdd[16] : r37_1, r37_3
|
#-----| False -> Block 2
|
||||||
# 38| r38_1(glval<char *>) = FieldAddress[name] : r37_4
|
#-----| True -> Block 1
|
||||||
# 38| r38_2(glval<char *>) = VariableAddress[name1] :
|
|
||||||
# 38| r38_3(char *) = Load : &:r38_2, ~mu36_4
|
# 41| Block 1
|
||||||
# 38| mu38_4(char *) = Store : &:r38_1, r38_3
|
|
||||||
# 38| r38_5(glval<..(*)(..)>) = FieldAddress[handler] : r37_4
|
|
||||||
# 38| r38_6(..(*)(..)) = FunctionAddress[handler1] :
|
|
||||||
# 38| mu38_7(..(*)(..)) = Store : &:r38_5, r38_6
|
|
||||||
# 37| r37_5(int) = Constant[1] :
|
|
||||||
# 37| r37_6(glval<Info>) = PointerAdd[16] : r37_1, r37_5
|
|
||||||
# 39| r39_1(glval<char *>) = FieldAddress[name] : r37_6
|
|
||||||
# 39| r39_2(glval<char[2]>) = StringConstant["2"] :
|
|
||||||
# 39| r39_3(char *) = Convert : r39_2
|
|
||||||
# 39| mu39_4(char *) = Store : &:r39_1, r39_3
|
|
||||||
# 39| r39_5(glval<..(*)(..)>) = FieldAddress[handler] : r37_6
|
|
||||||
# 39| r39_6(glval<..()(..)>) = FunctionAddress[handler2] :
|
|
||||||
# 39| r39_7(..(*)(..)) = CopyValue : r39_6
|
|
||||||
# 39| mu39_8(..(*)(..)) = Store : &:r39_5, r39_7
|
|
||||||
# 41| r41_1(glval<unknown>) = FunctionAddress[let_info_escape] :
|
# 41| r41_1(glval<unknown>) = FunctionAddress[let_info_escape] :
|
||||||
# 41| r41_2(glval<Info[2]>) = VariableAddress[static_infos] :
|
# 41| r41_2(glval<Info[2]>) = VariableAddress[static_infos] :
|
||||||
# 41| r41_3(Info *) = Convert : r41_2
|
# 41| r41_3(Info *) = Convert : r41_2
|
||||||
@@ -6404,3 +6507,28 @@ struct_init.cpp:
|
|||||||
# 36| v36_11(void) = UnmodeledUse : mu*
|
# 36| v36_11(void) = UnmodeledUse : mu*
|
||||||
# 36| v36_12(void) = AliasedUse : ~mu36_4
|
# 36| v36_12(void) = AliasedUse : ~mu36_4
|
||||||
# 36| v36_13(void) = ExitFunction :
|
# 36| v36_13(void) = ExitFunction :
|
||||||
|
|
||||||
|
# 37| Block 2
|
||||||
|
# 37| r37_4(glval<Info[2]>) = VariableAddress[static_infos] :
|
||||||
|
# 37| r37_5(int) = Constant[0] :
|
||||||
|
# 37| r37_6(glval<Info>) = PointerAdd[16] : r37_4, r37_5
|
||||||
|
# 38| r38_1(glval<char *>) = FieldAddress[name] : r37_6
|
||||||
|
# 38| r38_2(glval<char *>) = VariableAddress[name1] :
|
||||||
|
# 38| r38_3(char *) = Load : &:r38_2, ~mu36_4
|
||||||
|
# 38| mu38_4(char *) = Store : &:r38_1, r38_3
|
||||||
|
# 38| r38_5(glval<..(*)(..)>) = FieldAddress[handler] : r37_6
|
||||||
|
# 38| r38_6(..(*)(..)) = FunctionAddress[handler1] :
|
||||||
|
# 38| mu38_7(..(*)(..)) = Store : &:r38_5, r38_6
|
||||||
|
# 37| r37_7(int) = Constant[1] :
|
||||||
|
# 37| r37_8(glval<Info>) = PointerAdd[16] : r37_4, r37_7
|
||||||
|
# 39| r39_1(glval<char *>) = FieldAddress[name] : r37_8
|
||||||
|
# 39| r39_2(glval<char[2]>) = StringConstant["2"] :
|
||||||
|
# 39| r39_3(char *) = Convert : r39_2
|
||||||
|
# 39| mu39_4(char *) = Store : &:r39_1, r39_3
|
||||||
|
# 39| r39_5(glval<..(*)(..)>) = FieldAddress[handler] : r37_8
|
||||||
|
# 39| r39_6(glval<..()(..)>) = FunctionAddress[handler2] :
|
||||||
|
# 39| r39_7(..(*)(..)) = CopyValue : r39_6
|
||||||
|
# 39| mu39_8(..(*)(..)) = Store : &:r39_5, r39_7
|
||||||
|
# 37| r37_9(bool) = Constant[1] :
|
||||||
|
# 37| mu37_10(bool) = Store : &:r37_1, r37_9
|
||||||
|
#-----| Goto -> Block 1
|
||||||
|
|||||||
@@ -137,7 +137,6 @@
|
|||||||
| test.c:109:14:109:16 | Constant: 44 | positive strictlyPositive |
|
| test.c:109:14:109:16 | Constant: 44 | positive strictlyPositive |
|
||||||
| test.c:110:14:110:14 | Constant: 1 | positive strictlyPositive |
|
| test.c:110:14:110:14 | Constant: 1 | positive strictlyPositive |
|
||||||
| test.c:110:14:110:14 | Store: 1 | positive strictlyPositive |
|
| test.c:110:14:110:14 | Store: 1 | positive strictlyPositive |
|
||||||
| test.c:118:20:118:20 | Uninitialized: definition of n | positive |
|
|
||||||
| test.c:119:10:119:10 | Load: n | positive |
|
| test.c:119:10:119:10 | Load: n | positive |
|
||||||
| test.c:119:10:119:12 | Add: ... ++ | positive strictlyPositive |
|
| test.c:119:10:119:12 | Add: ... ++ | positive strictlyPositive |
|
||||||
| test.c:119:10:119:12 | Constant: ... ++ | positive strictlyPositive |
|
| test.c:119:10:119:12 | Constant: ... ++ | positive strictlyPositive |
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ instructionWithoutSuccessor
|
|||||||
| vla.c:11:6:11:16 | UnmodeledDefinition: vla_typedef |
|
| vla.c:11:6:11:16 | UnmodeledDefinition: vla_typedef |
|
||||||
ambiguousSuccessors
|
ambiguousSuccessors
|
||||||
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
||||||
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:10:16:10:16 | VariableAddress: definition of m |
|
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... |
|
||||||
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
||||||
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
||||||
| array_delete.cpp:5:6:5:6 | UnmodeledDefinition: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... |
|
| array_delete.cpp:5:6:5:6 | UnmodeledDefinition: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... |
|
||||||
@@ -358,7 +358,7 @@ ambiguousSuccessors
|
|||||||
| newexpr.cpp:6:6:6:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c |
|
| newexpr.cpp:6:6:6:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c |
|
||||||
| newexpr.cpp:6:6:6:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i |
|
| newexpr.cpp:6:6:6:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i |
|
||||||
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
||||||
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:10:16:10:16 | VariableAddress: definition of m |
|
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... |
|
||||||
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
||||||
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
||||||
| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 20 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a |
|
| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 20 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a |
|
||||||
@@ -408,7 +408,7 @@ ambiguousSuccessors
|
|||||||
| nonmembercallexpr.c:1:6:1:6 | UnmodeledDefinition: g | Goto | 2 | nonmembercallexpr.c:1:12:1:12 | NoOp: return ... |
|
| nonmembercallexpr.c:1:6:1:6 | UnmodeledDefinition: g | Goto | 2 | nonmembercallexpr.c:1:12:1:12 | NoOp: return ... |
|
||||||
| nonmembercallexpr.c:1:6:1:6 | UnmodeledDefinition: g | Goto | 2 | revsubscriptexpr.c:2:9:2:9 | VariableAddress: definition of x |
|
| nonmembercallexpr.c:1:6:1:6 | UnmodeledDefinition: g | Goto | 2 | revsubscriptexpr.c:2:9:2:9 | VariableAddress: definition of x |
|
||||||
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
||||||
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:10:16:10:16 | VariableAddress: definition of m |
|
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... |
|
||||||
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
||||||
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
||||||
| pmcallexpr.cpp:6:6:6:6 | UnmodeledDefinition: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... |
|
| pmcallexpr.cpp:6:6:6:6 | UnmodeledDefinition: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... |
|
||||||
@@ -460,7 +460,7 @@ ambiguousSuccessors
|
|||||||
| staticmembercallexpr_args.cpp:7:6:7:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c |
|
| staticmembercallexpr_args.cpp:7:6:7:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c |
|
||||||
| staticmembercallexpr_args.cpp:7:6:7:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i |
|
| staticmembercallexpr_args.cpp:7:6:7:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i |
|
||||||
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
||||||
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:10:16:10:16 | VariableAddress: definition of m |
|
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... |
|
||||||
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
||||||
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
||||||
| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 20 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a |
|
| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 20 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a |
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ instructionWithoutSuccessor
|
|||||||
| vla.c:14:92:14:94 | Store: (char *)... |
|
| vla.c:14:92:14:94 | Store: (char *)... |
|
||||||
ambiguousSuccessors
|
ambiguousSuccessors
|
||||||
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
||||||
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:10:16:10:16 | VariableAddress: definition of m |
|
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... |
|
||||||
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
||||||
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
||||||
| array_delete.cpp:5:6:5:6 | UnmodeledDefinition: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... |
|
| array_delete.cpp:5:6:5:6 | UnmodeledDefinition: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... |
|
||||||
@@ -413,7 +413,7 @@ ambiguousSuccessors
|
|||||||
| newexpr.cpp:6:6:6:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c |
|
| newexpr.cpp:6:6:6:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c |
|
||||||
| newexpr.cpp:6:6:6:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i |
|
| newexpr.cpp:6:6:6:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i |
|
||||||
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
||||||
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:10:16:10:16 | VariableAddress: definition of m |
|
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... |
|
||||||
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
||||||
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
||||||
| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 20 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a |
|
| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 20 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a |
|
||||||
@@ -463,7 +463,7 @@ ambiguousSuccessors
|
|||||||
| nonmembercallexpr.c:1:6:1:6 | UnmodeledDefinition: g | Goto | 2 | nonmembercallexpr.c:1:12:1:12 | NoOp: return ... |
|
| nonmembercallexpr.c:1:6:1:6 | UnmodeledDefinition: g | Goto | 2 | nonmembercallexpr.c:1:12:1:12 | NoOp: return ... |
|
||||||
| nonmembercallexpr.c:1:6:1:6 | UnmodeledDefinition: g | Goto | 2 | revsubscriptexpr.c:2:9:2:9 | VariableAddress: definition of x |
|
| nonmembercallexpr.c:1:6:1:6 | UnmodeledDefinition: g | Goto | 2 | revsubscriptexpr.c:2:9:2:9 | VariableAddress: definition of x |
|
||||||
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
||||||
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:10:16:10:16 | VariableAddress: definition of m |
|
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... |
|
||||||
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
||||||
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
||||||
| pmcallexpr.cpp:6:6:6:6 | UnmodeledDefinition: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... |
|
| pmcallexpr.cpp:6:6:6:6 | UnmodeledDefinition: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... |
|
||||||
@@ -515,7 +515,7 @@ ambiguousSuccessors
|
|||||||
| staticmembercallexpr_args.cpp:7:6:7:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c |
|
| staticmembercallexpr_args.cpp:7:6:7:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c |
|
||||||
| staticmembercallexpr_args.cpp:7:6:7:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i |
|
| staticmembercallexpr_args.cpp:7:6:7:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i |
|
||||||
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
||||||
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:10:16:10:16 | VariableAddress: definition of m |
|
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... |
|
||||||
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
||||||
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
||||||
| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 20 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a |
|
| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 20 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a |
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ instructionWithoutSuccessor
|
|||||||
| vla.c:11:6:11:16 | UnmodeledDefinition: vla_typedef |
|
| vla.c:11:6:11:16 | UnmodeledDefinition: vla_typedef |
|
||||||
ambiguousSuccessors
|
ambiguousSuccessors
|
||||||
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
||||||
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:10:16:10:16 | VariableAddress: definition of m |
|
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... |
|
||||||
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
||||||
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
| allocators.cpp:14:5:14:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
||||||
| array_delete.cpp:5:6:5:6 | UnmodeledDefinition: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... |
|
| array_delete.cpp:5:6:5:6 | UnmodeledDefinition: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... |
|
||||||
@@ -367,7 +367,7 @@ ambiguousSuccessors
|
|||||||
| newexpr.cpp:6:6:6:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c |
|
| newexpr.cpp:6:6:6:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c |
|
||||||
| newexpr.cpp:6:6:6:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i |
|
| newexpr.cpp:6:6:6:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i |
|
||||||
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
||||||
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:10:16:10:16 | VariableAddress: definition of m |
|
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... |
|
||||||
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
||||||
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
| no_dynamic_init.cpp:9:5:9:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
||||||
| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 20 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a |
|
| nodefaultswitchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 20 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a |
|
||||||
@@ -417,7 +417,7 @@ ambiguousSuccessors
|
|||||||
| nonmembercallexpr.c:1:6:1:6 | UnmodeledDefinition: g | Goto | 2 | nonmembercallexpr.c:1:12:1:12 | NoOp: return ... |
|
| nonmembercallexpr.c:1:6:1:6 | UnmodeledDefinition: g | Goto | 2 | nonmembercallexpr.c:1:12:1:12 | NoOp: return ... |
|
||||||
| nonmembercallexpr.c:1:6:1:6 | UnmodeledDefinition: g | Goto | 2 | revsubscriptexpr.c:2:9:2:9 | VariableAddress: definition of x |
|
| nonmembercallexpr.c:1:6:1:6 | UnmodeledDefinition: g | Goto | 2 | revsubscriptexpr.c:2:9:2:9 | VariableAddress: definition of x |
|
||||||
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
||||||
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:10:16:10:16 | VariableAddress: definition of m |
|
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... |
|
||||||
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
||||||
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
| parameterinitializer.cpp:18:5:18:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
||||||
| pmcallexpr.cpp:6:6:6:6 | UnmodeledDefinition: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... |
|
| pmcallexpr.cpp:6:6:6:6 | UnmodeledDefinition: f | Goto | 14 | array_delete.cpp:6:12:6:24 | Constant: (Foo *)... |
|
||||||
@@ -469,7 +469,7 @@ ambiguousSuccessors
|
|||||||
| staticmembercallexpr_args.cpp:7:6:7:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c |
|
| staticmembercallexpr_args.cpp:7:6:7:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr.cpp:7:4:7:4 | VariableAddress: definition of c |
|
||||||
| staticmembercallexpr_args.cpp:7:6:7:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i |
|
| staticmembercallexpr_args.cpp:7:6:7:6 | UnmodeledDefinition: f | Goto | 14 | staticmembercallexpr_args.cpp:8:6:8:6 | VariableAddress: definition of i |
|
||||||
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | allocators.cpp:16:8:16:10 | VariableAddress: definition of foo |
|
||||||
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:10:16:10:16 | VariableAddress: definition of m |
|
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | no_dynamic_init.cpp:11:3:11:11 | VariableAddress: return ... |
|
||||||
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | parameterinitializer.cpp:19:5:19:5 | FunctionAddress: call to f |
|
||||||
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
| stream_it.cpp:16:5:16:8 | UnmodeledDefinition: main | Goto | 4 | stream_it.cpp:18:15:18:16 | VariableAddress: definition of xs |
|
||||||
| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 20 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a |
|
| switchstmt.c:1:12:1:12 | InitializeParameter: i | Goto | 20 | aggregateinitializer.c:2:6:2:6 | VariableAddress: definition of a |
|
||||||
|
|||||||
@@ -59,8 +59,7 @@
|
|||||||
| variables.cpp:37:6:37:8 | ap3 | file://:0:0:0:0 | int * | StaticStorageDurationVariable | | |
|
| variables.cpp:37:6:37:8 | ap3 | file://:0:0:0:0 | int * | StaticStorageDurationVariable | | |
|
||||||
| variables.cpp:41:7:41:11 | local | file://:0:0:0:0 | char[] | LocalVariable | | |
|
| variables.cpp:41:7:41:11 | local | file://:0:0:0:0 | char[] | LocalVariable | | |
|
||||||
| variables.cpp:41:7:41:11 | local | file://:0:0:0:0 | char[] | SemanticStackVariable | | |
|
| variables.cpp:41:7:41:11 | local | file://:0:0:0:0 | char[] | SemanticStackVariable | | |
|
||||||
| variables.cpp:43:14:43:18 | local | file://:0:0:0:0 | int | LocalVariable | | static |
|
| variables.cpp:43:14:43:18 | local | file://:0:0:0:0 | int | StaticLocalVariable | | static |
|
||||||
| variables.cpp:43:14:43:18 | local | file://:0:0:0:0 | int | StaticStorageDurationVariable | | static |
|
|
||||||
| variables.cpp:48:9:48:12 | name | file://:0:0:0:0 | char * | Field | | |
|
| variables.cpp:48:9:48:12 | name | file://:0:0:0:0 | char * | Field | | |
|
||||||
| variables.cpp:49:12:49:17 | number | file://:0:0:0:0 | long | Field | | |
|
| variables.cpp:49:12:49:17 | number | file://:0:0:0:0 | long | Field | | |
|
||||||
| variables.cpp:50:9:50:14 | street | file://:0:0:0:0 | char * | Field | | |
|
| variables.cpp:50:9:50:14 | street | file://:0:0:0:0 | char * | Field | | |
|
||||||
|
|||||||
@@ -112,4 +112,29 @@ void myFunction() {
|
|||||||
assert(CHECK_RANGE(ui, 0, 10)); // reasonable use
|
assert(CHECK_RANGE(ui, 0, 10)); // reasonable use
|
||||||
assert(UI >= ZERO); // violation (not detected)
|
assert(UI >= ZERO); // violation (not detected)
|
||||||
assert(ui GE 0); // violation
|
assert(ui GE 0); // violation
|
||||||
|
|
||||||
|
if ((unsigned char)si >= 0) { // violation
|
||||||
|
}
|
||||||
|
if ((unsigned char)(signed int)si >= 0) { // violation
|
||||||
|
}
|
||||||
|
if ((signed int)(unsigned char)si >= 0) { // violation
|
||||||
|
}
|
||||||
|
if ((unsigned char)(signed char)si >= 0) { // violation
|
||||||
|
}
|
||||||
|
if ((signed char)(unsigned char)si >= 0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((signed int)(unsigned char)(signed int)si >= 0) { // violation
|
||||||
|
}
|
||||||
|
if ((signed char)(unsigned char)(signed int)si >= 0) {
|
||||||
|
}
|
||||||
|
if ((signed int)(unsigned char)(signed char)si >= 0) { // violation
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ui <= 0) {
|
||||||
|
}
|
||||||
|
if (0 <= ui) { // violation
|
||||||
|
}
|
||||||
|
if (0 < ui) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,4 +112,29 @@ void myFunction() {
|
|||||||
assert(CHECK_RANGE(ui, 0, 10)); // reasonable use
|
assert(CHECK_RANGE(ui, 0, 10)); // reasonable use
|
||||||
assert(UI >= ZERO); // violation (not detected)
|
assert(UI >= ZERO); // violation (not detected)
|
||||||
assert(ui GE 0); // violation
|
assert(ui GE 0); // violation
|
||||||
|
|
||||||
|
if ((unsigned char)si >= 0) { // violation
|
||||||
|
}
|
||||||
|
if ((unsigned char)(signed int)si >= 0) { // violation
|
||||||
|
}
|
||||||
|
if ((signed int)(unsigned char)si >= 0) { // violation
|
||||||
|
}
|
||||||
|
if ((unsigned char)(signed char)si >= 0) { // violation
|
||||||
|
}
|
||||||
|
if ((signed char)(unsigned char)si >= 0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((signed int)(unsigned char)(signed int)si >= 0) { // violation
|
||||||
|
}
|
||||||
|
if ((signed char)(unsigned char)(signed int)si >= 0) {
|
||||||
|
}
|
||||||
|
if ((signed int)(unsigned char)(signed char)si >= 0) { // violation
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ui <= 0) {
|
||||||
|
}
|
||||||
|
if (0 <= ui) { // violation
|
||||||
|
}
|
||||||
|
if (0 < ui) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,13 @@
|
|||||||
| UnsignedGEZero.c:101:9:101:15 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
| UnsignedGEZero.c:101:9:101:15 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
||||||
| UnsignedGEZero.c:111:9:111:15 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
| UnsignedGEZero.c:111:9:111:15 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
||||||
| UnsignedGEZero.c:114:9:114:15 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
| UnsignedGEZero.c:114:9:114:15 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
||||||
|
| UnsignedGEZero.c:116:6:116:27 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
||||||
|
| UnsignedGEZero.c:118:6:118:39 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
||||||
|
| UnsignedGEZero.c:120:6:120:39 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
||||||
|
| UnsignedGEZero.c:122:6:122:40 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
||||||
|
| UnsignedGEZero.c:127:6:127:51 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
||||||
|
| UnsignedGEZero.c:131:6:131:52 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
||||||
|
| UnsignedGEZero.c:136:6:136:12 | ... <= ... | Pointless comparison of unsigned value to zero. |
|
||||||
| UnsignedGEZero.cpp:40:6:40:12 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
| UnsignedGEZero.cpp:40:6:40:12 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
||||||
| UnsignedGEZero.cpp:48:6:48:15 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
| UnsignedGEZero.cpp:48:6:48:15 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
||||||
| UnsignedGEZero.cpp:54:6:54:12 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
| UnsignedGEZero.cpp:54:6:54:12 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
||||||
@@ -29,3 +36,10 @@
|
|||||||
| UnsignedGEZero.cpp:101:9:101:15 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
| UnsignedGEZero.cpp:101:9:101:15 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
||||||
| UnsignedGEZero.cpp:111:9:111:15 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
| UnsignedGEZero.cpp:111:9:111:15 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
||||||
| UnsignedGEZero.cpp:114:9:114:15 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
| UnsignedGEZero.cpp:114:9:114:15 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
||||||
|
| UnsignedGEZero.cpp:116:6:116:27 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
||||||
|
| UnsignedGEZero.cpp:118:6:118:39 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
||||||
|
| UnsignedGEZero.cpp:120:6:120:39 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
||||||
|
| UnsignedGEZero.cpp:122:6:122:40 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
||||||
|
| UnsignedGEZero.cpp:127:6:127:51 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
||||||
|
| UnsignedGEZero.cpp:131:6:131:52 | ... >= ... | Pointless comparison of unsigned value to zero. |
|
||||||
|
| UnsignedGEZero.cpp:136:6:136:12 | ... <= ... | Pointless comparison of unsigned value to zero. |
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
| tests.c:28:3:28:9 | call to sprintf | This 'call to sprintf' with input from $@ may overflow the destination. | tests.c:28:22:28:25 | argv | argv |
|
| tests.c:28:3:28:9 | call to sprintf | This 'call to sprintf' with input from $@ may overflow the destination. | tests.c:28:22:28:25 | argv | argv |
|
||||||
| tests.c:29:3:29:9 | call to sprintf | This 'call to sprintf' with input from $@ may overflow the destination. | tests.c:29:28:29:31 | argv | argv |
|
| tests.c:29:3:29:9 | call to sprintf | This 'call to sprintf' with input from $@ may overflow the destination. | tests.c:29:28:29:31 | argv | argv |
|
||||||
| tests.c:31:15:31:23 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:28:22:28:25 | argv | argv |
|
|
||||||
| tests.c:31:15:31:23 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:29:28:29:31 | argv | argv |
|
|
||||||
| tests.c:31:15:31:23 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:31:15:31:23 | buffer100 | buffer100 |
|
| tests.c:31:15:31:23 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:31:15:31:23 | buffer100 | buffer100 |
|
||||||
| tests.c:33:21:33:29 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:28:22:28:25 | argv | argv |
|
|
||||||
| tests.c:33:21:33:29 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:29:28:29:31 | argv | argv |
|
|
||||||
| tests.c:33:21:33:29 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:31:15:31:23 | buffer100 | buffer100 |
|
|
||||||
| tests.c:33:21:33:29 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:33:21:33:29 | buffer100 | buffer100 |
|
| tests.c:33:21:33:29 | buffer100 | This 'scanf string argument' with input from $@ may overflow the destination. | tests.c:33:21:33:29 | buffer100 | buffer100 |
|
||||||
| tests.c:34:25:34:33 | buffer100 | This 'sscanf string argument' with input from $@ may overflow the destination. | tests.c:34:10:34:13 | argv | argv |
|
| tests.c:34:25:34:33 | buffer100 | This 'sscanf string argument' with input from $@ may overflow the destination. | tests.c:34:10:34:13 | argv | argv |
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
| funcsLocal.c:17:9:17:10 | i1 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:16:8:16:9 | i1 | fread |
|
| funcsLocal.c:17:9:17:10 | i1 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:16:8:16:9 | i1 | fread |
|
||||||
| funcsLocal.c:27:9:27:10 | i3 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:26:8:26:9 | i3 | fgets |
|
| funcsLocal.c:27:9:27:10 | i3 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:26:8:26:9 | i3 | fgets |
|
||||||
| funcsLocal.c:32:9:32:10 | i4 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:31:13:31:17 | call to fgets | fgets |
|
| funcsLocal.c:32:9:32:10 | i4 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:31:13:31:17 | call to fgets | fgets |
|
||||||
|
| funcsLocal.c:32:9:32:10 | i4 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:31:19:31:21 | i41 | fgets |
|
||||||
| funcsLocal.c:37:9:37:10 | i5 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:36:7:36:8 | i5 | gets |
|
| funcsLocal.c:37:9:37:10 | i5 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:36:7:36:8 | i5 | gets |
|
||||||
| funcsLocal.c:42:9:42:10 | i6 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:41:13:41:16 | call to gets | gets |
|
| funcsLocal.c:42:9:42:10 | i6 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:41:13:41:16 | call to gets | gets |
|
||||||
|
| funcsLocal.c:42:9:42:10 | i6 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | funcsLocal.c:41:18:41:20 | i61 | gets |
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
| test.c:21:17:21:17 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:18:13:18:16 | call to rand | Uncontrolled value |
|
| test.c:21:17:21:17 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:18:13:18:16 | call to rand | Uncontrolled value |
|
||||||
| test.c:35:5:35:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:34:13:34:18 | call to rand | Uncontrolled value |
|
| test.c:35:5:35:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:34:13:34:18 | call to rand | Uncontrolled value |
|
||||||
| test.c:40:5:40:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:39:13:39:21 | ... % ... | Uncontrolled value |
|
| test.c:40:5:40:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:39:13:39:21 | ... % ... | Uncontrolled value |
|
||||||
| test.c:40:5:40:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:39:13:39:22 | call to rand | Uncontrolled value |
|
|
||||||
| test.c:45:5:45:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:44:13:44:16 | call to rand | Uncontrolled value |
|
| test.c:45:5:45:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:44:13:44:16 | call to rand | Uncontrolled value |
|
||||||
| test.c:56:5:56:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:54:13:54:16 | call to rand | Uncontrolled value |
|
| test.c:56:5:56:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:54:13:54:16 | call to rand | Uncontrolled value |
|
||||||
| test.c:67:5:67:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:66:13:66:16 | call to rand | Uncontrolled value |
|
| test.c:67:5:67:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:66:13:66:16 | call to rand | Uncontrolled value |
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ namespace Semmle.Autobuild
|
|||||||
|
|
||||||
var build = GetBuildScript(builder, dotNetPath, environment, compatibleClr, projectOrSolution.FullPath);
|
var build = GetBuildScript(builder, dotNetPath, environment, compatibleClr, projectOrSolution.FullPath);
|
||||||
|
|
||||||
ret &= clean & BuildScript.Try(restore) & build;
|
ret &= BuildScript.Try(clean) & BuildScript.Try(restore) & build;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ newtype TIRVariable =
|
|||||||
) {
|
) {
|
||||||
Construction::hasTempVariable(func, ast, tag, type)
|
Construction::hasTempVariable(func, ast, tag, type)
|
||||||
} or
|
} or
|
||||||
|
TIRDynamicInitializationFlag(
|
||||||
|
Language::Function func, Language::Variable var, Language::LanguageType type
|
||||||
|
) {
|
||||||
|
Construction::hasDynamicInitializationFlag(func, var, type)
|
||||||
|
} or
|
||||||
TIRStringLiteral(
|
TIRStringLiteral(
|
||||||
Language::Function func, Language::AST ast, Language::LanguageType type,
|
Language::Function func, Language::AST ast, Language::LanguageType type,
|
||||||
Language::StringLiteral literal
|
Language::StringLiteral literal
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ class IRVariable extends TIRVariable {
|
|||||||
IRVariable() {
|
IRVariable() {
|
||||||
this = TIRUserVariable(_, _, func) or
|
this = TIRUserVariable(_, _, func) or
|
||||||
this = TIRTempVariable(func, _, _, _) or
|
this = TIRTempVariable(func, _, _, _) or
|
||||||
this = TIRStringLiteral(func, _, _, _)
|
this = TIRStringLiteral(func, _, _, _) or
|
||||||
|
this = TIRDynamicInitializationFlag(func, _, _)
|
||||||
}
|
}
|
||||||
|
|
||||||
string toString() { none() }
|
string toString() { none() }
|
||||||
@@ -149,7 +150,8 @@ class IRGeneratedVariable extends IRVariable {
|
|||||||
|
|
||||||
IRGeneratedVariable() {
|
IRGeneratedVariable() {
|
||||||
this = TIRTempVariable(func, ast, _, type) or
|
this = TIRTempVariable(func, ast, _, type) or
|
||||||
this = TIRStringLiteral(func, ast, type, _)
|
this = TIRStringLiteral(func, ast, type, _) or
|
||||||
|
this = TIRDynamicInitializationFlag(func, ast, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
final override Language::LanguageType getLanguageType() { result = type }
|
final override Language::LanguageType getLanguageType() { result = type }
|
||||||
@@ -208,7 +210,7 @@ class IRReturnVariable extends IRTempVariable {
|
|||||||
class IRThrowVariable extends IRTempVariable {
|
class IRThrowVariable extends IRTempVariable {
|
||||||
IRThrowVariable() { tag = ThrowTempVar() }
|
IRThrowVariable() { tag = ThrowTempVar() }
|
||||||
|
|
||||||
override string getBaseString() { result = "#throw" }
|
final override string getBaseString() { result = "#throw" }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -236,7 +238,30 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
|||||||
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
|
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
|
||||||
}
|
}
|
||||||
|
|
||||||
override string getBaseString() { result = "#string" }
|
final override string getBaseString() { result = "#string" }
|
||||||
|
|
||||||
final Language::StringLiteral getLiteral() { result = literal }
|
final Language::StringLiteral getLiteral() { result = literal }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A variable generated to track whether a specific non-stack variable has been initialized. This is
|
||||||
|
* used to model the runtime initialization of static local variables in C++, as well as static
|
||||||
|
* fields in C#.
|
||||||
|
*/
|
||||||
|
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
|
||||||
|
Language::Variable var;
|
||||||
|
|
||||||
|
IRDynamicInitializationFlag() {
|
||||||
|
this = TIRDynamicInitializationFlag(func, var, type) and ast = var
|
||||||
|
}
|
||||||
|
|
||||||
|
final override string toString() { result = var.toString() + "#init" }
|
||||||
|
|
||||||
|
final Language::Variable getVariable() { result = var }
|
||||||
|
|
||||||
|
final override string getUniqueId() {
|
||||||
|
result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
|
||||||
|
}
|
||||||
|
|||||||
@@ -61,6 +61,11 @@ private module Cached {
|
|||||||
getTypeForPRValue(literal.getType()) = type
|
getTypeForPRValue(literal.getType()) = type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cached
|
||||||
|
predicate hasDynamicInitializationFlag(Callable callable, Language::Variable var, CSharpType type) {
|
||||||
|
none()
|
||||||
|
}
|
||||||
|
|
||||||
cached
|
cached
|
||||||
predicate hasModeledMemoryResult(Instruction instruction) { none() }
|
predicate hasModeledMemoryResult(Instruction instruction) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ class IRVariable extends TIRVariable {
|
|||||||
IRVariable() {
|
IRVariable() {
|
||||||
this = TIRUserVariable(_, _, func) or
|
this = TIRUserVariable(_, _, func) or
|
||||||
this = TIRTempVariable(func, _, _, _) or
|
this = TIRTempVariable(func, _, _, _) or
|
||||||
this = TIRStringLiteral(func, _, _, _)
|
this = TIRStringLiteral(func, _, _, _) or
|
||||||
|
this = TIRDynamicInitializationFlag(func, _, _)
|
||||||
}
|
}
|
||||||
|
|
||||||
string toString() { none() }
|
string toString() { none() }
|
||||||
@@ -149,7 +150,8 @@ class IRGeneratedVariable extends IRVariable {
|
|||||||
|
|
||||||
IRGeneratedVariable() {
|
IRGeneratedVariable() {
|
||||||
this = TIRTempVariable(func, ast, _, type) or
|
this = TIRTempVariable(func, ast, _, type) or
|
||||||
this = TIRStringLiteral(func, ast, type, _)
|
this = TIRStringLiteral(func, ast, type, _) or
|
||||||
|
this = TIRDynamicInitializationFlag(func, ast, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
final override Language::LanguageType getLanguageType() { result = type }
|
final override Language::LanguageType getLanguageType() { result = type }
|
||||||
@@ -208,7 +210,7 @@ class IRReturnVariable extends IRTempVariable {
|
|||||||
class IRThrowVariable extends IRTempVariable {
|
class IRThrowVariable extends IRTempVariable {
|
||||||
IRThrowVariable() { tag = ThrowTempVar() }
|
IRThrowVariable() { tag = ThrowTempVar() }
|
||||||
|
|
||||||
override string getBaseString() { result = "#throw" }
|
final override string getBaseString() { result = "#throw" }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -236,7 +238,30 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
|||||||
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
|
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
|
||||||
}
|
}
|
||||||
|
|
||||||
override string getBaseString() { result = "#string" }
|
final override string getBaseString() { result = "#string" }
|
||||||
|
|
||||||
final Language::StringLiteral getLiteral() { result = literal }
|
final Language::StringLiteral getLiteral() { result = literal }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A variable generated to track whether a specific non-stack variable has been initialized. This is
|
||||||
|
* used to model the runtime initialization of static local variables in C++, as well as static
|
||||||
|
* fields in C#.
|
||||||
|
*/
|
||||||
|
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
|
||||||
|
Language::Variable var;
|
||||||
|
|
||||||
|
IRDynamicInitializationFlag() {
|
||||||
|
this = TIRDynamicInitializationFlag(func, var, type) and ast = var
|
||||||
|
}
|
||||||
|
|
||||||
|
final override string toString() { result = var.toString() + "#init" }
|
||||||
|
|
||||||
|
final Language::Variable getVariable() { result = var }
|
||||||
|
|
||||||
|
final override string getUniqueId() {
|
||||||
|
result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,41 +1,7 @@
|
|||||||
# Experimental CodeQL queries and libraries
|
# Experimental CodeQL queries and libraries
|
||||||
|
|
||||||
In addition to our standard CodeQL queries and libraries, this repository may also contain queries and libraries of a more experimental nature. Experimental queries and libraries can be improved incrementally and may eventually reach a sufficient maturity to be included in our standard libraries and queries.
|
In addition to [our supported queries and libraries](supported-queries.md), this repository also contains queries and libraries of a more experimental nature. Experimental queries and libraries can be improved incrementally and may eventually reach a sufficient maturity to be included in our supported queries and libraries.
|
||||||
|
|
||||||
Experimental queries and libraries may not be actively maintained as the standard libraries evolve. They may also be changed in backwards-incompatible ways or may be removed entirely in the future without deprecation warnings.
|
Experimental queries and libraries may not be actively maintained as the [supported](supported-queries.md) libraries evolve. They may also be changed in backwards-incompatible ways or may be removed entirely in the future without deprecation warnings.
|
||||||
|
|
||||||
## Requirements
|
See [CONTRIBUTING.md](../CONTRIBUTING.md) for guidelines on submitting a new experimental query.
|
||||||
|
|
||||||
1. **Directory structure**
|
|
||||||
|
|
||||||
- Experimental queries and libraries are stored in the `experimental` subdirectory within each language-specific directory in the [CodeQL repository](https://github.com/Semmle/ql). For example, experimental Java queries and libraries are stored in `ql/java/ql/src/experimental` and any corresponding tests in `ql/java/ql/test/experimental`.
|
|
||||||
- The structure of an `experimental` subdirectory mirrors the structure of standard queries and libraries (or tests) in the parent directory.
|
|
||||||
|
|
||||||
2. **Query metadata**
|
|
||||||
|
|
||||||
- The query `@id` must not clash with any other queries in the repository.
|
|
||||||
- The query must have a `@name` and `@description` to explain its purpose.
|
|
||||||
- The query must have a `@kind` and `@problem.severity` as required by CodeQL tools.
|
|
||||||
|
|
||||||
For details, see the [guide on query metadata](https://github.com/Semmle/ql/blob/master/docs/query-metadata-style-guide.md).
|
|
||||||
|
|
||||||
3. **Formatting**
|
|
||||||
|
|
||||||
- The queries and libraries must be [autoformatted](https://help.semmle.com/codeql/codeql-for-vscode/reference/editor.html#autoformatting).
|
|
||||||
|
|
||||||
4. **Compilation**
|
|
||||||
|
|
||||||
- Compilation of the query and any associated libraries and tests must be resilient to future development of the standard libraries. This means that the functionality cannot use internal APIs, cannot depend on the output of `getAQlClass`, and cannot make use of regexp matching on `toString`.
|
|
||||||
- The query and any associated libraries and tests must not cause any compiler warnings to be emitted (such as use of deprecated functionality or missing `override` annotations).
|
|
||||||
|
|
||||||
5. **Results**
|
|
||||||
|
|
||||||
- The query must have at least one true positive result on some revision of a real project.
|
|
||||||
|
|
||||||
6. **Contributor License Agreement**
|
|
||||||
|
|
||||||
- The contributor can satisfy the [CLA](CONTRIBUTING.md#contributor-license-agreement).
|
|
||||||
|
|
||||||
## Non-requirements
|
|
||||||
|
|
||||||
Other criteria typically required for our standard queries and libraries are not required for experimental queries and libraries. In particular, fully disciplined query [metadata](docs/query-metadata-style-guide.md), query [help](docs/query-help-style-guide.md), tests, a low false positive rate and performance tuning are not required (but nonetheless recommended).
|
|
||||||
|
|||||||
79
docs/supported-queries.md
Normal file
79
docs/supported-queries.md
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
# Supported CodeQL queries and libraries
|
||||||
|
|
||||||
|
Queries and libraries outside [the `experimental` directories](experimental.md) are _supported_ by GitHub, allowing our users to rely on their continued existence and functionality in the future:
|
||||||
|
|
||||||
|
1. Once a query or library has appeared in a stable release, a one-year deprecation period is required before we can remove it. There can be exceptions to this when it's not technically possible to mark it as deprecated.
|
||||||
|
2. Major changes to supported queries and libraries are always announced in the [change notes for stable releases](../change-notes/).
|
||||||
|
3. We will do our best to address user reports of false positives or false negatives.
|
||||||
|
|
||||||
|
Because of these commitments, we set a high bar for accepting new supported queries. The requirements are detailed in the rest of this document.
|
||||||
|
|
||||||
|
## Steps for introducing a new supported query
|
||||||
|
|
||||||
|
The process must begin with the first step and must conclude with the final step. The remaining steps can be performed in any order.
|
||||||
|
|
||||||
|
1. **Have the query merged into the appropriate `experimental` subdirectory**
|
||||||
|
|
||||||
|
See [CONTRIBUTING.md](../CONTRIBUTING.md).
|
||||||
|
|
||||||
|
2. **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 query. For more information on writing query help, see the [Query help style guide](query-help-style-guide.md).
|
||||||
|
|
||||||
|
- Note, in particular, that almost all queries need to have a pair of "before" and "after" examples demonstrating the kind of problem the query identifies and how to fix it. Make sure that the examples are actually consistent with what the query does, for example by including them in your unit tests.
|
||||||
|
- At the time of writing, there is no way of previewing help locally. Once you've opened a PR, a preview will be created as part of the CI checks. A GitHub employee will review this and let you know of any problems.
|
||||||
|
|
||||||
|
3. **Write unit tests**
|
||||||
|
|
||||||
|
Add one or more unit tests for the query (and for any library changes you make) to the `ql/<language>/ql/test/experimental` directory. Tests for library changes go into the `library-tests` subdirectory, and tests for queries go into `query-tests` with their relative path mirroring the query's location under `ql/<language>/ql/src/experimental`.
|
||||||
|
|
||||||
|
See the section on [Testing custom queries](https://help.semmle.com/codeql/codeql-cli/procedures/test-queries.html) in the [CodeQL documentation](https://help.semmle.com/codeql/) for more information.
|
||||||
|
|
||||||
|
4. **Test for correctness on real-world code**
|
||||||
|
|
||||||
|
Test the query on a number of large real-world projects to make sure it doesn't give too many false positive results. Adjust the `@precision` and `@problem.severity` attributes in accordance with the real-world results you observe. See the advice on query metadata below.
|
||||||
|
|
||||||
|
You can use the LGTM.com [query console](https://lgtm.com/query) to get an overview of true and false positive results on a large number of projects. The simplest way to do this is to:
|
||||||
|
|
||||||
|
1. [Create a list of prominent projects](https://lgtm.com/help/lgtm/managing-project-lists) on LGTM.
|
||||||
|
2. In the query console, [run your query against your custom project list](https://lgtm.com/help/lgtm/using-query-console).
|
||||||
|
3. Save links to your query console results and include them in discussions on issues and pull requests.
|
||||||
|
|
||||||
|
5. **Test and improve performance**
|
||||||
|
|
||||||
|
There must be a balance between the execution time of a query and the value of its results: queries that are highly valuable and broadly applicable can be allowed to take longer to run. In all cases, you need to address any easy-to-fix performance issues before the query is put into production.
|
||||||
|
|
||||||
|
QL performance profiling and tuning is an advanced topic, and some tasks will require assistance from GitHub employees. With that said, there are several things you can do.
|
||||||
|
|
||||||
|
- Understand [the evaluation model of QL](https://help.semmle.com/QL/ql-handbook/evaluation.html). It's more similar to SQL than to any mainstream programming language.
|
||||||
|
- Most performance tuning in QL boils down to computing as few tuples (rows of data) as possible. As a mental model, think of predicate evaluation as enumerating all combinations of parameters that satisfy the predicate body. This includes the implicit parameters `this` and `result`.
|
||||||
|
- The major libraries in CodeQL are _cached_ and will only be computed once for the entire suite of queries. The first query that needs a cached _stage_ will trigger its evaluation. This means that query authors should usually only look at the run time of the last stage of evaluation.
|
||||||
|
- In [the settings for the VSCode extension](https://help.semmle.com/codeql/codeql-for-vscode/reference/settings.html), check the box "Running Queries: Debug" (`codeQL.runningQueries.debug`). Then find "CodeQL Query Server" in the VSCode Output panel (View -> Output) and capture the output when running the query. That output contains timing and tuple counts for all computed predicates.
|
||||||
|
- To clear the entire cache, invoke "CodeQL: Clear Cache" from the VSCode command palette.
|
||||||
|
|
||||||
|
6. **Make sure your query has the correct metadata**
|
||||||
|
|
||||||
|
For the full reference on writing query metadata, see the [Query metadata style guide](query-metadata-style-guide.md). The following constitutes a checklist.
|
||||||
|
|
||||||
|
a. Each query needs a `@name`, a `@description`, and a `@kind`.
|
||||||
|
|
||||||
|
b. Alert queries also need a `@problem.severity` and a `@precision`.
|
||||||
|
|
||||||
|
- The severity is one of `error`, `warning`, or `recommendation`.
|
||||||
|
- The precision is one of `very-high`, `high`, `medium` or `low`. It may take a few iterations to get this right.
|
||||||
|
- Currently, LGTM runs all `error` or `warning` queries with a `very-high`, `high`, or `medium` precision. In addition, `recommendation` queries with `very-high` or `high` precision are run.
|
||||||
|
- However, results from `error` and `warning` queries with `medium` precision, as well as `recommendation` queries with `high` precision, are not shown by default.
|
||||||
|
|
||||||
|
c. All queries need an `@id`.
|
||||||
|
|
||||||
|
- The ID should be consistent with the ids of similar queries for other languages; for example, there is a C/C++ query looking for comments containing the word "TODO" which has id `cpp/todo-comment`, and its C# counterpart has id `cs/todo-comment`.
|
||||||
|
|
||||||
|
d. Provide one or more `@tags` describing the query.
|
||||||
|
|
||||||
|
- Tags are free-form, but we have some conventions, especially for tagging security queries with corresponding CWE numbers.
|
||||||
|
|
||||||
|
7. **Move your query out of `experimental`**
|
||||||
|
|
||||||
|
- The structure of an `experimental` subdirectory mirrors the structure of its parent directory, so this step may just be a matter of removing the `experimental/` prefix of the query and test paths. Be sure to also edit any references to the query path in tests.
|
||||||
|
- Add the query to one of the legacy suite files in `ql/<language>/config/suites/<language>/` if it exists. Note that there are separate suite directories for C and C++, `c` and `cpp` respectively, and the query should be added to one or both as appropriate.
|
||||||
|
- Add a release note to `change-notes/<next-version>/analysis-<language>.md`.
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import javascript
|
import javascript
|
||||||
|
private import dataflow.internal.StepSummary
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A definition of a `Promise` object.
|
* A definition of a `Promise` object.
|
||||||
@@ -121,36 +122,156 @@ class AggregateES2015PromiseDefinition extends PromiseCreationCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This module defines how data-flow propagates into and out of a Promise.
|
* Common predicates shared between type-tracking and data-flow for promises.
|
||||||
* The data-flow is based on pseudo-properties rather than tainting the Promise object (which is what `PromiseTaintStep` does).
|
|
||||||
*/
|
*/
|
||||||
private module PromiseFlow {
|
module Promises {
|
||||||
/**
|
/**
|
||||||
* Gets the pseudo-field used to describe resolved values in a promise.
|
* Gets the pseudo-field used to describe resolved values in a promise.
|
||||||
*/
|
*/
|
||||||
string resolveField() { result = "$PromiseResolveField$" }
|
string valueProp() { result = "$PromiseResolveField$" }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the pseudo-field used to describe rejected values in a promise.
|
* Gets the pseudo-field used to describe rejected values in a promise.
|
||||||
*/
|
*/
|
||||||
string rejectField() { result = "$PromiseRejectField$" }
|
string errorProp() { result = "$PromiseRejectField$" }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A module for supporting promises in type-tracking predicates.
|
||||||
|
* The `PromiseTypeTracking::promiseStep` predicate is used for type tracking in and out of promises,
|
||||||
|
* and is included in the standard type-tracking steps (`SourceNode::track`).
|
||||||
|
* The `TypeTracker::startInPromise()` predicate can be used to initiate a type-tracker
|
||||||
|
* where the tracked value is a promise.
|
||||||
|
*
|
||||||
|
* The below is an example of a type-tracking predicate where the initial value is a promise:
|
||||||
|
* ```
|
||||||
|
* DataFlow::SourceNode myType(DataFlow::TypeTracker t) {
|
||||||
|
* t.startInPromise() and
|
||||||
|
* result = <the promise value> and
|
||||||
|
* or
|
||||||
|
* exists(DataFlow::TypeTracker t2 | result = myType(t2).track(t2, t))
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* The type-tracking predicate above will only end (`t = DataFlow::TypeTracker::end()`) after the tracked value has been
|
||||||
|
* extracted from the promise.
|
||||||
|
*
|
||||||
|
* The `PromiseTypeTracking::promiseStep` predicate can be used instead of `SourceNode::track`
|
||||||
|
* to get type-tracking only for promise steps.
|
||||||
|
*
|
||||||
|
* Replace `t.startInPromise()` in the above example with `t.start()` to create a type-tracking predicate
|
||||||
|
* where the value is not initially inside a promise.
|
||||||
|
*/
|
||||||
|
module PromiseTypeTracking {
|
||||||
|
/**
|
||||||
|
* Gets the result from a single step through a promise, from `pred` to `result` summarized by `summary`.
|
||||||
|
* This can be loading a resolved value from a promise, storing a value in a promise, or copying a resolved value from one promise to another.
|
||||||
|
*/
|
||||||
|
DataFlow::SourceNode promiseStep(DataFlow::SourceNode pred, StepSummary summary) {
|
||||||
|
exists(PromiseFlowStep step, string field | field = Promises::valueProp() |
|
||||||
|
summary = LoadStep(field) and
|
||||||
|
step.load(pred, result, field)
|
||||||
|
or
|
||||||
|
summary = StoreStep(field) and
|
||||||
|
step.store(pred, result, field)
|
||||||
|
or
|
||||||
|
summary = LevelStep() and
|
||||||
|
step.loadStore(pred, result, field)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the result from a single step through a promise, from `pred` with tracker `t2` to `result` with tracker `t`.
|
||||||
|
* This can be loading a resolved value from a promise, storing a value in a promise, or copying a resolved value from one promise to another.
|
||||||
|
*/
|
||||||
|
DataFlow::SourceNode promiseStep(
|
||||||
|
DataFlow::SourceNode pred, DataFlow::TypeTracker t, DataFlow::TypeTracker t2
|
||||||
|
) {
|
||||||
|
exists(StepSummary summary |
|
||||||
|
result = PromiseTypeTracking::promiseStep(pred, summary) and
|
||||||
|
t = t2.append(summary)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class enabling the use of the `resolveField` as a pseudo-property in type-tracking predicates.
|
||||||
|
*/
|
||||||
|
private class ResolveFieldAsTypeTrackingProperty extends TypeTrackingPseudoProperty {
|
||||||
|
ResolveFieldAsTypeTrackingProperty() { this = Promises::valueProp() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An `AdditionalFlowStep` used to model a data-flow step related to promises.
|
||||||
|
*
|
||||||
|
* The `loadStep`/`storeStep`/`loadStoreStep` methods are overloaded such that the new predicates
|
||||||
|
* `load`/`store`/`loadStore` can be used in the `PromiseTypeTracking` module.
|
||||||
|
* (Thereby avoiding conflicts with a "cousin" `AdditionalFlowStep` implementation.)
|
||||||
|
*
|
||||||
|
* The class is private and is only intended to be used inside the `PromiseTypeTracking` and `PromiseFlow` modules.
|
||||||
|
*/
|
||||||
|
abstract private class PromiseFlowStep extends DataFlow::AdditionalFlowStep {
|
||||||
|
final override predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||||
|
|
||||||
|
final override predicate step(
|
||||||
|
DataFlow::Node p, DataFlow::Node s, DataFlow::FlowLabel pl, DataFlow::FlowLabel sl
|
||||||
|
) {
|
||||||
|
none()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if the property `prop` of the object `pred` should be loaded into `succ`.
|
||||||
|
*/
|
||||||
|
predicate load(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
|
||||||
|
|
||||||
|
final override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||||
|
this.load(pred, succ, prop)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `pred` should be stored in the object `succ` under the property `prop`.
|
||||||
|
*/
|
||||||
|
predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
|
||||||
|
|
||||||
|
final override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||||
|
this.store(pred, succ, prop)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
|
||||||
|
*/
|
||||||
|
predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
|
||||||
|
|
||||||
|
final override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||||
|
this.loadStore(pred, succ, prop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This module defines how data-flow propagates into and out of a Promise.
|
||||||
|
* The data-flow is based on pseudo-properties rather than tainting the Promise object (which is what `PromiseTaintStep` does).
|
||||||
|
*/
|
||||||
|
private module PromiseFlow {
|
||||||
|
private predicate valueProp = Promises::valueProp/0;
|
||||||
|
|
||||||
|
private predicate errorProp = Promises::errorProp/0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A flow step describing a promise definition.
|
* A flow step describing a promise definition.
|
||||||
*
|
*
|
||||||
* The resolved/rejected value is written to a pseudo-field on the promise.
|
* The resolved/rejected value is written to a pseudo-field on the promise.
|
||||||
*/
|
*/
|
||||||
class PromiseDefitionStep extends DataFlow::AdditionalFlowStep {
|
class PromiseDefitionStep extends PromiseFlowStep {
|
||||||
PromiseDefinition promise;
|
PromiseDefinition promise;
|
||||||
|
|
||||||
PromiseDefitionStep() { this = promise }
|
PromiseDefitionStep() { this = promise }
|
||||||
|
|
||||||
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||||
prop = resolveField() and
|
prop = valueProp() and
|
||||||
pred = promise.getResolveParameter().getACall().getArgument(0) and
|
pred = promise.getResolveParameter().getACall().getArgument(0) and
|
||||||
succ = this
|
succ = this
|
||||||
or
|
or
|
||||||
prop = rejectField() and
|
prop = errorProp() and
|
||||||
(
|
(
|
||||||
pred = promise.getRejectParameter().getACall().getArgument(0) or
|
pred = promise.getRejectParameter().getACall().getArgument(0) or
|
||||||
pred = promise.getExecutor().getExceptionalReturn()
|
pred = promise.getExecutor().getExceptionalReturn()
|
||||||
@@ -158,9 +279,9 @@ private module PromiseFlow {
|
|||||||
succ = this
|
succ = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
override predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||||
// Copy the value of a resolved promise to the value of this promise.
|
// Copy the value of a resolved promise to the value of this promise.
|
||||||
prop = resolveField() and
|
prop = valueProp() and
|
||||||
pred = promise.getResolveParameter().getACall().getArgument(0) and
|
pred = promise.getResolveParameter().getACall().getArgument(0) and
|
||||||
succ = this
|
succ = this
|
||||||
}
|
}
|
||||||
@@ -169,20 +290,20 @@ private module PromiseFlow {
|
|||||||
/**
|
/**
|
||||||
* A flow step describing the a Promise.resolve (and similar) call.
|
* A flow step describing the a Promise.resolve (and similar) call.
|
||||||
*/
|
*/
|
||||||
class CreationStep extends DataFlow::AdditionalFlowStep {
|
class CreationStep extends PromiseFlowStep {
|
||||||
PromiseCreationCall promise;
|
PromiseCreationCall promise;
|
||||||
|
|
||||||
CreationStep() { this = promise }
|
CreationStep() { this = promise }
|
||||||
|
|
||||||
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||||
prop = resolveField() and
|
prop = valueProp() and
|
||||||
pred = promise.getValue() and
|
pred = promise.getValue() and
|
||||||
succ = this
|
succ = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
override predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||||
// Copy the value of a resolved promise to the value of this promise.
|
// Copy the value of a resolved promise to the value of this promise.
|
||||||
prop = resolveField() and
|
prop = valueProp() and
|
||||||
pred = promise.getValue() and
|
pred = promise.getValue() and
|
||||||
succ = this
|
succ = this
|
||||||
}
|
}
|
||||||
@@ -192,7 +313,7 @@ private module PromiseFlow {
|
|||||||
* A load step loading the pseudo-field describing that the promise is rejected.
|
* A load step loading the pseudo-field describing that the promise is rejected.
|
||||||
* The rejected value is thrown as a exception.
|
* The rejected value is thrown as a exception.
|
||||||
*/
|
*/
|
||||||
class AwaitStep extends DataFlow::AdditionalFlowStep {
|
class AwaitStep extends PromiseFlowStep {
|
||||||
DataFlow::Node operand;
|
DataFlow::Node operand;
|
||||||
AwaitExpr await;
|
AwaitExpr await;
|
||||||
|
|
||||||
@@ -201,12 +322,12 @@ private module PromiseFlow {
|
|||||||
operand.getEnclosingExpr() = await.getOperand()
|
operand.getEnclosingExpr() = await.getOperand()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
override predicate load(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||||
prop = resolveField() and
|
prop = valueProp() and
|
||||||
succ = this and
|
succ = this and
|
||||||
pred = operand
|
pred = operand
|
||||||
or
|
or
|
||||||
prop = rejectField() and
|
prop = errorProp() and
|
||||||
succ = await.getExceptionTarget() and
|
succ = await.getExceptionTarget() and
|
||||||
pred = operand
|
pred = operand
|
||||||
}
|
}
|
||||||
@@ -215,37 +336,37 @@ private module PromiseFlow {
|
|||||||
/**
|
/**
|
||||||
* A flow step describing the data-flow related to the `.then` method of a promise.
|
* A flow step describing the data-flow related to the `.then` method of a promise.
|
||||||
*/
|
*/
|
||||||
class ThenStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
|
class ThenStep extends PromiseFlowStep, DataFlow::MethodCallNode {
|
||||||
ThenStep() { this.getMethodName() = "then" }
|
ThenStep() { this.getMethodName() = "then" }
|
||||||
|
|
||||||
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
override predicate load(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||||
prop = resolveField() and
|
prop = valueProp() and
|
||||||
pred = getReceiver() and
|
pred = getReceiver() and
|
||||||
succ = getCallback(0).getParameter(0)
|
succ = getCallback(0).getParameter(0)
|
||||||
or
|
or
|
||||||
prop = rejectField() and
|
prop = errorProp() and
|
||||||
pred = getReceiver() and
|
pred = getReceiver() and
|
||||||
succ = getCallback(1).getParameter(0)
|
succ = getCallback(1).getParameter(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
override predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||||
not exists(this.getArgument(1)) and
|
not exists(this.getArgument(1)) and
|
||||||
prop = rejectField() and
|
prop = errorProp() and
|
||||||
pred = getReceiver() and
|
pred = getReceiver() and
|
||||||
succ = this
|
succ = this
|
||||||
or
|
or
|
||||||
// read the value of a resolved/rejected promise that is returned
|
// read the value of a resolved/rejected promise that is returned
|
||||||
(prop = rejectField() or prop = resolveField()) and
|
(prop = errorProp() or prop = valueProp()) and
|
||||||
pred = getCallback([0 .. 1]).getAReturn() and
|
pred = getCallback([0 .. 1]).getAReturn() and
|
||||||
succ = this
|
succ = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||||
prop = resolveField() and
|
prop = valueProp() and
|
||||||
pred = getCallback([0 .. 1]).getAReturn() and
|
pred = getCallback([0 .. 1]).getAReturn() and
|
||||||
succ = this
|
succ = this
|
||||||
or
|
or
|
||||||
prop = rejectField() and
|
prop = errorProp() and
|
||||||
pred = getCallback([0 .. 1]).getExceptionalReturn() and
|
pred = getCallback([0 .. 1]).getExceptionalReturn() and
|
||||||
succ = this
|
succ = this
|
||||||
}
|
}
|
||||||
@@ -254,32 +375,32 @@ private module PromiseFlow {
|
|||||||
/**
|
/**
|
||||||
* A flow step describing the data-flow related to the `.catch` method of a promise.
|
* A flow step describing the data-flow related to the `.catch` method of a promise.
|
||||||
*/
|
*/
|
||||||
class CatchStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
|
class CatchStep extends PromiseFlowStep, DataFlow::MethodCallNode {
|
||||||
CatchStep() { this.getMethodName() = "catch" }
|
CatchStep() { this.getMethodName() = "catch" }
|
||||||
|
|
||||||
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
override predicate load(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||||
prop = rejectField() and
|
prop = errorProp() and
|
||||||
pred = getReceiver() and
|
pred = getReceiver() and
|
||||||
succ = getCallback(0).getParameter(0)
|
succ = getCallback(0).getParameter(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
override predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||||
prop = resolveField() and
|
prop = valueProp() and
|
||||||
pred = getReceiver().getALocalSource() and
|
pred = getReceiver().getALocalSource() and
|
||||||
succ = this
|
succ = this
|
||||||
or
|
or
|
||||||
// read the value of a resolved/rejected promise that is returned
|
// read the value of a resolved/rejected promise that is returned
|
||||||
(prop = rejectField() or prop = resolveField()) and
|
(prop = errorProp() or prop = valueProp()) and
|
||||||
pred = getCallback(0).getAReturn() and
|
pred = getCallback(0).getAReturn() and
|
||||||
succ = this
|
succ = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||||
prop = rejectField() and
|
prop = errorProp() and
|
||||||
pred = getCallback(0).getExceptionalReturn() and
|
pred = getCallback(0).getExceptionalReturn() and
|
||||||
succ = this
|
succ = this
|
||||||
or
|
or
|
||||||
prop = resolveField() and
|
prop = valueProp() and
|
||||||
pred = getCallback(0).getAReturn() and
|
pred = getCallback(0).getAReturn() and
|
||||||
succ = this
|
succ = this
|
||||||
}
|
}
|
||||||
@@ -288,22 +409,22 @@ private module PromiseFlow {
|
|||||||
/**
|
/**
|
||||||
* A flow step describing the data-flow related to the `.finally` method of a promise.
|
* A flow step describing the data-flow related to the `.finally` method of a promise.
|
||||||
*/
|
*/
|
||||||
class FinallyStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
|
class FinallyStep extends PromiseFlowStep, DataFlow::MethodCallNode {
|
||||||
FinallyStep() { this.getMethodName() = "finally" }
|
FinallyStep() { this.getMethodName() = "finally" }
|
||||||
|
|
||||||
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
override predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||||
(prop = resolveField() or prop = rejectField()) and
|
(prop = valueProp() or prop = errorProp()) and
|
||||||
pred = getReceiver() and
|
pred = getReceiver() and
|
||||||
succ = this
|
succ = this
|
||||||
or
|
or
|
||||||
// read the value of a rejected promise that is returned
|
// read the value of a rejected promise that is returned
|
||||||
prop = rejectField() and
|
prop = errorProp() and
|
||||||
pred = getCallback(0).getAReturn() and
|
pred = getCallback(0).getAReturn() and
|
||||||
succ = this
|
succ = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||||
prop = rejectField() and
|
prop = errorProp() and
|
||||||
pred = getCallback(0).getExceptionalReturn() and
|
pred = getCallback(0).getExceptionalReturn() and
|
||||||
succ = this
|
succ = this
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -354,7 +354,7 @@ private predicate barrierGuardBlocksExpr(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `guard` blocks the flow of a value reachable through exploratory flow.
|
* Holds if `guard` may block the flow of a value reachable through exploratory flow.
|
||||||
*/
|
*/
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
private predicate barrierGuardIsRelevant(BarrierGuardNode guard) {
|
private predicate barrierGuardIsRelevant(BarrierGuardNode guard) {
|
||||||
@@ -1106,6 +1106,7 @@ private predicate flowStep(
|
|||||||
// Flow into higher-order call
|
// Flow into higher-order call
|
||||||
flowIntoHigherOrderCall(pred, succ, cfg, summary)
|
flowIntoHigherOrderCall(pred, succ, cfg, summary)
|
||||||
) and
|
) and
|
||||||
|
isRelevant(succ, cfg) and
|
||||||
not cfg.isBarrier(succ) and
|
not cfg.isBarrier(succ) and
|
||||||
not isBarrierEdge(cfg, pred, succ) and
|
not isBarrierEdge(cfg, pred, succ) and
|
||||||
not isLabeledBarrierEdge(cfg, pred, succ, summary.getEndLabel()) and
|
not isLabeledBarrierEdge(cfg, pred, succ, summary.getEndLabel()) and
|
||||||
|
|||||||
@@ -8,147 +8,7 @@
|
|||||||
|
|
||||||
private import javascript
|
private import javascript
|
||||||
private import internal.FlowSteps
|
private import internal.FlowSteps
|
||||||
|
private import internal.StepSummary
|
||||||
private class PropertyName extends string {
|
|
||||||
PropertyName() {
|
|
||||||
this = any(DataFlow::PropRef pr).getPropertyName()
|
|
||||||
or
|
|
||||||
AccessPath::isAssignedInUniqueFile(this)
|
|
||||||
or
|
|
||||||
exists(AccessPath::getAnAssignmentTo(_, this))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class OptionalPropertyName extends string {
|
|
||||||
OptionalPropertyName() { this instanceof PropertyName or this = "" }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A description of a step on an inter-procedural data flow path.
|
|
||||||
*/
|
|
||||||
private newtype TStepSummary =
|
|
||||||
LevelStep() or
|
|
||||||
CallStep() or
|
|
||||||
ReturnStep() or
|
|
||||||
StoreStep(PropertyName prop) or
|
|
||||||
LoadStep(PropertyName prop)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* INTERNAL: Use `TypeTracker` or `TypeBackTracker` instead.
|
|
||||||
*
|
|
||||||
* A description of a step on an inter-procedural data flow path.
|
|
||||||
*/
|
|
||||||
class StepSummary extends TStepSummary {
|
|
||||||
/** Gets a textual representation of this step summary. */
|
|
||||||
string toString() {
|
|
||||||
this instanceof LevelStep and result = "level"
|
|
||||||
or
|
|
||||||
this instanceof CallStep and result = "call"
|
|
||||||
or
|
|
||||||
this instanceof ReturnStep and result = "return"
|
|
||||||
or
|
|
||||||
exists(string prop | this = StoreStep(prop) | result = "store " + prop)
|
|
||||||
or
|
|
||||||
exists(string prop | this = LoadStep(prop) | result = "load " + prop)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module StepSummary {
|
|
||||||
/**
|
|
||||||
* INTERNAL: Use `SourceNode.track()` or `SourceNode.backtrack()` instead.
|
|
||||||
*/
|
|
||||||
cached
|
|
||||||
predicate step(DataFlow::SourceNode pred, DataFlow::SourceNode succ, StepSummary summary) {
|
|
||||||
exists(DataFlow::Node mid | pred.flowsTo(mid) | smallstep(mid, succ, summary))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* INTERNAL: Use `TypeBackTracker.smallstep()` instead.
|
|
||||||
*/
|
|
||||||
predicate smallstep(DataFlow::Node pred, DataFlow::Node succ, StepSummary summary) {
|
|
||||||
// Flow through properties of objects
|
|
||||||
propertyFlowStep(pred, succ) and
|
|
||||||
summary = LevelStep()
|
|
||||||
or
|
|
||||||
// Flow through global variables
|
|
||||||
globalFlowStep(pred, succ) and
|
|
||||||
summary = LevelStep()
|
|
||||||
or
|
|
||||||
// Flow into function
|
|
||||||
callStep(pred, succ) and
|
|
||||||
summary = CallStep()
|
|
||||||
or
|
|
||||||
// Flow out of function
|
|
||||||
returnStep(pred, succ) and
|
|
||||||
summary = ReturnStep()
|
|
||||||
or
|
|
||||||
// Flow through an instance field between members of the same class
|
|
||||||
DataFlow::localFieldStep(pred, succ) and
|
|
||||||
summary = LevelStep()
|
|
||||||
or
|
|
||||||
exists(string prop |
|
|
||||||
basicStoreStep(pred, succ, prop) and
|
|
||||||
summary = StoreStep(prop)
|
|
||||||
or
|
|
||||||
basicLoadStep(pred, succ, prop) and
|
|
||||||
summary = LoadStep(prop)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
any(AdditionalTypeTrackingStep st).step(pred, succ) and
|
|
||||||
summary = LevelStep()
|
|
||||||
or
|
|
||||||
// Store to global access path
|
|
||||||
exists(string name |
|
|
||||||
pred = AccessPath::getAnAssignmentTo(name) and
|
|
||||||
AccessPath::isAssignedInUniqueFile(name) and
|
|
||||||
succ = DataFlow::globalAccessPathRootPseudoNode() and
|
|
||||||
summary = StoreStep(name)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
// Load from global access path
|
|
||||||
exists(string name |
|
|
||||||
succ = AccessPath::getAReferenceTo(name) and
|
|
||||||
AccessPath::isAssignedInUniqueFile(name) and
|
|
||||||
pred = DataFlow::globalAccessPathRootPseudoNode() and
|
|
||||||
summary = LoadStep(name)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
// Store to non-global access path
|
|
||||||
exists(string name |
|
|
||||||
pred = AccessPath::getAnAssignmentTo(succ, name) and
|
|
||||||
summary = StoreStep(name)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
// Load from non-global access path
|
|
||||||
exists(string name |
|
|
||||||
succ = AccessPath::getAReferenceTo(pred, name) and
|
|
||||||
summary = LoadStep(name) and
|
|
||||||
name != ""
|
|
||||||
)
|
|
||||||
or
|
|
||||||
// Summarize calls with flow directly from a parameter to a return.
|
|
||||||
exists(DataFlow::ParameterNode param, DataFlow::FunctionNode fun |
|
|
||||||
(
|
|
||||||
param.flowsTo(fun.getAReturn()) and
|
|
||||||
summary = LevelStep()
|
|
||||||
or
|
|
||||||
exists(string prop |
|
|
||||||
param.getAPropertyRead(prop).flowsTo(fun.getAReturn()) and
|
|
||||||
summary = LoadStep(prop)
|
|
||||||
)
|
|
||||||
) and
|
|
||||||
if param = fun.getAParameter()
|
|
||||||
then
|
|
||||||
// Step from argument to call site.
|
|
||||||
argumentPassing(succ, pred, fun.getFunction(), param)
|
|
||||||
else (
|
|
||||||
// Step from captured parameter to local call sites
|
|
||||||
pred = param and
|
|
||||||
succ = fun.getAnInvocation()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalPropertyName prop)
|
private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalPropertyName prop)
|
||||||
|
|
||||||
@@ -216,6 +76,18 @@ class TypeTracker extends TTypeTracker {
|
|||||||
*/
|
*/
|
||||||
predicate start() { hasCall = false and prop = "" }
|
predicate start() { hasCall = false and prop = "" }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this is the starting point of type tracking, and the value starts in the property named `propName`.
|
||||||
|
* The type tracking only ends after the property has been loaded.
|
||||||
|
*/
|
||||||
|
predicate startInProp(PropertyName propName) { hasCall = false and prop = propName }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this is the starting point of type tracking, and the initial value is a promise.
|
||||||
|
* The type tracking only ends after the value has been extracted from the promise.
|
||||||
|
*/
|
||||||
|
predicate startInPromise() { startInProp(Promises::valueProp()) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this is the starting point of type tracking
|
* Holds if this is the starting point of type tracking
|
||||||
* when tracking a parameter into a call, but not out of it.
|
* when tracking a parameter into a call, but not out of it.
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private import javascript
|
private import javascript
|
||||||
|
private import semmle.javascript.dataflow.internal.StepSummary
|
||||||
|
|
||||||
cached
|
cached
|
||||||
module CallGraph {
|
module CallGraph {
|
||||||
@@ -83,7 +84,7 @@ module CallGraph {
|
|||||||
getAFunctionReference(function, 0, t.continue()).flowsTo(callback)
|
getAFunctionReference(function, 0, t.continue()).flowsTo(callback)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(DataFlow::StepSummary summary, DataFlow::TypeTracker t2 |
|
exists(StepSummary summary, DataFlow::TypeTracker t2 |
|
||||||
result = getABoundFunctionReferenceAux(function, boundArgs, t2, summary) and
|
result = getABoundFunctionReferenceAux(function, boundArgs, t2, summary) and
|
||||||
t = t2.append(summary)
|
t = t2.append(summary)
|
||||||
)
|
)
|
||||||
@@ -91,12 +92,11 @@ module CallGraph {
|
|||||||
|
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
private DataFlow::SourceNode getABoundFunctionReferenceAux(
|
private DataFlow::SourceNode getABoundFunctionReferenceAux(
|
||||||
DataFlow::FunctionNode function, int boundArgs, DataFlow::TypeTracker t,
|
DataFlow::FunctionNode function, int boundArgs, DataFlow::TypeTracker t, StepSummary summary
|
||||||
DataFlow::StepSummary summary
|
|
||||||
) {
|
) {
|
||||||
exists(DataFlow::SourceNode prev |
|
exists(DataFlow::SourceNode prev |
|
||||||
prev = getABoundFunctionReferenceAux(function, boundArgs, t) and
|
prev = getABoundFunctionReferenceAux(function, boundArgs, t) and
|
||||||
DataFlow::StepSummary::step(prev, result, summary)
|
StepSummary::step(prev, result, summary)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,157 @@
|
|||||||
|
import javascript
|
||||||
|
private import semmle.javascript.dataflow.TypeTracking
|
||||||
|
private import FlowSteps
|
||||||
|
|
||||||
|
class PropertyName extends string {
|
||||||
|
PropertyName() {
|
||||||
|
this = any(DataFlow::PropRef pr).getPropertyName()
|
||||||
|
or
|
||||||
|
AccessPath::isAssignedInUniqueFile(this)
|
||||||
|
or
|
||||||
|
exists(AccessPath::getAnAssignmentTo(_, this))
|
||||||
|
or
|
||||||
|
this instanceof TypeTrackingPseudoProperty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OptionalPropertyName extends string {
|
||||||
|
OptionalPropertyName() { this instanceof PropertyName or this = "" }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A pseudo-property that can be used in type-tracking.
|
||||||
|
*/
|
||||||
|
abstract class TypeTrackingPseudoProperty extends string {
|
||||||
|
bindingset[this]
|
||||||
|
TypeTrackingPseudoProperty() { any() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A description of a step on an inter-procedural data flow path.
|
||||||
|
*/
|
||||||
|
newtype TStepSummary =
|
||||||
|
LevelStep() or
|
||||||
|
CallStep() or
|
||||||
|
ReturnStep() or
|
||||||
|
StoreStep(PropertyName prop) or
|
||||||
|
LoadStep(PropertyName prop)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* INTERNAL: Use `TypeTracker` or `TypeBackTracker` instead.
|
||||||
|
*
|
||||||
|
* A description of a step on an inter-procedural data flow path.
|
||||||
|
*/
|
||||||
|
class StepSummary extends TStepSummary {
|
||||||
|
/** Gets a textual representation of this step summary. */
|
||||||
|
string toString() {
|
||||||
|
this instanceof LevelStep and result = "level"
|
||||||
|
or
|
||||||
|
this instanceof CallStep and result = "call"
|
||||||
|
or
|
||||||
|
this instanceof ReturnStep and result = "return"
|
||||||
|
or
|
||||||
|
exists(string prop | this = StoreStep(prop) | result = "store " + prop)
|
||||||
|
or
|
||||||
|
exists(string prop | this = LoadStep(prop) | result = "load " + prop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module StepSummary {
|
||||||
|
/**
|
||||||
|
* INTERNAL: Use `SourceNode.track()` or `SourceNode.backtrack()` instead.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate step(DataFlow::SourceNode pred, DataFlow::SourceNode succ, StepSummary summary) {
|
||||||
|
exists(DataFlow::Node mid | pred.flowsTo(mid) | smallstep(mid, succ, summary))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* INTERNAL: Use `TypeBackTracker.smallstep()` instead.
|
||||||
|
*/
|
||||||
|
predicate smallstep(DataFlow::Node pred, DataFlow::Node succ, StepSummary summary) {
|
||||||
|
// Flow through properties of objects
|
||||||
|
propertyFlowStep(pred, succ) and
|
||||||
|
summary = LevelStep()
|
||||||
|
or
|
||||||
|
// Flow through global variables
|
||||||
|
globalFlowStep(pred, succ) and
|
||||||
|
summary = LevelStep()
|
||||||
|
or
|
||||||
|
// Flow into function
|
||||||
|
callStep(pred, succ) and
|
||||||
|
summary = CallStep()
|
||||||
|
or
|
||||||
|
// Flow out of function
|
||||||
|
returnStep(pred, succ) and
|
||||||
|
summary = ReturnStep()
|
||||||
|
or
|
||||||
|
// Flow through an instance field between members of the same class
|
||||||
|
DataFlow::localFieldStep(pred, succ) and
|
||||||
|
summary = LevelStep()
|
||||||
|
or
|
||||||
|
exists(string prop |
|
||||||
|
basicStoreStep(pred, succ, prop) and
|
||||||
|
summary = StoreStep(prop)
|
||||||
|
or
|
||||||
|
basicLoadStep(pred, succ, prop) and
|
||||||
|
summary = LoadStep(prop)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
any(AdditionalTypeTrackingStep st).step(pred, succ) and
|
||||||
|
summary = LevelStep()
|
||||||
|
or
|
||||||
|
// Store to global access path
|
||||||
|
exists(string name |
|
||||||
|
pred = AccessPath::getAnAssignmentTo(name) and
|
||||||
|
AccessPath::isAssignedInUniqueFile(name) and
|
||||||
|
succ = DataFlow::globalAccessPathRootPseudoNode() and
|
||||||
|
summary = StoreStep(name)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
// Load from global access path
|
||||||
|
exists(string name |
|
||||||
|
succ = AccessPath::getAReferenceTo(name) and
|
||||||
|
AccessPath::isAssignedInUniqueFile(name) and
|
||||||
|
pred = DataFlow::globalAccessPathRootPseudoNode() and
|
||||||
|
summary = LoadStep(name)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
// Store to non-global access path
|
||||||
|
exists(string name |
|
||||||
|
pred = AccessPath::getAnAssignmentTo(succ, name) and
|
||||||
|
summary = StoreStep(name)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
// Load from non-global access path
|
||||||
|
exists(string name |
|
||||||
|
succ = AccessPath::getAReferenceTo(pred, name) and
|
||||||
|
summary = LoadStep(name) and
|
||||||
|
name != ""
|
||||||
|
)
|
||||||
|
or
|
||||||
|
// Step in/out of a promise
|
||||||
|
succ = PromiseTypeTracking::promiseStep(pred, summary)
|
||||||
|
or
|
||||||
|
// Summarize calls with flow directly from a parameter to a return.
|
||||||
|
exists(DataFlow::ParameterNode param, DataFlow::FunctionNode fun |
|
||||||
|
(
|
||||||
|
param.flowsTo(fun.getAReturn()) and
|
||||||
|
summary = LevelStep()
|
||||||
|
or
|
||||||
|
exists(string prop |
|
||||||
|
param.getAPropertyRead(prop).flowsTo(fun.getAReturn()) and
|
||||||
|
summary = LoadStep(prop)
|
||||||
|
)
|
||||||
|
) and
|
||||||
|
if param = fun.getAParameter()
|
||||||
|
then
|
||||||
|
// Step from argument to call site.
|
||||||
|
argumentPassing(succ, pred, fun.getFunction(), param)
|
||||||
|
else (
|
||||||
|
// Step from captured parameter to local call sites
|
||||||
|
pred = param and
|
||||||
|
succ = fun.getAnInvocation()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -566,36 +566,18 @@ module ClientRequest {
|
|||||||
* The `isPromise` parameter reflects whether the reference is a promise containing
|
* The `isPromise` parameter reflects whether the reference is a promise containing
|
||||||
* an instance of `chrome-remote-interface`, or an instance of `chrome-remote-interface`.
|
* an instance of `chrome-remote-interface`, or an instance of `chrome-remote-interface`.
|
||||||
*/
|
*/
|
||||||
private DataFlow::SourceNode chromeRemoteInterface(DataFlow::TypeTracker t, boolean isPromise) {
|
private DataFlow::SourceNode chromeRemoteInterface(DataFlow::TypeTracker t) {
|
||||||
t.start() and
|
|
||||||
exists(DataFlow::CallNode call |
|
exists(DataFlow::CallNode call |
|
||||||
call = DataFlow::moduleImport("chrome-remote-interface").getAnInvocation()
|
call = DataFlow::moduleImport("chrome-remote-interface").getAnInvocation()
|
||||||
|
|
|
|
||||||
result = call and isPromise = true
|
// the client is inside in a promise.
|
||||||
|
t.startInPromise() and result = call
|
||||||
or
|
or
|
||||||
result = call.getCallback([0 .. 1]).getParameter(0) and isPromise = false
|
// the client is accessed directly using a callback.
|
||||||
|
t.start() and result = call.getCallback([0 .. 1]).getParameter(0)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(DataFlow::TypeTracker t2 | result = chromeRemoteInterface(t2, isPromise).track(t2, t))
|
exists(DataFlow::TypeTracker t2 | result = chromeRemoteInterface(t2).track(t2, t))
|
||||||
or
|
|
||||||
// Simple promise tracking.
|
|
||||||
exists(DataFlow::TypeTracker t2, DataFlow::SourceNode pred |
|
|
||||||
pred = chromeRemoteInterface(t2, true) and
|
|
||||||
isPromise = false and
|
|
||||||
(
|
|
||||||
t2 = t and
|
|
||||||
exists(AwaitExpr await | DataFlow::valueNode(await.getOperand()).getALocalSource() = pred |
|
|
||||||
result.getEnclosingExpr() = await
|
|
||||||
)
|
|
||||||
or
|
|
||||||
t2 = t and
|
|
||||||
exists(DataFlow::MethodCallNode thenCall |
|
|
||||||
thenCall.getMethodName() = "then" and pred = thenCall.getReceiver().getALocalSource()
|
|
||||||
|
|
|
||||||
result = thenCall.getCallback(0).getParameter(0)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -606,7 +588,7 @@ module ClientRequest {
|
|||||||
|
|
||||||
ChromeRemoteInterfaceRequest() {
|
ChromeRemoteInterfaceRequest() {
|
||||||
exists(DataFlow::SourceNode instance |
|
exists(DataFlow::SourceNode instance |
|
||||||
instance = chromeRemoteInterface(DataFlow::TypeTracker::end(), false)
|
instance = chromeRemoteInterface(DataFlow::TypeTracker::end())
|
||||||
|
|
|
|
||||||
optionsArg = 0 and
|
optionsArg = 0 and
|
||||||
this = instance.getAPropertyRead("Page").getAMemberCall("navigate")
|
this = instance.getAPropertyRead("Page").getAMemberCall("navigate")
|
||||||
|
|||||||
@@ -4,6 +4,23 @@
|
|||||||
|
|
||||||
import javascript
|
import javascript
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A call that can produce a file name.
|
||||||
|
*/
|
||||||
|
abstract private class FileNameProducer extends DataFlow::Node {
|
||||||
|
/**
|
||||||
|
* Gets a file name produced by this producer.
|
||||||
|
*/
|
||||||
|
abstract DataFlow::Node getAFileName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A node that contains a file name, and is produced by a `ProducesFileNames`.
|
||||||
|
*/
|
||||||
|
private class ProducedFileName extends FileNameSource {
|
||||||
|
ProducedFileName() { this = any(FileNameProducer producer).getAFileName() }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A file name from the `walk-sync` library.
|
* A file name from the `walk-sync` library.
|
||||||
*/
|
*/
|
||||||
@@ -58,33 +75,41 @@ private class GlobFileNameSource extends FileNameSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A file name or an array of file names from the `globby` library.
|
* Gets a file name or an array of file names from the `globby` library.
|
||||||
|
* The predicate uses type-tracking. However, type-tracking is only used to track a step out of a promise.
|
||||||
*/
|
*/
|
||||||
private class GlobbyFileNameSource extends FileNameSource {
|
private DataFlow::SourceNode globbyFileNameSource(DataFlow::TypeTracker t) {
|
||||||
GlobbyFileNameSource() {
|
|
||||||
exists(string moduleName | moduleName = "globby" |
|
exists(string moduleName | moduleName = "globby" |
|
||||||
// `require('globby').sync(_)`
|
// `require('globby').sync(_)`
|
||||||
this = DataFlow::moduleMember(moduleName, "sync").getACall()
|
t.start() and
|
||||||
|
result = DataFlow::moduleMember(moduleName, "sync").getACall()
|
||||||
or
|
or
|
||||||
// `files` in `require('globby')(_).then(files => ...)`
|
// `files` in `require('globby')(_).then(files => ...)`
|
||||||
this =
|
t.startInPromise() and
|
||||||
DataFlow::moduleImport(moduleName)
|
result = DataFlow::moduleImport(moduleName).getACall()
|
||||||
.getACall()
|
)
|
||||||
.getAMethodCall("then")
|
or
|
||||||
.getCallback(0)
|
// Tracking out of a promise
|
||||||
.getParameter(0)
|
exists(DataFlow::TypeTracker t2 |
|
||||||
|
result = PromiseTypeTracking::promiseStep(globbyFileNameSource(t2), t, t2)
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A file name or an array of file names from the `fast-glob` library.
|
* A file name or an array of file names from the `globby` library.
|
||||||
*/
|
*/
|
||||||
private class FastGlobFileNameSource extends FileNameSource {
|
private class GlobbyFileNameSource extends FileNameSource {
|
||||||
FastGlobFileNameSource() {
|
GlobbyFileNameSource() { this = globbyFileNameSource(DataFlow::TypeTracker::end()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a file name or an array of file names from the `fast-glob` library.
|
||||||
|
* The predicate uses type-tracking. However, type-tracking is only used to track a step out of a promise.
|
||||||
|
*/
|
||||||
|
private DataFlow::Node fastGlobFileNameSource(DataFlow::TypeTracker t) {
|
||||||
exists(string moduleName | moduleName = "fast-glob" |
|
exists(string moduleName | moduleName = "fast-glob" |
|
||||||
// `require('fast-glob').sync(_)`
|
// `require('fast-glob').sync(_)
|
||||||
this = DataFlow::moduleMember(moduleName, "sync").getACall()
|
t.start() and result = DataFlow::moduleMember(moduleName, "sync").getACall()
|
||||||
or
|
or
|
||||||
exists(DataFlow::SourceNode f |
|
exists(DataFlow::SourceNode f |
|
||||||
f = DataFlow::moduleImport(moduleName)
|
f = DataFlow::moduleImport(moduleName)
|
||||||
@@ -93,16 +118,168 @@ private class FastGlobFileNameSource extends FileNameSource {
|
|||||||
|
|
|
|
||||||
// `files` in `require('fast-glob')(_).then(files => ...)` and
|
// `files` in `require('fast-glob')(_).then(files => ...)` and
|
||||||
// `files` in `require('fast-glob').async(_).then(files => ...)`
|
// `files` in `require('fast-glob').async(_).then(files => ...)`
|
||||||
this = f.getACall().getAMethodCall("then").getCallback(0).getParameter(0)
|
t.startInPromise() and result = f.getACall()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// `file` in `require('fast-glob').stream(_).on(_, file => ...)`
|
// `file` in `require('fast-glob').stream(_).on(_, file => ...)`
|
||||||
this =
|
t.start() and
|
||||||
|
result =
|
||||||
DataFlow::moduleMember(moduleName, "stream")
|
DataFlow::moduleMember(moduleName, "stream")
|
||||||
.getACall()
|
.getACall()
|
||||||
.getAMethodCall(EventEmitter::on())
|
.getAMethodCall(EventEmitter::on())
|
||||||
.getCallback(1)
|
.getCallback(1)
|
||||||
.getParameter(0)
|
.getParameter(0)
|
||||||
)
|
)
|
||||||
|
or
|
||||||
|
// Tracking out of a promise
|
||||||
|
exists(DataFlow::TypeTracker t2 |
|
||||||
|
result = PromiseTypeTracking::promiseStep(fastGlobFileNameSource(t2), t, t2)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A file name or an array of file names from the `fast-glob` library.
|
||||||
|
*/
|
||||||
|
private class FastGlobFileNameSource extends FileNameSource {
|
||||||
|
FastGlobFileNameSource() { this = fastGlobFileNameSource(DataFlow::TypeTracker::end()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Classes and predicates for modelling the `fstream` library (https://www.npmjs.com/package/fstream).
|
||||||
|
*/
|
||||||
|
private module FStream {
|
||||||
|
/**
|
||||||
|
* Gets a reference to a method in the `fstream` library.
|
||||||
|
*/
|
||||||
|
private DataFlow::SourceNode getAnFStreamProperty() {
|
||||||
|
exists(DataFlow::SourceNode mod, string readOrWrite, string subMod |
|
||||||
|
mod = DataFlow::moduleImport("fstream") and
|
||||||
|
(readOrWrite = "Reader" or readOrWrite = "Writer") and
|
||||||
|
(subMod = "File" or subMod = "Dir" or subMod = "Link" or subMod = "Proxy")
|
||||||
|
|
|
||||||
|
result = mod.getAPropertyRead(readOrWrite) or
|
||||||
|
result = mod.getAPropertyRead(readOrWrite).getAPropertyRead(subMod) or
|
||||||
|
result = mod.getAPropertyRead(subMod).getAPropertyRead(readOrWrite)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An invocation of a method defined in the `fstream` library.
|
||||||
|
*/
|
||||||
|
private class FStream extends FileSystemAccess, DataFlow::InvokeNode {
|
||||||
|
FStream() { this = getAnFStreamProperty().getAnInvocation() }
|
||||||
|
|
||||||
|
override DataFlow::Node getAPathArgument() {
|
||||||
|
result = getOptionArgument(0, "path")
|
||||||
|
or
|
||||||
|
not exists(getOptionArgument(0, "path")) and
|
||||||
|
result = getArgument(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A call to the library `write-file-atomic`.
|
||||||
|
*/
|
||||||
|
private class WriteFileAtomic extends FileSystemWriteAccess, DataFlow::CallNode {
|
||||||
|
WriteFileAtomic() {
|
||||||
|
this = DataFlow::moduleImport("write-file-atomic").getACall()
|
||||||
|
or
|
||||||
|
this = DataFlow::moduleMember("write-file-atomic", "sync").getACall()
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
|
||||||
|
|
||||||
|
override DataFlow::Node getADataNode() { result = getArgument(1) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A call to the library `recursive-readdir`.
|
||||||
|
*/
|
||||||
|
private class RecursiveReadDir extends FileSystemAccess, FileNameProducer, DataFlow::CallNode {
|
||||||
|
RecursiveReadDir() { this = DataFlow::moduleImport("recursive-readdir").getACall() }
|
||||||
|
|
||||||
|
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
|
||||||
|
|
||||||
|
override DataFlow::Node getAFileName() { result = getCallback([1 .. 2]).getParameter(1) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Classes and predicates for modelling the `jsonfile` library (https://www.npmjs.com/package/jsonfile).
|
||||||
|
*/
|
||||||
|
private module JSONFile {
|
||||||
|
/**
|
||||||
|
* A reader for JSON files.
|
||||||
|
*/
|
||||||
|
class JSONFileReader extends FileSystemReadAccess, DataFlow::CallNode {
|
||||||
|
JSONFileReader() {
|
||||||
|
this =
|
||||||
|
DataFlow::moduleMember("jsonfile", any(string s | s = "readFile" or s = "readFileSync"))
|
||||||
|
.getACall()
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
|
||||||
|
|
||||||
|
override DataFlow::Node getADataNode() {
|
||||||
|
this.getCalleeName() = "readFile" and result = getCallback([1 .. 2]).getParameter(1)
|
||||||
|
or
|
||||||
|
this.getCalleeName() = "readFileSync" and result = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A writer for JSON files.
|
||||||
|
*/
|
||||||
|
class JSONFileWriter extends FileSystemWriteAccess, DataFlow::CallNode {
|
||||||
|
JSONFileWriter() {
|
||||||
|
this =
|
||||||
|
DataFlow::moduleMember("jsonfile", any(string s | s = "writeFile" or s = "writeFileSync"))
|
||||||
|
.getACall()
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
|
||||||
|
|
||||||
|
override DataFlow::Node getADataNode() { result = getArgument(1) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A file system access made by a NodeJS library.
|
||||||
|
* This class models multiple NodeJS libraries that access files.
|
||||||
|
*/
|
||||||
|
private class LibraryAccess extends FileSystemAccess, DataFlow::InvokeNode {
|
||||||
|
int pathArgument; // The index of the path argument.
|
||||||
|
|
||||||
|
LibraryAccess() {
|
||||||
|
pathArgument = 0 and
|
||||||
|
(
|
||||||
|
this = DataFlow::moduleImport("path-exists").getACall()
|
||||||
|
or
|
||||||
|
this = DataFlow::moduleImport("rimraf").getACall()
|
||||||
|
or
|
||||||
|
this =
|
||||||
|
DataFlow::moduleMember("node-dir",
|
||||||
|
any(string s |
|
||||||
|
s = "readFiles" or
|
||||||
|
s = "readFilesStream" or
|
||||||
|
s = "files" or
|
||||||
|
s = "promiseFiles" or
|
||||||
|
s = "subdirs" or
|
||||||
|
s = "paths"
|
||||||
|
)).getACall()
|
||||||
|
)
|
||||||
|
or
|
||||||
|
pathArgument = 0 and
|
||||||
|
this =
|
||||||
|
DataFlow::moduleMember("vinyl-fs", any(string s | s = "src" or s = "dest" or s = "symlink"))
|
||||||
|
.getACall()
|
||||||
|
or
|
||||||
|
pathArgument = [0 .. 1] and
|
||||||
|
(
|
||||||
|
this = DataFlow::moduleImport("ncp").getACall() or
|
||||||
|
this = DataFlow::moduleMember("ncp", "ncp").getACall()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Node getAPathArgument() { result = getArgument(pathArgument) }
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
getPathArgument
|
||||||
|
| file-access.js:3:1:4:34 | fstream ... file"}) | file-access.js:4:19:4:32 | "path/to/file" |
|
||||||
|
| file-access.js:8:1:9:24 | fstream ... o/dir") | file-access.js:9:11:9:23 | "path/to/dir" |
|
||||||
|
| file-access.js:10:9:10:43 | fstream ... r/dir") | file-access.js:10:24:10:42 | "path/to/other/dir" |
|
||||||
|
| file-access.js:15:1:15:60 | writeFi ... rr) {}) | file-access.js:15:17:15:28 | 'atmoic.txt' |
|
||||||
|
| file-access.js:18:1:18:59 | writeFi ... tions]) | file-access.js:18:21:18:34 | "syncFile.txt" |
|
||||||
|
| file-access.js:22:1:22:48 | recursi ... es) {}) | file-access.js:22:11:22:21 | "some/path" |
|
||||||
|
| file-access.js:25:1:25:59 | jsonfil ... bj) {}) | file-access.js:25:19:25:34 | '/tmp/data.json' |
|
||||||
|
| file-access.js:26:1:26:39 | jsonfil ... .json') | file-access.js:26:23:26:38 | '/tmp/data.json' |
|
||||||
|
| file-access.js:28:1:28:60 | jsonfil ... rr) {}) | file-access.js:28:20:28:35 | '/tmp/data.json' |
|
||||||
|
| file-access.js:29:1:29:45 | jsonfil ... ', obj) | file-access.js:29:24:29:39 | '/tmp/data.json' |
|
||||||
|
| file-access.js:34:4:34:23 | pathExists('foo.js') | file-access.js:34:15:34:22 | 'foo.js' |
|
||||||
|
| file-access.js:39:1:39:28 | rimraf( ... => {}) | file-access.js:39:8:39:10 | "/" |
|
||||||
|
| file-access.js:42:1:42:59 | dir.rea ... on(){}) | file-access.js:42:15:42:31 | "/some/directory" |
|
||||||
|
| file-access.js:46:1:46:25 | vfs.src ... path"]) | file-access.js:46:9:46:24 | ["some", "path"] |
|
||||||
|
| file-access.js:47:1:47:36 | vfs.des ... true }) | file-access.js:47:10:47:13 | './' |
|
||||||
|
| file-access.js:51:1:51:36 | ncp("fr ... rr) {}) | file-access.js:51:5:51:10 | "from" |
|
||||||
|
| file-access.js:51:1:51:36 | ncp("fr ... rr) {}) | file-access.js:51:13:51:16 | "to" |
|
||||||
|
getReadNode
|
||||||
|
| file-access.js:25:1:25:59 | jsonfil ... bj) {}) | file-access.js:25:52:25:54 | obj |
|
||||||
|
| file-access.js:26:1:26:39 | jsonfil ... .json') | file-access.js:26:1:26:39 | jsonfil ... .json') |
|
||||||
|
getWriteNode
|
||||||
|
| file-access.js:15:1:15:60 | writeFi ... rr) {}) | file-access.js:15:31:15:36 | 'Data' |
|
||||||
|
| file-access.js:18:1:18:59 | writeFi ... tions]) | file-access.js:18:37:18:47 | "More data" |
|
||||||
|
| file-access.js:28:1:28:60 | jsonfil ... rr) {}) | file-access.js:28:38:28:40 | obj |
|
||||||
|
| file-access.js:29:1:29:45 | jsonfil ... ', obj) | file-access.js:29:42:29:44 | obj |
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import javascript
|
||||||
|
|
||||||
|
query DataFlow::Node getPathArgument(FileSystemAccess access) { result = access.getAPathArgument() }
|
||||||
|
|
||||||
|
query DataFlow::Node getReadNode(FileSystemReadAccess access) { result = access.getADataNode() }
|
||||||
|
|
||||||
|
query DataFlow::Node getWriteNode(FileSystemWriteAccess access) { result = access.getADataNode() }
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
| file-access.js:22:39:22:43 | files |
|
||||||
| tst-file-names.js:7:1:7:10 | walkSync() |
|
| tst-file-names.js:7:1:7:10 | walkSync() |
|
||||||
| tst-file-names.js:9:35:9:44 | stats.name |
|
| tst-file-names.js:9:35:9:44 | stats.name |
|
||||||
| tst-file-names.js:11:1:11:12 | glob.sync(_) |
|
| tst-file-names.js:11:1:11:12 | glob.sync(_) |
|
||||||
@@ -10,3 +11,7 @@
|
|||||||
| tst-file-names.js:25:18:25:22 | files |
|
| tst-file-names.js:25:18:25:22 | files |
|
||||||
| tst-file-names.js:27:24:27:28 | files |
|
| tst-file-names.js:27:24:27:28 | files |
|
||||||
| tst-file-names.js:29:27:29:30 | file |
|
| tst-file-names.js:29:27:29:30 | file |
|
||||||
|
| tst-file-names.js:32:34:32:38 | files |
|
||||||
|
| tst-file-names.js:34:15:34:29 | await globby(_) |
|
||||||
|
| tst-file-names.js:36:16:36:38 | await f ... sync(_) |
|
||||||
|
| tst-file-names.js:38:16:38:57 | await f ... => {}) |
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
var fstream = require("fstream");
|
||||||
|
|
||||||
|
fstream
|
||||||
|
.Writer({ path: "path/to/file"})
|
||||||
|
.write("hello\n")
|
||||||
|
.end()
|
||||||
|
|
||||||
|
fstream
|
||||||
|
.Reader("path/to/dir")
|
||||||
|
.pipe(fstream.Writer("path/to/other/dir"))
|
||||||
|
|
||||||
|
|
||||||
|
var writeFileAtomic= require("write-file-atomic");
|
||||||
|
|
||||||
|
writeFileAtomic('atmoic.txt', 'Data', {}, function (err) {});
|
||||||
|
|
||||||
|
var writeFileAtomicSync = require('write-file-atomic').sync
|
||||||
|
writeFileAtomicSync("syncFile.txt", "More data", [options])
|
||||||
|
|
||||||
|
var recursive = require("recursive-readdir");
|
||||||
|
|
||||||
|
recursive("some/path", function (err, files) {});
|
||||||
|
|
||||||
|
const jsonfile = require('jsonfile');
|
||||||
|
jsonfile.readFile('/tmp/data.json', function (err, obj) {});
|
||||||
|
jsonfile.readFileSync('/tmp/data.json');
|
||||||
|
|
||||||
|
jsonfile.writeFile('/tmp/data.json', obj, function (err) {});
|
||||||
|
jsonfile.writeFileSync('/tmp/data.json', obj);
|
||||||
|
|
||||||
|
|
||||||
|
const pathExists = require('path-exists');
|
||||||
|
|
||||||
|
if(pathExists('foo.js')) {
|
||||||
|
// do something.
|
||||||
|
}
|
||||||
|
|
||||||
|
var rimraf = require("rimraf");
|
||||||
|
rimraf("/", {}, (err) => {});
|
||||||
|
|
||||||
|
var dir = require("node-dir");
|
||||||
|
dir.readFiles("/some/directory",function() {},function(){});
|
||||||
|
|
||||||
|
var vfs = require("vinyl-fs");
|
||||||
|
|
||||||
|
vfs.src(["some", "path"]);
|
||||||
|
vfs.dest('./', { sourcemaps: true });
|
||||||
|
|
||||||
|
|
||||||
|
var ncp = require('ncp').ncp;
|
||||||
|
ncp("from", "to", function (err) {});
|
||||||
@@ -27,3 +27,13 @@ fastGlob(_).then(files => files);
|
|||||||
fastGlob.async(_).then(files => files);
|
fastGlob.async(_).then(files => files);
|
||||||
|
|
||||||
fastGlob.stream(_).on(_, file => file); // XXX
|
fastGlob.stream(_).on(_, file => file); // XXX
|
||||||
|
|
||||||
|
async function foo() {
|
||||||
|
globby(_).catch(() => {}).then(files => files);
|
||||||
|
|
||||||
|
var files = await globby(_);
|
||||||
|
|
||||||
|
var files2 = await fastGlob.async(_);
|
||||||
|
|
||||||
|
var files2 = await fastGlob.async(_).catch((wat) => {});
|
||||||
|
}
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ nodes
|
|||||||
| tst.js:61:29:61:35 | tainted |
|
| tst.js:61:29:61:35 | tainted |
|
||||||
| tst.js:64:30:64:36 | tainted |
|
| tst.js:64:30:64:36 | tainted |
|
||||||
| tst.js:64:30:64:36 | tainted |
|
| tst.js:64:30:64:36 | tainted |
|
||||||
|
| tst.js:68:30:68:36 | tainted |
|
||||||
|
| tst.js:68:30:68:36 | tainted |
|
||||||
edges
|
edges
|
||||||
| tst.js:14:9:14:52 | tainted | tst.js:18:13:18:19 | tainted |
|
| tst.js:14:9:14:52 | tainted | tst.js:18:13:18:19 | tainted |
|
||||||
| tst.js:14:9:14:52 | tainted | tst.js:18:13:18:19 | tainted |
|
| tst.js:14:9:14:52 | tainted | tst.js:18:13:18:19 | tainted |
|
||||||
@@ -89,6 +91,8 @@ edges
|
|||||||
| tst.js:58:9:58:52 | tainted | tst.js:61:29:61:35 | tainted |
|
| tst.js:58:9:58:52 | tainted | tst.js:61:29:61:35 | tainted |
|
||||||
| tst.js:58:9:58:52 | tainted | tst.js:64:30:64:36 | tainted |
|
| tst.js:58:9:58:52 | tainted | tst.js:64:30:64:36 | tainted |
|
||||||
| tst.js:58:9:58:52 | tainted | tst.js:64:30:64:36 | tainted |
|
| tst.js:58:9:58:52 | tainted | tst.js:64:30:64:36 | tainted |
|
||||||
|
| tst.js:58:9:58:52 | tainted | tst.js:68:30:68:36 | tainted |
|
||||||
|
| tst.js:58:9:58:52 | tainted | tst.js:68:30:68:36 | tainted |
|
||||||
| tst.js:58:19:58:42 | url.par ... , true) | tst.js:58:19:58:48 | url.par ... ).query |
|
| tst.js:58:19:58:42 | url.par ... , true) | tst.js:58:19:58:48 | url.par ... ).query |
|
||||||
| tst.js:58:19:58:48 | url.par ... ).query | tst.js:58:19:58:52 | url.par ... ery.url |
|
| tst.js:58:19:58:48 | url.par ... ).query | tst.js:58:19:58:52 | url.par ... ery.url |
|
||||||
| tst.js:58:19:58:52 | url.par ... ery.url | tst.js:58:9:58:52 | tainted |
|
| tst.js:58:19:58:52 | url.par ... ery.url | tst.js:58:9:58:52 | tainted |
|
||||||
@@ -109,3 +113,4 @@ edges
|
|||||||
| tst.js:45:5:45:57 | request ... ainted) | tst.js:14:29:14:35 | req.url | tst.js:45:13:45:56 | 'http:/ ... tainted | The $@ of this request depends on $@. | tst.js:45:13:45:56 | 'http:/ ... tainted | URL | tst.js:14:29:14:35 | req.url | a user-provided value |
|
| tst.js:45:5:45:57 | request ... ainted) | tst.js:14:29:14:35 | req.url | tst.js:45:13:45:56 | 'http:/ ... tainted | The $@ of this request depends on $@. | tst.js:45:13:45:56 | 'http:/ ... tainted | URL | tst.js:14:29:14:35 | req.url | a user-provided value |
|
||||||
| tst.js:61:2:61:37 | client. ... inted}) | tst.js:58:29:58:35 | req.url | tst.js:61:29:61:35 | tainted | The $@ of this request depends on $@. | tst.js:61:29:61:35 | tainted | URL | tst.js:58:29:58:35 | req.url | a user-provided value |
|
| tst.js:61:2:61:37 | client. ... inted}) | tst.js:58:29:58:35 | req.url | tst.js:61:29:61:35 | tainted | The $@ of this request depends on $@. | tst.js:61:29:61:35 | tainted | URL | tst.js:58:29:58:35 | req.url | a user-provided value |
|
||||||
| tst.js:64:3:64:38 | client. ... inted}) | tst.js:58:29:58:35 | req.url | tst.js:64:30:64:36 | tainted | The $@ of this request depends on $@. | tst.js:64:30:64:36 | tainted | URL | tst.js:58:29:58:35 | req.url | a user-provided value |
|
| tst.js:64:3:64:38 | client. ... inted}) | tst.js:58:29:58:35 | req.url | tst.js:64:30:64:36 | tainted | The $@ of this request depends on $@. | tst.js:64:30:64:36 | tainted | URL | tst.js:58:29:58:35 | req.url | a user-provided value |
|
||||||
|
| tst.js:68:3:68:38 | client. ... inted}) | tst.js:58:29:58:35 | req.url | tst.js:68:30:68:36 | tainted | The $@ of this request depends on $@. | tst.js:68:30:68:36 | tainted | URL | tst.js:58:29:58:35 | req.url | a user-provided value |
|
||||||
|
|||||||
@@ -60,6 +60,10 @@ var server = http.createServer(async function(req, res) {
|
|||||||
var client = await CDP(options);
|
var client = await CDP(options);
|
||||||
client.Page.navigate({url: tainted}); // NOT OK.
|
client.Page.navigate({url: tainted}); // NOT OK.
|
||||||
|
|
||||||
|
CDP(options).catch((ignored) => {}).then((client) => {
|
||||||
|
client.Page.navigate({url: tainted}); // NOT OK.
|
||||||
|
})
|
||||||
|
|
||||||
CDP(options, (client) => {
|
CDP(options, (client) => {
|
||||||
client.Page.navigate({url: tainted}); // NOT OK.
|
client.Page.navigate({url: tainted}); // NOT OK.
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,18 +13,18 @@
|
|||||||
|
|
||||||
import python
|
import python
|
||||||
|
|
||||||
from ExceptFlowNode ex, Object t, ClassObject c, ControlFlowNode origin, string what
|
from ExceptFlowNode ex, Value t, ClassValue c, ControlFlowNode origin, string what
|
||||||
where ex.handledException(t, c, origin) and
|
where
|
||||||
(
|
ex.handledException(t, c, origin) and
|
||||||
exists(ClassObject x | x = t |
|
(
|
||||||
|
exists(ClassValue x | x = t |
|
||||||
not x.isLegalExceptionType() and
|
not x.isLegalExceptionType() and
|
||||||
not x.failedInference() and
|
not x.failedInference(_) and
|
||||||
what = "class '" + x.getName() + "'"
|
what = "class '" + x.getName() + "'"
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
not t instanceof ClassObject and
|
not t instanceof ClassValue and
|
||||||
what = "instance of '" + c.getName() + "'"
|
what = "instance of '" + c.getName() + "'"
|
||||||
)
|
)
|
||||||
|
select ex.getNode(),
|
||||||
select ex.getNode(), "Non-exception $@ in exception handler which will never match raised exception.", origin, what
|
"Non-exception $@ in exception handler which will never match raised exception.", origin, what
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,12 @@ class Value extends TObject {
|
|||||||
PointsToInternal::pointsTo(result, _, this, _)
|
PointsToInternal::pointsTo(result, _, this, _)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Gets the origin CFG node for this value. */
|
||||||
|
ControlFlowNode getOrigin() {
|
||||||
|
result = this.(ObjectInternal).getOrigin()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Gets the class of this object.
|
/** Gets the class of this object.
|
||||||
* Strictly, the `Value` representing the class of the objects
|
* Strictly, the `Value` representing the class of the objects
|
||||||
* represented by this Value.
|
* represented by this Value.
|
||||||
|
|||||||
@@ -260,35 +260,58 @@ class ExceptFlowNode extends ControlFlowNode {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate handledObject(Object obj, ClassObject cls, ControlFlowNode origin) {
|
private predicate handledObject_objectapi(Object obj, ClassObject cls, ControlFlowNode origin) {
|
||||||
this.getType().refersTo(obj, cls, origin)
|
this.getType().refersTo(obj, cls, origin)
|
||||||
or
|
or
|
||||||
exists(Object tup |
|
exists(Object tup |
|
||||||
this.handledObject(tup, theTupleType(), _) |
|
this.handledObject_objectapi(tup, theTupleType(), _) |
|
||||||
element_from_tuple(tup).refersTo(obj, cls, origin)
|
element_from_tuple_objectapi(tup).refersTo(obj, cls, origin)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private predicate handledObject(Value val, ClassValue cls, ControlFlowNode origin) {
|
||||||
|
val.getClass() = cls and
|
||||||
|
(
|
||||||
|
this.getType().pointsTo(val, origin)
|
||||||
|
or
|
||||||
|
exists(TupleValue tup |
|
||||||
|
this.handledObject(tup, ClassValue::tuple(), _) |
|
||||||
|
val = tup.getItem(_) and origin = val.getOrigin()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the inferred type(s) that are handled by this node, splitting tuples if possible. */
|
/** Gets the inferred type(s) that are handled by this node, splitting tuples if possible. */
|
||||||
pragma [noinline]
|
pragma [noinline]
|
||||||
predicate handledException(Object obj, ClassObject cls, ControlFlowNode origin) {
|
predicate handledException_objectapi(Object obj, ClassObject cls, ControlFlowNode origin) {
|
||||||
this.handledObject(obj, cls, origin) and not cls = theTupleType()
|
this.handledObject_objectapi(obj, cls, origin) and not cls = theTupleType()
|
||||||
or
|
or
|
||||||
not exists(this.getNode().(ExceptStmt).getType()) and obj = theBaseExceptionType() and cls = theTypeType() and
|
not exists(this.getNode().(ExceptStmt).getType()) and obj = theBaseExceptionType() and cls = theTypeType() and
|
||||||
origin = this
|
origin = this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Gets the inferred type(s) that are handled by this node, splitting tuples if possible. */
|
||||||
|
pragma [noinline]
|
||||||
|
predicate handledException(Value val, ClassValue cls, ControlFlowNode origin) {
|
||||||
|
this.handledObject(val, cls, origin) and not cls = ClassValue::tuple()
|
||||||
|
or
|
||||||
|
not exists(this.getNode().(ExceptStmt).getType()) and val = ClassValue::baseException() and cls = ClassValue::type() and
|
||||||
|
origin = this
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Whether this `except` handles `cls` */
|
/** Whether this `except` handles `cls` */
|
||||||
predicate handles(ClassObject cls) {
|
predicate handles(ClassObject cls) {
|
||||||
exists(ClassObject handled |
|
exists(ClassObject handled |
|
||||||
this.handledException(handled, _, _) |
|
this.handledException_objectapi(handled, _, _) |
|
||||||
cls.getAnImproperSuperType() = handled
|
cls.getAnImproperSuperType() = handled
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ControlFlowNode element_from_tuple(Object tuple) {
|
private ControlFlowNode element_from_tuple_objectapi(Object tuple) {
|
||||||
exists(Tuple t |
|
exists(Tuple t |
|
||||||
t = tuple.getOrigin() and result = t.getAnElt().getAFlowNode()
|
t = tuple.getOrigin() and result = t.getAnElt().getAFlowNode()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
import python
|
import python
|
||||||
|
|
||||||
from ExceptFlowNode ex, Object obj
|
from ExceptFlowNode ex, Value val
|
||||||
where ex.handledException(obj, _, _)
|
where ex.handledException(val, _, _)
|
||||||
select ex.getLocation().getStartLine(), ex.toString(), obj.toString()
|
select ex.getLocation().getStartLine(), ex.toString(), val.toString()
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
import python
|
import python
|
||||||
|
|
||||||
from ExceptFlowNode ex, Object obj
|
from ExceptFlowNode ex, Value val
|
||||||
where ex.handledException(obj, _, _)
|
where ex.handledException(val, _, _)
|
||||||
select ex.getLocation().getStartLine(), ex.toString(), obj.toString()
|
select ex.getLocation().getStartLine(), ex.toString(), val.toString()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import python
|
import python
|
||||||
|
|
||||||
from ExceptFlowNode ex, Object t
|
from ExceptFlowNode ex, Value val
|
||||||
where ex.handledException(t, _, _)
|
where ex.handledException(val, _, _)
|
||||||
select ex.getLocation().getStartLine(), ex.toString(), t.toString()
|
select ex.getLocation().getStartLine(), ex.toString(), val.toString()
|
||||||
Reference in New Issue
Block a user