Merge remote-tracking branch 'upstream/master' into dbartol/VarArgIR

This commit is contained in:
Dave Bartolomeo
2020-03-18 09:52:21 -04:00
67 changed files with 1984 additions and 466 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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) {});

View File

@@ -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) => {});
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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