Merge branch 'main' of github.com:github/codeql into SharedDataflow_NestedComprehensions

This commit is contained in:
Rasmus Lerchedahl Petersen
2020-09-10 10:20:55 +02:00
1108 changed files with 45281 additions and 16261 deletions

View File

@@ -4,6 +4,6 @@
"slevesque.vscode-zipexplorer"
],
"settings": {
"codeQL.experimentalBqrsParsing": true
"codeQL.runningQueries.memory": 2048
}
}

11
.github/workflows/labeler.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
name: "Pull Request Labeler"
on:
- pull_request_target
jobs:
triage:
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v2
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"

View File

@@ -9,7 +9,7 @@ You can use the [interactive query console](https://lgtm.com/help/lgtm/using-que
## Contributing
We welcome contributions to our standard library and standard checks. Do you have an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request! Before you do, though, please take the time to read our [contributing guidelines](CONTRIBUTING.md). You can also consult our [style guides](https://github.com/github/codeql/tree/master/docs) to learn how to format your code for consistency and clarity, how to write query metadata, and how to write query help documentation for your query.
We welcome contributions to our standard library and standard checks. Do you have an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request! Before you do, though, please take the time to read our [contributing guidelines](CONTRIBUTING.md). You can also consult our [style guides](https://github.com/github/codeql/tree/main/docs) to learn how to format your code for consistency and clarity, how to write query metadata, and how to write query help documentation for your query.
## License

View File

@@ -30,7 +30,7 @@
- [yargs](https://www.npmjs.com/package/yargs)
- [webpack-dev-server](https://www.npmjs.com/package/webpack-dev-server)
* TypeScript 3.9 is now supported.
* TypeScript 4.0 is now supported.
* TypeScript code embedded in HTML and Vue files is now extracted and analyzed.

View File

@@ -13,13 +13,16 @@ The following changes in version 1.26 affect C/C++ analysis in all applications.
| **Query** | **Expected impact** | **Change** |
|----------------------------|------------------------|------------------------------------------------------------------|
| Declaration hides parameter (`cpp/declaration-hides-parameter`) | Fewer false positive results | False positives involving template functions have been fixed. |
| Inconsistent direction of for loop (`cpp/inconsistent-loop-direction`) | Fewer false positive results | The query now accounts for intentional wrapping of an unsigned loop counter. |
| Overflow in uncontrolled allocation size (`cpp/uncontrolled-allocation-size`) | | The precision of this query has been decreased from "high" to "medium". As a result, the query is still run but results are no longer displayed on LGTM by default. |
| Comparison result is always the same (`cpp/constant-comparison`) | More correct results | Bounds on expressions involving multiplication can now be determined in more cases. |
## Changes to libraries
* The models library now models some taint flows through `std::array`, `std::vector`, `std::deque`, `std::list` and `std::forward_list`.
* The QL class `Block`, denoting the `{ ... }` statement, is renamed to `BlockStmt`.
* The models library now models many taint flows through `std::array`, `std::vector`, `std::deque`, `std::list` and `std::forward_list`.
* The models library now models many more taint flows through `std::string`.
* The models library now models some taint flows through `std::ostream`.
* The `SimpleRangeAnalysis` library now supports multiplications of the form
`e1 * e2` and `x *= e2` when `e1` and `e2` are unsigned or constant.

View File

@@ -19,6 +19,12 @@ The following changes in version 1.26 affect C# analysis in all applications.
## Changes to code extraction
* Partial method bodies are extracted. Previously, partial method bodies were skipped completely.
* Inferring the lengths of implicitely sized arrays is fixed. Previously, multidimensional arrays were always extracted with the same length for
each dimension. With the fix, the array sizes `2` and `1` are extracted for `new int[,]{{1},{2}}`. Previously `2` and `2` were extracted.
* The extractor is now assembly-insensitive by default. This means that two entities with the same
fully-qualified name are now mapped to the same entity in the resulting database, regardless of
whether they belong to different assemblies. Assembly sensitivity can be reenabled by passing
`--assemblysensitivetrap` to the extractor.
## Changes to libraries

View File

@@ -0,0 +1,21 @@
# Improvements to Java analysis
The following changes in version 1.26 affect Java analysis in all applications.
## General improvements
## New queries
| **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------|
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
|------------------------------|------------------------|-----------------------------------|
## Changes to libraries
* The QL class `Block`, denoting the `{ ... }` statement, is renamed to `BlockStmt`.

View File

@@ -26,7 +26,13 @@
| **Query** | **Expected impact** | **Change** |
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
| Potentially unsafe external link (`js/unsafe-external-link`) | Fewer results | This query no longer flags URLs constructed using a template system where only the hash or query part of the URL is dynamic. |
| Incomplete URL substring sanitization (`js/incomplete-url-substring-sanitization`) | More results | This query now recognizes additional URLs when the substring check is an inclusion check. |
| Ambiguous HTML id attribute (`js/duplicate-html-id`) | Results no longer shown | Precision tag reduced to "low". The query is no longer run by default. |
| Unused loop iteration variable (`js/unused-loop-variable`) | Fewer results | This query no longer flags variables in a destructuring array assignment that are not the last variable in the destructed array. |
| Unsafe shell command constructed from library input (`js/shell-command-constructed-from-input`) | More results | This query now recognizes more commands where colon, dash, and underscore are used. |
| Unsafe jQuery plugin (`js/unsafe-jquery-plugin`) | More results | This query now detects more unsafe uses of nested option properties. |
## Changes to libraries
* The predicate `TypeAnnotation.hasQualifiedName` now works in more cases when the imported library was not present during extraction.

View File

@@ -0,0 +1,22 @@
# Improvements to Python analysis
The following changes in version 1.26 affect Python analysis in all applications.
## General improvements
## New queries
| **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------|
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
|----------------------------|------------------------|------------------------------------------------------------------|
## Changes to libraries
* Added taint tracking support for string formatting through f-strings.

View File

@@ -325,11 +325,60 @@
"csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll"
],
"Inline Test Expectations": [
"cpp/ql/test/TestUtilities/InlineExpectationsTest.qll",
"python/ql/test/TestUtilities/InlineExpectationsTest.qll"
],
"XML": [
"cpp/ql/src/semmle/code/cpp/XML.qll",
"csharp/ql/src/semmle/code/csharp/XML.qll",
"java/ql/src/semmle/code/xml/XML.qll",
"javascript/ql/src/semmle/javascript/XML.qll",
"python/ql/src/semmle/python/xml/XML.qll"
],
"DuplicationProblems.qhelp": [
"cpp/ql/src/Metrics/Files/DuplicationProblems.qhelp",
"csharp/ql/src/Metrics/Files/DuplicationProblems.qhelp",
"javascript/ql/src/Metrics/DuplicationProblems.qhelp",
"python/ql/src/Metrics/DuplicationProblems.qhelp"
],
"CommentedOutCodeQuery.qhelp": [
"cpp/ql/src/Documentation/CommentedOutCodeQuery.qhelp",
"python/ql/src/Lexical/CommentedOutCodeQuery.qhelp",
"csharp/ql/src/Bad Practices/Comments/CommentedOutCodeQuery.qhelp",
"java/ql/src/Violations of Best Practice/Comments/CommentedOutCodeQuery.qhelp",
"javascript/ql/src/Comments/CommentedOutCodeQuery.qhelp"
],
"FLinesOfCodeReferences.qhelp": [
"java/ql/src/Metrics/Files/FLinesOfCodeReferences.qhelp",
"javascript/ql/src/Metrics/FLinesOfCodeReferences.qhelp"
],
"FCommentRatioCommon.qhelp": [
"java/ql/src/Metrics/Files/FCommentRatioCommon.qhelp",
"javascript/ql/src/Metrics/FCommentRatioCommon.qhelp"
],
"FLinesOfCodeOverview.qhelp": [
"java/ql/src/Metrics/Files/FLinesOfCodeOverview.qhelp",
"javascript/ql/src/Metrics/FLinesOfCodeOverview.qhelp"
],
"CommentedOutCodeMetricOverview.qhelp": [
"cpp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp",
"csharp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp",
"java/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp",
"javascript/ql/src/Comments/CommentedOutCodeMetricOverview.qhelp",
"python/ql/src/Lexical/CommentedOutCodeMetricOverview.qhelp"
],
"FLinesOfDuplicatedCodeCommon.qhelp": [
"cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.qhelp",
"java/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.qhelp",
"javascript/ql/src/Metrics/FLinesOfDuplicatedCodeCommon.qhelp",
"python/ql/src/Metrics/FLinesOfDuplicatedCodeCommon.qhelp"
],
"CommentedOutCodeReferences.qhelp": [
"cpp/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp",
"csharp/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp",
"java/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp",
"javascript/ql/src/Comments/CommentedOutCodeReferences.qhelp",
"python/ql/src/Lexical/CommentedOutCodeReferences.qhelp"
]
}

View File

@@ -9,6 +9,6 @@
import cpp
from Block blk
from BlockStmt blk
where blk.getNumStmt() = 0
select blk

View File

@@ -13,5 +13,5 @@
import cpp
from IfStmt i
where i.getThen().(Block).getNumStmt() = 0
where i.getThen().(BlockStmt).getNumStmt() = 0
select i

View File

@@ -8,6 +8,6 @@
import cpp
from Block b
from BlockStmt b
where b.getNumStmt() = 1
select b

View File

@@ -14,7 +14,7 @@ import cpp
class ComplexStmt extends Stmt {
ComplexStmt() {
exists(Block body |
exists(BlockStmt body |
body = this.(Loop).getStmt() or
body = this.(SwitchStmt).getStmt()
|
@@ -24,7 +24,7 @@ class ComplexStmt extends Stmt {
}
}
from Block b, int n, ComplexStmt complexStmt
from BlockStmt b, int n, ComplexStmt complexStmt
where
n = strictcount(ComplexStmt s | s = b.getAStmt()) and
n > 3 and

View File

@@ -11,6 +11,17 @@
import cpp
/**
* Gets the template that a function `f` is constructed from, or just `f` if it
* is not from a template instantiation.
*/
Function getConstructedFrom(Function f) {
f.isConstructedFrom(result)
or
not f.isConstructedFrom(_) and
result = f
}
/**
* Gets the parameter of `f` with name `name`, which has to come from the
* _definition_ of `f` and not a prototype declaration.
@@ -18,13 +29,17 @@ import cpp
* This should not happen in a single application but since we
* have a system wide view it is likely to happen for instance for
* the main function.
*
* Note: we use `getConstructedFrom` to ensure that we look at template
* functions rather than their instantiations. We get better results this way
* as the instantiation is artificial and may have inherited parameter names
* from the declaration rather than the definition.
*/
ParameterDeclarationEntry functionParameterNames(Function f, string name) {
exists(FunctionDeclarationEntry fe |
result.getFunctionDeclarationEntry() = fe and
fe.getFunction() = f and
getConstructedFrom(f).getDefinition() = fe and
fe.getLocation() = f.getDefinitionLocation() and
result.getFile() = fe.getFile() and // Work around CPP-331
strictcount(f.getDefinitionLocation()) = 1 and
result.getName() = name
)

View File

@@ -17,7 +17,7 @@ where
shadowing(lv1, lv2) and
not lv1.isCompilerGenerated() and
not lv2.isCompilerGenerated() and
not lv1.getParentScope().(Block).isInMacroExpansion() and
not lv2.getParentScope().(Block).isInMacroExpansion()
not lv1.getParentScope().(BlockStmt).isInMacroExpansion() and
not lv2.getParentScope().(BlockStmt).isInMacroExpansion()
select lv1, "Variable " + lv1.getName() + " hides another variable of the same name (on $@).", lv2,
"line " + lv2.getLocation().getStartLine().toString()

View File

@@ -14,7 +14,7 @@
import cpp
predicate emptyBlock(ControlStructure s, Block b) {
predicate emptyBlock(ControlStructure s, BlockStmt b) {
b = s.getAChild() and
not exists(b.getAChild()) and
not b.isInMacroExpansion() and
@@ -23,7 +23,7 @@ predicate emptyBlock(ControlStructure s, Block b) {
class AffectedFile extends File {
AffectedFile() {
exists(Block b |
exists(BlockStmt b |
emptyBlock(_, b) and
this = b.getFile()
)
@@ -37,7 +37,7 @@ class AffectedFile extends File {
class BlockOrNonChild extends Element {
BlockOrNonChild() {
(
this instanceof Block
this instanceof BlockStmt
or
this instanceof Comment
or
@@ -78,7 +78,7 @@ class BlockOrNonChild extends Element {
/**
* A block that contains a non-child element.
*/
predicate emptyBlockContainsNonchild(Block b) {
predicate emptyBlockContainsNonchild(BlockStmt b) {
emptyBlock(_, b) and
exists(BlockOrNonChild c, AffectedFile file |
c.(BlockOrNonChild).getStartRankIn(file) = 1 + b.(BlockOrNonChild).getStartRankIn(file) and
@@ -91,7 +91,7 @@ predicate emptyBlockContainsNonchild(Block b) {
* A block that is entirely on one line, which also contains a comment. Chances
* are the comment is intended to refer to the block.
*/
predicate lineComment(Block b) {
predicate lineComment(BlockStmt b) {
emptyBlock(_, b) and
exists(Location bLocation, File f, int line |
bLocation = b.getLocation() and
@@ -106,7 +106,7 @@ predicate lineComment(Block b) {
)
}
from ControlStructure s, Block eb
from ControlStructure s, BlockStmt eb
where
emptyBlock(s, eb) and
not emptyBlockContainsNonchild(eb) and

View File

@@ -12,7 +12,7 @@
import cpp
import semmle.code.cpp.commons.Exclusions
Stmt getNextRealStmt(Block b, int i) {
Stmt getNextRealStmt(BlockStmt b, int i) {
result = b.getStmt(i + 1) and
not result instanceof EmptyStmt
or
@@ -20,7 +20,7 @@ Stmt getNextRealStmt(Block b, int i) {
result = getNextRealStmt(b, i + 1)
}
from JumpStmt js, Block b, int i, Stmt s
from JumpStmt js, BlockStmt b, int i, Stmt s
where
b.getStmt(i) = js and
s = getNextRealStmt(b, i) and

View File

@@ -0,0 +1,11 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<fragment>
<warning>
This check is an approximation, so some results may not be actual defects in the program.
It is not possible in general to compute the exact value of the variable without running the program with all possible input data.
</warning>
</fragment>
</qhelp>

View File

@@ -0,0 +1,12 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<fragment>
<warning>
This check is an approximation, so some results may not be actual defects in the program.
It is not possible in general to compute which function is actually called in a virtual call,
or a call through a pointer, without running the program with all possible input data.
</warning>
</fragment>
</qhelp>

View File

@@ -0,0 +1,13 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<fragment>
<warning>
This check is an approximation, so some results may not be actual defects in the program.
It is not possible in general to compute the actual branch taken in conditional statements such
as "if" without running the program with all possible input data. This means that it is not possible
to determine if a particular statement is going to be executed.
</warning>
</fragment>
</qhelp>

View File

@@ -0,0 +1,11 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<fragment>
<warning>
This check is an approximation, so some results may not be actual defects in the program. It is not possible
in general to compute the values of pointers without running the program with all input data.
</warning>
</fragment>
</qhelp>

View File

@@ -3,5 +3,5 @@
"qhelp.dtd">
<qhelp>
<include src="CommentedOutCodeQuery.qhelp" />
<include src="CommentedOutCodeReferences.qhelp" />
<include src="../Metrics/Files/CommentedOutCodeReferences.qhelp" />
</qhelp>

View File

@@ -0,0 +1,25 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Commented-out code is distracting and confusing for developers who read the surrounding code,
and its significance is often unclear. It will not get compiled or tested when the code around
it changes, so it's likely to break over time. For these reasons, commented-out code should be
avoided.
</p>
</overview>
<recommendation>
<p>
Remove or reinstate the commented-out code. If you want to include a snippet of example code
in a comment, consider enclosing it in quotes or marking it up as appropriate for the source
language.
</p>
</recommendation>
</qhelp>

View File

@@ -12,7 +12,7 @@
import cpp
int lineInBlock(File f) {
exists(Block block, Location blockLocation |
exists(BlockStmt block, Location blockLocation |
block.getFile() = f and blockLocation = block.getLocation()
|
result in [blockLocation.getStartLine() .. blockLocation.getEndLine()]

View File

@@ -27,11 +27,11 @@ predicate macroUseLocation(File f, int start, int end) {
}
pragma[noopt]
predicate emptyIf(IfStmt s, Block b, File f, int start, int end) {
predicate emptyIf(IfStmt s, BlockStmt b, File f, int start, int end) {
s instanceof IfStmt and
not exists(s.getElse()) and
b = s.getThen() and
b instanceof Block and
b instanceof BlockStmt and
not exists(b.getAChild()) and
f = b.getFile() and
exists(Location l |
@@ -42,7 +42,7 @@ predicate emptyIf(IfStmt s, Block b, File f, int start, int end) {
}
pragma[noopt]
predicate query(IfStmt s, Block b) {
predicate query(IfStmt s, BlockStmt b) {
exists(File f, int blockStart, int blockEnd |
emptyIf(s, b, f, blockStart, blockEnd) and
not exists(int macroStart, int macroEnd |
@@ -53,7 +53,7 @@ predicate query(IfStmt s, Block b) {
)
}
from IfStmt s, Block b
from IfStmt s, BlockStmt b
where
query(s, b) and
not b.isInMacroExpansion()

View File

@@ -0,0 +1,12 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
This metric counts the number of lines of commented-out code in each file. Large amounts of
commented-out code often indicate poorly maintained code.
</p>
</overview>
</qhelp>

View File

@@ -0,0 +1,12 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<references>
<li>Mark Needham: <a href="http://www.markhneedham.com/blog/2009/01/17/the-danger-of-commenting-out-code/">The danger of commenting out code</a>.</li>
<li>Los Techies: <a href="http://lostechies.com/rodpaddock/2010/12/29/commented-code-technical-debt">Commented Code == Technical Debt</a>.</li>
<li>High Integrity C++ Coding Standard: <a href="http://www.codingstandard.com/rule/2-3-2-do-not-comment-out-code/">2.3.2 Do not comment out code</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,16 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Duplicated code increases overall code size, making the code base
harder to maintain and harder to understand. It also becomes harder to fix bugs,
since a programmer applying a fix to one copy has to always remember to update
other copies accordingly. Finally, code duplication is generally an indication of
a poorly designed or hastily written code base, which typically suffers from other
problems as well.
</p>
</overview>
</qhelp>

View File

@@ -0,0 +1,35 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
This metric measures the number of lines in a file that are contained within a block that is duplicated elsewhere. These lines may include code, comments and whitespace, and the duplicate block may be in this file or in another file.
</p>
<p>
A file that contains many lines that are duplicated within the code base is problematic
for a number of reasons.
</p>
</overview>
<include src="DuplicationProblems.qhelp" />
<recommendation>
<p>
Refactor files with lots of duplicated code to extract the common code into
a shared library or module.
</p>
</recommendation>
<references>
<li>Wikipedia: <a href="http://en.wikipedia.org/wiki/Duplicate_code">Duplicate code</a>.</li>
<li>M. Fowler, <em>Refactoring</em>. Addison-Wesley, 1999.</li>
</references>
</qhelp>

View File

@@ -27,7 +27,7 @@ int logicalLength(FunctionDeclarationEntry f) {
count(Stmt s |
s.getEnclosingFunction() = f.getFunction() and
s.getFile() = f.getFile() and
not s instanceof Block and
not s instanceof BlockStmt and
not s instanceof EmptyStmt and
not exists(ForStmt for | s = for.getInitialization()) and
not s.isAffectedByMacro()

View File

@@ -14,7 +14,7 @@ import cpp
class OneLineStmt extends Stmt {
OneLineStmt() {
this.getLocation().getStartLine() = this.getLocation().getEndLine() and
not this instanceof Block and
not this instanceof BlockStmt and
not exists(ForStmt for | this = for.getInitialization()) and
(
// Either this statement is not touched by a macro at all...

View File

@@ -27,7 +27,7 @@ int logicalLength(FunctionDeclarationEntry f) {
count(Stmt s |
s.getEnclosingFunction() = f.getFunction() and
s.getFile() = f.getFile() and
not s instanceof Block and
not s instanceof BlockStmt and
not s instanceof EmptyStmt and
not exists(ForStmt for | s = for.getInitialization()) and
not s.isAffectedByMacro()

View File

@@ -0,0 +1,11 @@
void test(char *arg1, int *arg2) {
if (arg1[0] == 'A') {
if (arg2 != NULL) { //maybe redundant
*arg2 = 42;
}
}
if (arg1[1] == 'B')
{
*arg2 = 54; //dereferenced without checking first
}
}

View File

@@ -0,0 +1,29 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This rule finds comparisons of a function parameter to null that occur when in another path the parameter is dereferenced without a guard check. It's
likely either the check is not required and can be removed, or it should be added before the dereference
so that a null pointer dereference does not occur.</p>
</overview>
<recommendation>
<p>A check should be added to before the dereference, in a way that prevents a null pointer value from
being dereferenced. If it's clear that the pointer cannot be null, consider removing the check instead.</p>
</recommendation>
<example>
<sample src="RedundantNullCheckParam.cpp" />
</example>
<references>
<li>
<a href="https://www.owasp.org/index.php/Null_Dereference">
Null Dereference
</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,56 @@
/**
* @name Redundant null check or missing null check of parameter
* @description Checking a parameter for nullness in one path,
* and not in another is likely to be a sign that either
* the check can be removed, or added in the other case.
* @kind problem
* @id cpp/redundant-null-check-param
* @problem.severity recommendation
* @tags reliability
* security
* external/cwe/cwe-476
*/
import cpp
predicate blockDominates(BlockStmt check, BlockStmt access) {
check.getLocation().getStartLine() <= access.getLocation().getStartLine() and
check.getLocation().getEndLine() >= access.getLocation().getEndLine()
}
predicate isCheckedInstruction(VariableAccess unchecked, VariableAccess checked) {
checked = any(VariableAccess va | va.getTarget() = unchecked.getTarget()) and
//Simple test if the first access in this code path is dereferenced
not dereferenced(checked) and
blockDominates(checked.getEnclosingBlock(), unchecked.getEnclosingBlock())
}
predicate candidateResultUnchecked(VariableAccess unchecked) {
not isCheckedInstruction(unchecked, _)
}
predicate candidateResultChecked(VariableAccess check, EqualityOperation eqop) {
//not dereferenced to check against pointer, not its pointed value
not dereferenced(check) and
//assert macros are not taken into account
not check.isInMacroExpansion() and
// is part of a comparison against some constant NULL
eqop.getAnOperand() = check and
eqop.getAnOperand() instanceof NullValue
}
from VariableAccess unchecked, VariableAccess check, EqualityOperation eqop, Parameter param
where
// a dereference
dereferenced(unchecked) and
// for a function parameter
unchecked.getTarget() = param and
// this function parameter is not overwritten
count(param.getAnAssignment()) = 0 and
check.getTarget() = param and
// which is once checked
candidateResultChecked(check, eqop) and
// and which has not been checked before in this code path
candidateResultUnchecked(unchecked)
select check, "This null check is redundant or there is a missing null check before $@ ", unchecked,
"where dereferencing happens"

View File

@@ -117,7 +117,7 @@ private predicate blockCoversStatement(int equivClass, int first, int last, Stmt
private Stmt statementInMethod(FunctionDeclarationEntry m) {
result.getParent+() = m.getBlock() and
not result.getLocation() instanceof UnknownStmtLocation and
not result instanceof Block
not result instanceof BlockStmt
}
private predicate duplicateStatement(

View File

@@ -7,7 +7,7 @@
<overview>
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
<include src="cpp/jsfNote.qhelp" />
<include src="../jsfNote.qhelp" />
<p>
This query highlights calls to the standard library functions <code>abort, exit, getenv</code> and <code>system</code>.

View File

@@ -13,7 +13,7 @@ import cpp
from Stmt parent, Stmt child
where
not child instanceof Block and
not child instanceof BlockStmt and
(
child = parent.(IfStmt).getThen()
or

View File

@@ -45,6 +45,16 @@ predicate dereferenceThis(Expr e) {
or
// `*this = ...` (where `=` is not overloaded, so an `AssignExpr`)
dereferenceThis(e.(AssignExpr).getLValue())
or
// `e ? ... : ... `
exists(ConditionalExpr cond |
cond = e and
dereferenceThis(cond.getThen()) and
dereferenceThis(cond.getElse())
)
or
// `..., ... `
dereferenceThis(e.(CommaExpr).getRightOperand())
}
/**

View File

@@ -7,7 +7,7 @@
<overview>
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
<include src="cpp/jsfNote.qhelp" />
<include src="../jsfNote.qhelp" />
<p>
This query ensures that all operators with opposites (e.g. == and !=) are both defined, and

View File

@@ -28,7 +28,7 @@ predicate oppositeOperators(string op1, string op2) {
* `!op2(_, _)`.
*/
predicate implementedAsNegationOf(Operator op1, Operator op2) {
exists(Block b, ReturnStmt r, NotExpr n, Expr o |
exists(BlockStmt b, ReturnStmt r, NotExpr n, Expr o |
b = op1.getBlock() and
b.getNumStmt() = 1 and
r = b.getStmt(0) and

View File

@@ -7,7 +7,7 @@
<overview>
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
<include src="cpp/jsfNote.qhelp" />
<include src="../jsfNote.qhelp" />
<p>
This query highlights return statements that return pointers to an object allocated on the stack. The lifetime
@@ -18,7 +18,7 @@ memory after the function has already returned will have undefined results.
<!-- Mention how the results could be probabilistic (uses pointsto) -->
<include src="pointsToWarning.qhelp" />
<include src="../../Critical/pointsToWarning.qhelp" />
</overview>
<recommendation>

View File

@@ -12,7 +12,7 @@ calling convention for x86, it would be whatever value was in the AX/EAX registe
assuming the function had a non-float return type that can fit in a machine word.
</p>
<include src="dataFlowWarning.qhelp" />
<include src="../../Critical/dataFlowWarning.qhelp" />
<!--/*FALSEPOSITIVE_WARNING*/-->

View File

@@ -7,7 +7,7 @@
<overview>
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
<include src="cpp/jsfNote.qhelp" />
<include src="../jsfNote.qhelp" />
<p>
This query highlights identifiers in an inner scope that hide (have the same name as) an identifier in an outer scope.

View File

@@ -29,7 +29,7 @@ predicate localShadowsParameter(LocalVariable lv, Parameter p) {
from Variable v, Variable shadowed
where
not v.getParentScope().(Block).isInMacroExpansion() and
not v.getParentScope().(BlockStmt).isInMacroExpansion() and
(
v.(LocalVariableOrParameter).shadowsGlobal(shadowed.(GlobalVariable)) or
localShadowsParameter(v, shadowed) or

View File

@@ -7,7 +7,7 @@
<overview>
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
<include src="cpp/jsfNote.qhelp" />
<include src="../jsfNote.qhelp" />
<p>
This query highlights variables with the <code>register</code> storage class specifier. Modern compilers are now capable of

View File

@@ -38,7 +38,7 @@ predicate noDefUsePath(LocalVariable lv, ControlFlowNode n) {
}
predicate neighbouringStmts(Stmt s1, Stmt s2) {
exists(Block b, int i |
exists(BlockStmt b, int i |
i in [0 .. b.getNumStmt() - 2] and
s1 = b.getStmt(i) and
s2 = b.getStmt(i + 1)

View File

@@ -7,7 +7,7 @@
<overview>
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
<include src="cpp/jsfNote.qhelp" />
<include src="../jsfNote.qhelp" />
<p>
This query highlights portions of code that can expose the floating point implementation of the underlying

View File

@@ -7,7 +7,7 @@
<overview>
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
<include src="cpp/jsfNote.qhelp" />
<include src="../jsfNote.qhelp" />
<p>
This query highlights string literals that are assigned to a non-<code>const</code> variable. String literals

View File

@@ -7,7 +7,7 @@
<overview>
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
<include src="cpp/jsfNote.qhelp" />
<include src="../jsfNote.qhelp" />
<p>
This query finds bit fields with members that are not explicitly declared to be unsigned.

View File

@@ -7,7 +7,7 @@
<overview>
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
<include src="cpp/jsfNote.qhelp" />
<include src="../jsfNote.qhelp" />
<p>
This query finds unsigned values that are being negated. Behavior is undefined in such cases.

View File

@@ -22,6 +22,6 @@ where
not s instanceof ControlStructure and
// Exclude blocks; if a child of the block violates the rule that will still
// be picked up so there is no point in blaming the block as well
not s instanceof Block and
not s instanceof BlockStmt and
s.isPure()
select s, "AV Rule 187: All non-null statements shall potentially have a side-effect."

View File

@@ -6,7 +6,7 @@
<overview>
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
<include src="cpp/jsfNote.qhelp" />
<include src="../jsfNote.qhelp" />
<p>Use of goto statements makes code more difficult to understand and maintain. Consequently, the use
of goto statements is deprecated except as a mechanism for breaking out of multiple nested loops.

View File

@@ -18,7 +18,7 @@ import cpp
// whether t is the last statement of s, possibly peeling off blocks
predicate isTerminatingStmt(Stmt s, Stmt t) {
s = t or isTerminatingStmt(s.(Block).getLastStmt(), t)
s = t or isTerminatingStmt(s.(BlockStmt).getLastStmt(), t)
}
from BreakStmt s

View File

@@ -0,0 +1,18 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<fragment>
<p>
This query is part of a suite that tests code against
the <em>Joint Strike Fighter Air Vehicle C++ Coding Standard</em> (JSF).
Alerts reported by this query highlight code that may break the
JSF rule listed in the References section.
</p>
<p>
The JSF rule this query tests is likely to be too strict for projects
that do not follow the JSF standard.
</p>
</fragment>
</qhelp>

View File

@@ -128,7 +128,7 @@ class Element extends ElementBase {
/**
* Gets the parent scope of this `Element`, if any.
* A scope is a `Type` (`Class` / `Enum`), a `Namespace`, a `Block`, a `Function`,
* A scope is a `Type` (`Class` / `Enum`), a `Namespace`, a `BlockStmt`, a `Function`,
* or certain kinds of `Statement`.
*/
Element getParentScope() {
@@ -161,7 +161,7 @@ class Element extends ElementBase {
exists(EnumConstant e | this = e and result = e.getDeclaringEnum())
or
// result instanceof block|function
exists(Block b | this = b and blockscope(unresolveElement(b), unresolveElement(result)))
exists(BlockStmt b | this = b and blockscope(unresolveElement(b), unresolveElement(result)))
or
exists(TemplateFunction tf | this = tf.getATemplateArgument() and result = tf)
or

View File

@@ -268,7 +268,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
* block, this gives the block guarded by the try statement. See
* `FunctionTryStmt` for further information.
*/
Block getBlock() { result.getParentScope() = this }
BlockStmt getBlock() { result.getParentScope() = this }
/** Holds if this function has an entry point. */
predicate hasEntryPoint() { exists(getEntryPoint()) }
@@ -276,7 +276,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
/**
* Gets the first node in this function's control flow graph.
*
* For most functions, this first node will be the `Block` returned by
* For most functions, this first node will be the `BlockStmt` returned by
* `getBlock`. However in C++, the first node can also be a
* `FunctionTryStmt`.
*/
@@ -564,7 +564,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
* If this is a function definition, get the block containing the
* function body.
*/
Block getBlock() {
BlockStmt getBlock() {
this.isDefinition() and
result = getFunction().getBlock() and
result.getFile() = this.getFile()
@@ -576,7 +576,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
*/
pragma[noopt]
int getNumberOfLines() {
exists(Block b, Location l, int start, int end, int diff | b = getBlock() |
exists(BlockStmt b, Location l, int start, int end, int diff | b = getBlock() |
l = b.getLocation() and
start = l.getStartLine() and
end = l.getEndLine() and

View File

@@ -136,7 +136,7 @@ deprecated class ObjcTryStmt extends TryStmt {
* DEPRECATED: Objective-C is no longer supported.
* An Objective C `@finally` block.
*/
deprecated class FinallyBlock extends Block {
deprecated class FinallyBlock extends BlockStmt {
FinallyBlock() { none() }
/** Gets the try statement corresponding to this finally block. */

View File

@@ -98,7 +98,7 @@ class Parameter extends LocalScopeVariable, @parameter {
* DEPRECATED: this method was used in a previous implementation of
* getName, but is no longer in use.
*/
deprecated string getNameInBlock(Block b) {
deprecated string getNameInBlock(BlockStmt b) {
exists(ParameterDeclarationEntry pde |
pde.getFunctionDeclarationEntry().getBlock() = b and
this.getFunction().getBlock() = b and
@@ -127,7 +127,7 @@ class Parameter extends LocalScopeVariable, @parameter {
* Gets the catch block to which this parameter belongs, if it is a catch
* block parameter.
*/
Block getCatchBlock() { params(underlyingElement(this), unresolveElement(result), _, _) }
BlockStmt getCatchBlock() { params(underlyingElement(this), unresolveElement(result), _, _) }
/**
* Gets the zero-based index of this parameter.

View File

@@ -25,7 +25,7 @@ private predicate exprInVoidContext(Expr e) {
(
exists(ExprStmt s |
s = e.getParent() and
not exists(StmtExpr se | s = se.getStmt().(Block).getLastStmt())
not exists(StmtExpr se | s = se.getStmt().(BlockStmt).getLastStmt())
)
or
exists(ConditionalExpr c | c.getThen() = e and c instanceof ExprInVoidContext)

View File

@@ -65,7 +65,7 @@ class ControlFlowNode extends Locatable, ControlFlowNodeBase {
* taken when this expression is true.
*/
ControlFlowNode getATrueSuccessor() {
truecond_base(this, result) and
qlCFGTrueSuccessor(this, result) and
result = getASuccessor()
}
@@ -74,7 +74,7 @@ class ControlFlowNode extends Locatable, ControlFlowNodeBase {
* taken when this expression is false.
*/
ControlFlowNode getAFalseSuccessor() {
falsecond_base(this, result) and
qlCFGFalseSuccessor(this, result) and
result = getASuccessor()
}
@@ -95,18 +95,20 @@ import ControlFlowGraphPublic
class ControlFlowNodeBase extends ElementBase, @cfgnode { }
/**
* DEPRECATED: Use `ControlFlowNode.getATrueSuccessor()` instead.
* Holds when `n2` is a control-flow node such that the control-flow
* edge `(n1, n2)` may be taken when `n1` is an expression that is true.
*/
predicate truecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
deprecated predicate truecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
qlCFGTrueSuccessor(n1, n2)
}
/**
* DEPRECATED: Use `ControlFlowNode.getAFalseSuccessor()` instead.
* Holds when `n2` is a control-flow node such that the control-flow
* edge `(n1, n2)` may be taken when `n1` is an expression that is false.
*/
predicate falsecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
deprecated predicate falsecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
qlCFGFalseSuccessor(n1, n2)
}
@@ -134,7 +136,7 @@ abstract class AdditionalControlFlowEdge extends ControlFlowNodeBase {
/**
* Holds if there is a control-flow edge from `source` to `target` in either
* the extractor-generated control-flow graph or in a subclass of
* `AdditionalControlFlowEdge`. Use this relation instead of `successors`.
* `AdditionalControlFlowEdge`. Use this relation instead of `qlCFGSuccessor`.
*/
predicate successors_extended(ControlFlowNodeBase source, ControlFlowNodeBase target) {
qlCFGSuccessor(source, target)

View File

@@ -73,8 +73,20 @@ private predicate addressTakenVariable(StackVariable var) {
)
}
/**
* Holds if `v` is a stack-allocated reference-typed local variable. We don't
* build SSA for such variables since they are likely to change values even
* when not syntactically mentioned. For the same reason,
* `addressTakenVariable` is used to prevent tracking variables that may be
* aliased by such a reference.
*
* Reference-typed parameters are treated as if they weren't references.
* That's because it's in practice highly unlikely that they alias other data
* accessible from the function body.
*/
private predicate isReferenceVar(StackVariable v) {
v.getUnspecifiedType() instanceof ReferenceType
v.getUnspecifiedType() instanceof ReferenceType and
not v instanceof Parameter
}
/**

View File

@@ -118,7 +118,7 @@ private predicate excludeNodeAndNodesBelow(Expr e) {
or
// Constructor init lists should be evaluated, and we can change this in
// the future, but it would mean that a `Function` entry point is not
// always a `Block` or `FunctionTryStmt`.
// always a `BlockStmt` or `FunctionTryStmt`.
e instanceof ConstructorInit
or
// Destructor field destructions should also be hooked into the CFG
@@ -408,10 +408,10 @@ private Node getControlOrderChildSparse(Node n, int i) {
// in-line in the block containing their corresponding DeclStmt but should
// not be evaluated in the order implied by their position in the block. We
// do the following.
// - Block skips all the VlaDeclStmt and VlaDimensionStmt children.
// - BlockStmt skips all the VlaDeclStmt and VlaDimensionStmt children.
// - VlaDeclStmt is inserted as a child of DeclStmt
// - VlaDimensionStmt is inserted as a child of VlaDeclStmt
result = n.(Block).getChild(i) and
result = n.(BlockStmt).getChild(i) and
not result instanceof VlaDeclStmt and
not result instanceof VlaDimensionStmt
or
@@ -557,7 +557,7 @@ private class Spec extends Pos {
*/
private predicate straightLineSparse(Node scope, int i, Node ni, Spec spec) {
scope =
any(Block b |
any(BlockStmt b |
i = -1 and ni = b and spec.isAt()
or
if exists(getLastControlOrderChild(b))
@@ -734,7 +734,7 @@ private predicate straightLineSparse(Node scope, int i, Node ni, Spec spec) {
or
// If the switch body is not a block then this step is skipped, and the
// expression jumps directly to the cases.
i = 1 and ni = s.getStmt().(Block) and spec.isAt()
i = 1 and ni = s.getStmt().(BlockStmt) and spec.isAt()
or
i = 2 and ni = s.getASwitchCase() and spec.isBefore()
or
@@ -1010,7 +1010,7 @@ private predicate subEdgeIncludingDestructors(Pos p1, Node n1, Node n2, Pos p2)
* The exact placement of that call in the CFG depends on the type of
* `node` as follows:
*
* - `Block`: after ordinary control flow falls off the end of the block
* - `BlockStmt`: after ordinary control flow falls off the end of the block
* without jumps or exceptions.
* - `ReturnStmt`: After the statement itself or after its operand (if
* present).
@@ -1376,8 +1376,6 @@ private module Cached {
/**
* Holds if `n2` is a successor of `n1` in the CFG. This includes also
* true-successors and false-successors.
*
* This corresponds to the old `successors` dbscheme relation.
*/
cached
predicate qlCFGSuccessor(Node n1, Node n2) {
@@ -1390,9 +1388,8 @@ private module Cached {
}
/**
* Holds if `n2` is a true-successor of `n1` in the CFG.
*
* This corresponds to the old `truecond` dbscheme relation.
* Holds if `n2` is a control-flow node such that the control-flow
* edge `(n1, n2)` may be taken when `n1` is an expression that is true.
*/
cached
predicate qlCFGTrueSuccessor(Node n1, Node n2) {
@@ -1401,9 +1398,8 @@ private module Cached {
}
/**
* Holds if `n2` is a false-successor of `n1` in the CFG.
*
* This corresponds to the old `falsecond` dbscheme relation.
* Holds if `n2` is a control-flow node such that the control-flow
* edge `(n1, n2)` may be taken when `n1` is an expression that is false.
*/
cached
predicate qlCFGFalseSuccessor(Node n1, Node n2) {

View File

@@ -1,5 +1,6 @@
import cpp
private import PrimitiveBasicBlocks
private import semmle.code.cpp.controlflow.internal.CFG
private class Node = ControlFlowNodeBase;
@@ -153,8 +154,8 @@ private predicate nonAnalyzableFunction(Function f) {
*/
private predicate impossibleFalseEdge(Expr condition, Node succ) {
conditionAlwaysTrue(condition) and
falsecond_base(condition, succ) and
not truecond_base(condition, succ)
qlCFGFalseSuccessor(condition, succ) and
not qlCFGTrueSuccessor(condition, succ)
}
/**
@@ -162,8 +163,8 @@ private predicate impossibleFalseEdge(Expr condition, Node succ) {
*/
private predicate impossibleTrueEdge(Expr condition, Node succ) {
conditionAlwaysFalse(condition) and
truecond_base(condition, succ) and
not falsecond_base(condition, succ)
qlCFGTrueSuccessor(condition, succ) and
not qlCFGFalseSuccessor(condition, succ)
}
/**
@@ -181,7 +182,7 @@ private int switchCaseRangeEnd(SwitchCase sc) {
* body `switchBlock`. There may be several such expressions: for example, if
* the condition is `(x ? y : z)` then the result is {`y`, `z`}.
*/
private Node getASwitchExpr(SwitchStmt switch, Block switchBlock) {
private Node getASwitchExpr(SwitchStmt switch, BlockStmt switchBlock) {
switch.getStmt() = switchBlock and
successors_extended(result, switchBlock)
}
@@ -191,7 +192,7 @@ private Node getASwitchExpr(SwitchStmt switch, Block switchBlock) {
* from `switchBlock` to `sc` is impossible. This considers only non-`default`
* switch cases.
*/
private predicate impossibleSwitchEdge(Block switchBlock, SwitchCase sc) {
private predicate impossibleSwitchEdge(BlockStmt switchBlock, SwitchCase sc) {
not sc instanceof DefaultCase and
exists(SwitchStmt switch |
switch = sc.getSwitchStmt() and
@@ -214,7 +215,7 @@ private predicate impossibleSwitchEdge(Block switchBlock, SwitchCase sc) {
* If a switch provably always chooses a non-default case, then the edge to
* the default case is impossible.
*/
private predicate impossibleDefaultSwitchEdge(Block switchBlock, DefaultCase dc) {
private predicate impossibleDefaultSwitchEdge(BlockStmt switchBlock, DefaultCase dc) {
exists(SwitchStmt switch |
switch = dc.getSwitchStmt() and
switch.getStmt() = switchBlock and
@@ -863,9 +864,9 @@ library class ConditionEvaluator extends ExprEvaluator {
ConditionEvaluator() { this = 0 }
override predicate interesting(Expr e) {
falsecond_base(e, _)
qlCFGFalseSuccessor(e, _)
or
truecond_base(e, _)
qlCFGTrueSuccessor(e, _)
}
}

View File

@@ -123,8 +123,18 @@ module Consistency {
n.getEnclosingCallable() != call.getEnclosingCallable()
}
// This predicate helps the compiler forget that in some languages
// it is impossible for a result of `getPreUpdateNode` to be an
// instance of `PostUpdateNode`.
private Node getPre(PostUpdateNode n) {
result = n.getPreUpdateNode()
or
none()
}
query predicate postIsNotPre(PostUpdateNode n, string msg) {
n.getPreUpdateNode() = n and msg = "PostUpdateNode should not equal its pre-update node."
getPre(n) = n and
msg = "PostUpdateNode should not equal its pre-update node."
}
query predicate postHasUniquePre(PostUpdateNode n, string msg) {
@@ -152,12 +162,6 @@ module Consistency {
msg = "Origin of readStep is missing a PostUpdateNode."
}
query predicate storeIsPostUpdate(Node n, string msg) {
storeStep(_, _, n) and
not n instanceof PostUpdateNode and
msg = "Store targets should be PostUpdateNodes."
}
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
not hasPost(n) and
not isImmutableOrUnobservable(n) and

View File

@@ -50,13 +50,25 @@ class Node extends TNode {
/** Gets the type of this node. */
Type getType() { none() } // overridden in subclasses
/** Gets the expression corresponding to this node, if any. */
/**
* Gets the expression corresponding to this node, if any. This predicate
* only has a result on nodes that represent the value of evaluating the
* expression. For data flowing _out of_ an expression, like when an
* argument is passed by reference, use `asDefiningArgument` instead of
* `asExpr`.
*/
Expr asExpr() { result = this.(ExprNode).getExpr() }
/** Gets the parameter corresponding to this node, if any. */
Parameter asParameter() { result = this.(ExplicitParameterNode).getParameter() }
/** Gets the argument that defines this `DefinitionByReferenceNode`, if any. */
/**
* Gets the argument that defines this `DefinitionByReferenceNode`, if any.
* This predicate should be used instead of `asExpr` when referring to the
* value of a reference argument _after_ the call has returned. For example,
* in `f(&x)`, this predicate will have `&x` as its result for the `Node`
* that represents the new value of `x`.
*/
Expr asDefiningArgument() { result = this.(DefinitionByReferenceNode).getArgument() }
/**
@@ -383,7 +395,9 @@ class PreConstructorInitThis extends Node, TPreConstructorInitThis {
}
/**
* Gets the `Node` corresponding to `e`.
* Gets the `Node` corresponding to the value of evaluating `e`. For data
* flowing _out of_ an expression, like when an argument is passed by
* reference, use `definitionByReferenceNodeFromArgument` instead.
*/
ExprNode exprNode(Expr e) { result.getExpr() = e }
@@ -484,6 +498,17 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
// Expr -> Expr
exprToExprStep_nocfg(nodeFrom.asExpr(), nodeTo.asExpr())
or
// Assignment -> LValue post-update node
//
// This is used for assignments whose left-hand side is not a variable
// assignment or a storeStep but is still modeled by other means. It could be
// a call to `operator*` or `operator[]` where taint should flow to the
// post-update node of the qualifier.
exists(AssignExpr assign |
nodeFrom.asExpr() = assign and
nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() = assign.getLValue()
)
or
// Node -> FlowVar -> VariableAccess
exists(FlowVar var |
(
@@ -514,6 +539,19 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
inner = nodeTo.(InnerPartialDefinitionNode).getPreUpdateNode().asExpr() and
outer = nodeFrom.(PartialDefinitionNode).getPreUpdateNode().asExpr()
)
or
// Reverse flow: data that flows from the post-update node of a reference
// returned by a function call, back into the qualifier of that function.
// This allows data to flow 'in' through references returned by a modeled
// function such as `operator[]`.
exists(DataFlowFunction f, Call call, FunctionInput inModel, FunctionOutput outModel |
call.getTarget() = f and
inModel.isReturnValueDeref() and
outModel.isQualifierObject() and
f.hasDataFlow(inModel, outModel) and
nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr() = call and
nodeTo.asDefiningArgument() = call.getQualifier()
)
}
/**

View File

@@ -120,15 +120,25 @@ private module PartialDefinitions {
)
}
predicate partiallyDefines(Variable v) { innerDefinedExpr = v.getAnAccess() }
deprecated predicate partiallyDefines(Variable v) { innerDefinedExpr = v.getAnAccess() }
predicate partiallyDefinesThis(ThisExpr e) { innerDefinedExpr = e }
deprecated predicate partiallyDefinesThis(ThisExpr e) { innerDefinedExpr = e }
/**
* Gets the subBasicBlock where this `PartialDefinition` is defined.
*/
ControlFlowNode getSubBasicBlockStart() { result = node }
/**
* Holds if this `PartialDefinition` defines variable `v` at control-flow
* node `cfn`.
*/
pragma[noinline]
predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) {
innerDefinedExpr = v.getAnAccess() and
cfn = node
}
/**
* Holds if this partial definition may modify `inner` (or what it points
* to) through `outer`. These expressions will never be `Conversion`s.
@@ -188,7 +198,7 @@ module FlowVar_internal {
predicate fullySupportedSsaVariable(Variable v) {
v = any(SsaDefinition def).getAVariable() and
// A partially-defined variable is handled using the partial definitions logic.
not any(PartialDefinition p).partiallyDefines(v) and
not any(PartialDefinition p).partiallyDefinesVariableAt(v, _) and
// SSA variables do not exist before their first assignment, but one
// feature of this data flow library is to track where uninitialized data
// ends up.
@@ -232,7 +242,7 @@ module FlowVar_internal {
or
assignmentLikeOperation(sbb, v, _, _)
or
sbb = any(PartialDefinition p | p.partiallyDefines(v)).getSubBasicBlockStart()
exists(PartialDefinition p | p.partiallyDefinesVariableAt(v, sbb))
or
blockVarDefinedByVariable(sbb, v)
)
@@ -363,8 +373,7 @@ module FlowVar_internal {
override predicate definedPartiallyAt(Expr e) {
exists(PartialDefinition p |
p.partiallyDefines(v) and
sbb = p.getSubBasicBlockStart() and
p.partiallyDefinesVariableAt(v, sbb) and
p.definesExpressions(_, e)
)
}
@@ -427,7 +436,7 @@ module FlowVar_internal {
/**
* Gets a variable that is assigned in this loop and read outside the loop.
*/
private Variable getARelevantVariable() {
Variable getARelevantVariable() {
result = this.getAVariableAssignedInLoop() and
exists(VariableAccess va |
va.getTarget() = result and
@@ -472,10 +481,16 @@ module FlowVar_internal {
reachesWithoutAssignment(bb.getAPredecessor(), v) and
this.bbInLoop(bb)
) and
not assignmentLikeOperation(bb.getANode(), v, _, _)
not assignsToVar(bb, v)
}
}
pragma[noinline]
private predicate assignsToVar(BasicBlock bb, Variable v) {
assignmentLikeOperation(bb.getANode(), v, _, _) and
exists(AlwaysTrueUponEntryLoop loop | v = loop.getARelevantVariable())
}
/**
* Holds if `loop` always assigns to `v` before leaving through an edge
* from `bbInside` in its condition to `bbOutside` outside the loop. Also,
@@ -736,7 +751,7 @@ module FlowVar_internal {
exists(Variable v | not fullySupportedSsaVariable(v) |
assignmentLikeOperation(this, v, _, _)
or
this = any(PartialDefinition p | p.partiallyDefines(v)).getSubBasicBlockStart()
exists(PartialDefinition p | p.partiallyDefinesVariableAt(v, this))
// It is not necessary to cut the basic blocks at `Initializer` nodes
// because the affected variable can have no _other_ value before its
// initializer. It is not necessary to cut basic blocks at procedure

View File

@@ -33,10 +33,10 @@ predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
}
/**
* Holds if `node` should be a barrier in all global taint flow configurations
* Holds if `node` should be a sanitizer in all global taint flow configurations
* but not in local taint.
*/
predicate defaultTaintBarrier(DataFlow::Node node) { none() }
predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
/**
* Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding
@@ -82,6 +82,26 @@ predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeT
exprToDefinitionByReferenceStep(nodeFrom.asExpr(), nodeTo.asDefiningArgument())
or
exprToPartialDefinitionStep(nodeFrom.asExpr(), nodeTo.asPartialDefinition())
or
// Reverse taint: taint that flows from the post-update node of a reference
// returned by a function call, back into the qualifier of that function.
// This allows taint to flow 'in' through references returned by a modeled
// function such as `operator[]`.
exists(TaintFunction f, Call call, FunctionInput inModel, FunctionOutput outModel |
call.getTarget() = f and
inModel.isReturnValueDeref() and
nodeFrom.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() = call and
f.hasTaintFlow(inModel, outModel) and
(
outModel.isQualifierObject() and
nodeTo.asDefiningArgument() = call.getQualifier()
or
exists(int argOutIndex |
outModel.isParameterDeref(argOutIndex) and
nodeTo.asDefiningArgument() = call.getArgument(argOutIndex)
)
)
)
}
/**

View File

@@ -76,20 +76,20 @@ abstract class Configuration extends DataFlow::Configuration {
final override predicate isBarrier(DataFlow::Node node) {
isSanitizer(node) or
defaultTaintBarrier(node)
defaultTaintSanitizer(node)
}
/** Holds if data flow into `node` is prohibited. */
/** Holds if taint propagation into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
/** Holds if data flow out of `node` is prohibited. */
/** Holds if taint propagation out of `node` is prohibited. */
predicate isSanitizerOut(DataFlow::Node node) { none() }
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
/** Holds if data flow through nodes guarded by `guard` is prohibited. */
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }

View File

@@ -76,20 +76,20 @@ abstract class Configuration extends DataFlow::Configuration {
final override predicate isBarrier(DataFlow::Node node) {
isSanitizer(node) or
defaultTaintBarrier(node)
defaultTaintSanitizer(node)
}
/** Holds if data flow into `node` is prohibited. */
/** Holds if taint propagation into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
/** Holds if data flow out of `node` is prohibited. */
/** Holds if taint propagation out of `node` is prohibited. */
predicate isSanitizerOut(DataFlow::Node node) { none() }
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
/** Holds if data flow through nodes guarded by `guard` is prohibited. */
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }

View File

@@ -27,7 +27,7 @@ class Expr extends StmtParent, @expr {
Function getEnclosingFunction() { result = exprEnclosingElement(this) }
/** Gets the nearest enclosing set of curly braces around this expression in the source, if any. */
Block getEnclosingBlock() { result = getEnclosingStmt().getEnclosingBlock() }
BlockStmt getEnclosingBlock() { result = getEnclosingStmt().getEnclosingBlock() }
override Stmt getEnclosingStmt() {
result = this.getParent().(Expr).getEnclosingStmt()
@@ -1109,7 +1109,7 @@ class StmtExpr extends Expr, @expr_stmt {
/** Get the result expression of a statement. (Helper function for StmtExpr.) */
private Expr getStmtResultExpr(Stmt stmt) {
result = stmt.(ExprStmt).getExpr() or
result = getStmtResultExpr(stmt.(Block).getLastStmt())
result = getStmtResultExpr(stmt.(BlockStmt).getLastStmt())
}
/**
@@ -1268,3 +1268,31 @@ class SpaceshipExpr extends BinaryOperation, @spaceshipexpr {
override string getOperator() { result = "<=>" }
}
/**
* A C/C++ `co_await` expression.
* ```
* co_await foo();
* ```
*/
class CoAwaitExpr extends UnaryOperation, @co_await {
override string getAPrimaryQlClass() { result = "CoAwaitExpr" }
override string getOperator() { result = "co_await" }
override int getPrecedence() { result = 16 }
}
/**
* A C/C++ `co_yield` expression.
* ```
* co_yield 1;
* ```
*/
class CoYieldExpr extends UnaryOperation, @co_yield {
override string getAPrimaryQlClass() { result = "CoYieldExpr" }
override string getOperator() { result = "co_yield" }
override int getPrecedence() { result = 2 }
}

View File

@@ -70,7 +70,7 @@ private DataFlow::Node getNodeForSource(Expr source) {
//
// This case goes together with the similar (but not identical) rule in
// `nodeIsBarrierIn`.
result = DataFlow::definitionByReferenceNode(source) and
result = DataFlow::definitionByReferenceNodeFromArgument(source) and
not argv(source.(VariableAccess).getTarget())
)
}
@@ -210,7 +210,7 @@ private predicate nodeIsBarrierIn(DataFlow::Node node) {
or
// This case goes together with the similar (but not identical) rule in
// `getNodeForSource`.
node = DataFlow::definitionByReferenceNode(source)
node = DataFlow::definitionByReferenceNodeFromArgument(source)
)
}

View File

@@ -123,8 +123,18 @@ module Consistency {
n.getEnclosingCallable() != call.getEnclosingCallable()
}
// This predicate helps the compiler forget that in some languages
// it is impossible for a result of `getPreUpdateNode` to be an
// instance of `PostUpdateNode`.
private Node getPre(PostUpdateNode n) {
result = n.getPreUpdateNode()
or
none()
}
query predicate postIsNotPre(PostUpdateNode n, string msg) {
n.getPreUpdateNode() = n and msg = "PostUpdateNode should not equal its pre-update node."
getPre(n) = n and
msg = "PostUpdateNode should not equal its pre-update node."
}
query predicate postHasUniquePre(PostUpdateNode n, string msg) {
@@ -152,12 +162,6 @@ module Consistency {
msg = "Origin of readStep is missing a PostUpdateNode."
}
query predicate storeIsPostUpdate(Node n, string msg) {
storeStep(_, _, n) and
not n instanceof PostUpdateNode and
msg = "Store targets should be PostUpdateNodes."
}
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
not hasPost(n) and
not isImmutableOrUnobservable(n) and

View File

@@ -11,6 +11,7 @@ private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.models.interfaces.DataFlow
cached
private newtype TIRDataFlowNode =
TInstructionNode(Instruction i) or
TOperandNode(Operand op) or
@@ -42,9 +43,14 @@ class Node extends TIRDataFlowNode {
Operand asOperand() { result = this.(OperandNode).getOperand() }
/**
* Gets the non-conversion expression corresponding to this node, if any. If
* this node strictly (in the sense of `asConvertedExpr`) corresponds to a
* `Conversion`, then the result is that `Conversion`'s non-`Conversion` base
* Gets the non-conversion expression corresponding to this node, if any.
* This predicate only has a result on nodes that represent the value of
* evaluating the expression. For data flowing _out of_ an expression, like
* when an argument is passed by reference, use `asDefiningArgument` instead
* of `asExpr`.
*
* If this node strictly (in the sense of `asConvertedExpr`) corresponds to
* a `Conversion`, then the result is the underlying non-`Conversion` base
* expression.
*/
Expr asExpr() { result = this.(ExprNode).getExpr() }
@@ -55,7 +61,13 @@ class Node extends TIRDataFlowNode {
*/
Expr asConvertedExpr() { result = this.(ExprNode).getConvertedExpr() }
/** Gets the argument that defines this `DefinitionByReferenceNode`, if any. */
/**
* Gets the argument that defines this `DefinitionByReferenceNode`, if any.
* This predicate should be used instead of `asExpr` when referring to the
* value of a reference argument _after_ the call has returned. For example,
* in `f(&x)`, this predicate will have `&x` as its result for the `Node`
* that represents the new value of `x`.
*/
Expr asDefiningArgument() { result = this.(DefinitionByReferenceNode).getArgument() }
/** Gets the positional parameter corresponding to this node, if any. */
@@ -378,7 +390,7 @@ private class ExplicitSingleFieldStoreQualifierNode extends PartialDefinitionNod
class DefinitionByReferenceNode extends InstructionNode {
override WriteSideEffectInstruction instr;
/** Gets the argument corresponding to this node. */
/** Gets the unconverted argument corresponding to this node. */
Expr getArgument() {
result =
instr
@@ -462,20 +474,26 @@ class VariableNode extends Node, TVariableNode {
InstructionNode instructionNode(Instruction instr) { result.getInstruction() = instr }
/**
* DEPRECATED: use `definitionByReferenceNodeFromArgument` instead.
*
* Gets the `Node` corresponding to a definition by reference of the variable
* that is passed as `argument` of a call.
*/
DefinitionByReferenceNode definitionByReferenceNode(Expr e) { result.getArgument() = e }
deprecated DefinitionByReferenceNode definitionByReferenceNode(Expr e) { result.getArgument() = e }
/**
* Gets a `Node` corresponding to `e` or any of its conversions. There is no
* result if `e` is a `Conversion`.
* Gets the `Node` corresponding to the value of evaluating `e` or any of its
* conversions. There is no result if `e` is a `Conversion`. For data flowing
* _out of_ an expression, like when an argument is passed by reference, use
* `definitionByReferenceNodeFromArgument` instead.
*/
ExprNode exprNode(Expr e) { result.getExpr() = e }
/**
* Gets the `Node` corresponding to `e`, if any. Here, `e` may be a
* `Conversion`.
* Gets the `Node` corresponding to the value of evaluating `e`. Here, `e` may
* be a `Conversion`. For data flowing _out of_ an expression, like when an
* argument is passed by reference, use
* `definitionByReferenceNodeFromArgument` instead.
*/
ExprNode convertedExprNode(Expr e) { result.getConvertedExpr() = e }
@@ -484,6 +502,14 @@ ExprNode convertedExprNode(Expr e) { result.getConvertedExpr() = e }
*/
ExplicitParameterNode parameterNode(Parameter p) { result.getParameter() = p }
/**
* Gets the `Node` corresponding to a definition by reference of the variable
* that is passed as unconverted `argument` of a call.
*/
DefinitionByReferenceNode definitionByReferenceNodeFromArgument(Expr argument) {
result.getArgument() = argument
}
/** Gets the `VariableNode` corresponding to the variable `v`. */
VariableNode variableNode(Variable v) { result.getVariable() = v }
@@ -508,11 +534,11 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFr
* data flow. It may have less flow than the `localFlowStep` predicate.
*/
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
// Instruction -> Instruction flow
simpleInstructionLocalFlowStep(nodeFrom.asInstruction(), nodeTo.asInstruction())
or
// Operand -> Instruction flow
simpleOperandLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction())
simpleInstructionLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction())
or
// Instruction -> Operand flow
simpleOperandLocalFlowStep(nodeFrom.asInstruction(), nodeTo.asOperand())
}
pragma[noinline]
@@ -524,26 +550,20 @@ private predicate getFieldSizeOfClass(Class c, Type type, int size) {
)
}
private predicate simpleOperandLocalFlowStep(Operand opFrom, Instruction iTo) {
// Certain dataflow steps (for instance `PostUpdateNode.getPreUpdateNode()`) generates flow to
// operands, so we include dataflow from those operands to the "result" of the instruction (i.e., to
// the instruction itself).
exists(PostUpdateNode post |
opFrom = post.getPreUpdateNode().asOperand() and
iTo.getAnOperand() = opFrom
private predicate isSingleFieldClass(Type type, Class cTo) {
exists(int size |
cTo.getSize() = size and
getFieldSizeOfClass(cTo, type, size)
)
}
cached
private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction iTo) {
iTo.(CopyInstruction).getSourceValue() = iFrom
private predicate simpleOperandLocalFlowStep(Instruction iFrom, Operand opTo) {
// Propagate flow from an instruction to its exact uses.
opTo.getDef() = iFrom
or
iTo.(PhiInstruction).getAnOperand().getDef() = iFrom
or
// A read side effect is almost never exact since we don't know exactly how
// much memory the callee will read.
iTo.(ReadSideEffectInstruction).getSideEffectOperand().getAnyDef() = iFrom and
not iFrom.isResultConflated()
opTo = any(ReadSideEffectInstruction read).getSideEffectOperand() and
not iFrom.isResultConflated() and
iFrom = opTo.getAnyDef()
or
// Loading a single `int` from an `int *` parameter is not an exact load since
// the parameter may point to an entire array rather than a single `int`. The
@@ -557,20 +577,38 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
// leads to a phi node.
exists(InitializeIndirectionInstruction init |
iFrom = init and
iTo.(LoadInstruction).getSourceValueOperand().getAnyDef() = init and
opTo.(LoadOperand).getAnyDef() = init and
// Check that the types match. Otherwise we can get flow from an object to
// its fields, which leads to field conflation when there's flow from other
// fields to the object elsewhere.
init.getParameter().getType().getUnspecifiedType().(DerivedType).getBaseType() =
iTo.getResultType().getUnspecifiedType()
opTo.getType().getUnspecifiedType()
)
or
// Flow from stores to structs with a single field to a load of that field.
exists(LoadInstruction load |
load.getSourceValueOperand() = opTo and
opTo.getAnyDef() = iFrom and
isSingleFieldClass(iFrom.getResultType(), opTo.getType())
)
}
cached
private predicate simpleInstructionLocalFlowStep(Operand opFrom, Instruction iTo) {
iTo.(CopyInstruction).getSourceValueOperand() = opFrom
or
iTo.(PhiInstruction).getAnInputOperand() = opFrom
or
// A read side effect is almost never exact since we don't know exactly how
// much memory the callee will read.
iTo.(ReadSideEffectInstruction).getSideEffectOperand() = opFrom
or
// Treat all conversions as flow, even conversions between different numeric types.
iTo.(ConvertInstruction).getUnary() = iFrom
iTo.(ConvertInstruction).getUnaryOperand() = opFrom
or
iTo.(CheckedConvertOrNullInstruction).getUnary() = iFrom
iTo.(CheckedConvertOrNullInstruction).getUnaryOperand() = opFrom
or
iTo.(InheritanceConversionInstruction).getUnary() = iFrom
iTo.(InheritanceConversionInstruction).getUnaryOperand() = opFrom
or
// A chi instruction represents a point where a new value (the _partial_
// operand) may overwrite an old value (the _total_ operand), but the alias
@@ -583,7 +621,7 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
//
// Flow through the partial operand belongs in the taint-tracking libraries
// for now.
iTo.getAnOperand().(ChiTotalOperand).getDef() = iFrom
iTo.getAnOperand().(ChiTotalOperand) = opFrom
or
// Add flow from write side-effects to non-conflated chi instructions through their
// partial operands. From there, a `readStep` will find subsequent reads of that field.
@@ -598,24 +636,16 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
// Here, a `WriteSideEffectInstruction` will provide a new definition for `p->x` after the call to
// `setX`, which will be melded into `p` through a chi instruction.
exists(ChiInstruction chi | chi = iTo |
chi.getPartialOperand().getDef() = iFrom.(WriteSideEffectInstruction) and
opFrom.getAnyDef() instanceof WriteSideEffectInstruction and
chi.getPartialOperand() = opFrom and
not chi.isResultConflated()
)
or
// Flow from stores to structs with a single field to a load of that field.
iTo.(LoadInstruction).getSourceValueOperand().getAnyDef() = iFrom and
exists(int size, Type type, Class cTo |
type = iFrom.getResultType() and
cTo = iTo.getResultType() and
cTo.getSize() = size and
getFieldSizeOfClass(cTo, type, size)
)
or
// Flow through modeled functions
modelFlow(iFrom, iTo)
modelFlow(opFrom, iTo)
}
private predicate modelFlow(Instruction iFrom, Instruction iTo) {
private predicate modelFlow(Operand opFrom, Instruction iTo) {
exists(
CallInstruction call, DataFlowFunction func, FunctionInput modelIn, FunctionOutput modelOut
|
@@ -640,17 +670,17 @@ private predicate modelFlow(Instruction iFrom, Instruction iTo) {
(
exists(int index |
modelIn.isParameter(index) and
iFrom = call.getPositionalArgument(index)
opFrom = call.getPositionalArgumentOperand(index)
)
or
exists(int index, ReadSideEffectInstruction read |
modelIn.isParameterDeref(index) and
read = getSideEffectFor(call, index) and
iFrom = read.getSideEffectOperand().getAnyDef()
opFrom = read.getSideEffectOperand()
)
or
modelIn.isQualifierAddress() and
iFrom = call.getThisArgument()
opFrom = call.getThisArgumentOperand()
// TODO: add read side effects for qualifiers
)
)

View File

@@ -100,10 +100,10 @@ predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
}
/**
* Holds if `node` should be a barrier in all global taint flow configurations
* Holds if `node` should be a sanitizer in all global taint flow configurations
* but not in local taint.
*/
predicate defaultTaintBarrier(DataFlow::Node node) { none() }
predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
/**
* Holds if taint can flow from `instrIn` to `instrOut` through a call to a

View File

@@ -76,20 +76,20 @@ abstract class Configuration extends DataFlow::Configuration {
final override predicate isBarrier(DataFlow::Node node) {
isSanitizer(node) or
defaultTaintBarrier(node)
defaultTaintSanitizer(node)
}
/** Holds if data flow into `node` is prohibited. */
/** Holds if taint propagation into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
/** Holds if data flow out of `node` is prohibited. */
/** Holds if taint propagation out of `node` is prohibited. */
predicate isSanitizerOut(DataFlow::Node node) { none() }
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
/** Holds if data flow through nodes guarded by `guard` is prohibited. */
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }

View File

@@ -76,20 +76,20 @@ abstract class Configuration extends DataFlow::Configuration {
final override predicate isBarrier(DataFlow::Node node) {
isSanitizer(node) or
defaultTaintBarrier(node)
defaultTaintSanitizer(node)
}
/** Holds if data flow into `node` is prohibited. */
/** Holds if taint propagation into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
/** Holds if data flow out of `node` is prohibited. */
/** Holds if taint propagation out of `node` is prohibited. */
predicate isSanitizerOut(DataFlow::Node node) { none() }
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
/** Holds if data flow through nodes guarded by `guard` is prohibited. */
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }

View File

@@ -2905,7 +2905,7 @@ predicate exprNeedsCopyIfNotLoaded(Expr expr) {
private predicate exprImmediatelyDiscarded(Expr expr) {
exists(ExprStmt s |
s = expr.getParent() and
not exists(StmtExpr se | s = se.getStmt().(Block).getLastStmt())
not exists(StmtExpr se | s = se.getStmt().(BlockStmt).getLastStmt())
)
or
exists(CommaExpr c | c.getLeftOperand() = expr)

View File

@@ -290,7 +290,7 @@ class TranslatedTryStmt extends TranslatedStmt {
}
class TranslatedBlock extends TranslatedStmt {
override Block stmt;
override BlockStmt stmt;
override TranslatedElement getChild(int id) { result = getStmt(id) }

View File

@@ -334,7 +334,7 @@ private predicate branchingExpr(Expr expr) {
* Gets the number of branching statements and expressions in a block. This is
* for computing cyclomatic complexity.
*/
int cyclomaticComplexityBranches(Block b) {
int cyclomaticComplexityBranches(BlockStmt b) {
result =
count(Stmt stmt |
branchingStmt(stmt) and
@@ -373,7 +373,7 @@ private predicate skipParent(Stmt s) {
exists(Stmt parent | parent = s.getParentStmt() |
s instanceof IfStmt and parent.(IfStmt).getElse() = s
or
parent instanceof Block
parent instanceof BlockStmt
or
exists(File f, int startLine, int startCol |
startsAt(s, f, startLine, startCol) and

View File

@@ -4,6 +4,7 @@ private import implementations.Fread
private import implementations.Gets
private import implementations.IdentityFunction
private import implementations.Inet
private import implementations.Iterator
private import implementations.MemberFunction
private import implementations.Memcpy
private import implementations.Memset

View File

@@ -0,0 +1,273 @@
/**
* Provides implementation classes modeling C++ iterators, including
* `std::iterator`, `std::iterator_traits`, and types meeting the
* `LegacyIterator` named requirement. See `semmle.code.cpp.models.Models` for
* usage information.
*/
import cpp
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.DataFlow
/**
* An instantiation of the `std::iterator_traits` template.
*/
class IteratorTraits extends Class {
IteratorTraits() {
this.hasQualifiedName("std", "iterator_traits") and
not this instanceof TemplateClass and
exists(TypedefType t |
this.getAMember() = t and
t.getName() = "iterator_category"
)
}
Type getIteratorType() { result = this.getTemplateArgument(0) }
}
/**
* A type which has the typedefs expected for an iterator.
*/
class IteratorByTypedefs extends Class {
IteratorByTypedefs() {
this.getAMember().(TypedefType).hasName("difference_type") and
this.getAMember().(TypedefType).hasName("value_type") and
this.getAMember().(TypedefType).hasName("pointer") and
this.getAMember().(TypedefType).hasName("reference") and
this.getAMember().(TypedefType).hasName("iterator_category") and
not this.hasQualifiedName("std", "iterator_traits")
}
}
/**
* The `std::iterator` class.
*/
class StdIterator extends Class {
StdIterator() { this.hasQualifiedName("std", "iterator") }
}
/**
* A type which can be used as an iterator
*/
class Iterator extends Type {
Iterator() {
this instanceof IteratorByTypedefs or
exists(IteratorTraits it | it.getIteratorType() = this) or
this instanceof StdIterator
}
}
private FunctionInput getIteratorArgumentInput(Operator op, int index) {
exists(Type t |
t =
op
.getACallToThisFunction()
.getArgument(index)
.getExplicitlyConverted()
.getType()
.stripTopLevelSpecifiers()
|
(
t instanceof Iterator or
t.(ReferenceType).getBaseType() instanceof Iterator
) and
if op.getParameter(index).getUnspecifiedType() instanceof ReferenceType
then result.isParameterDeref(index)
else result.isParameter(index)
)
}
/**
* A non-member prefix `operator*` function for an iterator type.
*/
class IteratorPointerDereferenceOperator extends Operator, TaintFunction {
FunctionInput iteratorInput;
IteratorPointerDereferenceOperator() {
this.hasName("operator*") and
iteratorInput = getIteratorArgumentInput(this, 0)
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input = iteratorInput and
output.isReturnValue()
}
}
/**
* A non-member `operator++` or `operator--` function for an iterator type.
*/
class IteratorCrementOperator extends Operator, DataFlowFunction {
FunctionInput iteratorInput;
IteratorCrementOperator() {
this.hasName(["operator++", "operator--"]) and
iteratorInput = getIteratorArgumentInput(this, 0)
}
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
input = iteratorInput and
output.isReturnValue()
}
}
/**
* A non-member `operator+` function for an iterator type.
*/
class IteratorAddOperator extends Operator, TaintFunction {
FunctionInput iteratorInput;
IteratorAddOperator() {
this.hasName("operator+") and
iteratorInput = getIteratorArgumentInput(this, [0, 1])
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input = iteratorInput and
output.isReturnValue()
}
}
/**
* A non-member `operator-` function that takes a pointer difference type as its second argument.
*/
class IteratorSubOperator extends Operator, TaintFunction {
FunctionInput iteratorInput;
IteratorSubOperator() {
this.hasName("operator-") and
iteratorInput = getIteratorArgumentInput(this, 0) and
this.getParameter(1).getUnspecifiedType() instanceof IntegralType // not an iterator difference
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input = iteratorInput and
output.isReturnValue()
}
}
/**
* A non-member `operator+=` or `operator-=` function for an iterator type.
*/
class IteratorAssignArithmeticOperator extends Operator, DataFlowFunction, TaintFunction {
IteratorAssignArithmeticOperator() {
this.hasName(["operator+=", "operator-="]) and
this.getDeclaringType() instanceof Iterator
}
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
input.isParameter(0) and
output.isReturnValue()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isParameterDeref(1) and
output.isParameterDeref(0)
}
}
/**
* A prefix `operator*` member function for an iterator type.
*/
class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunction {
IteratorPointerDereferenceMemberOperator() {
this.hasName("operator*") and
this.getDeclaringType() instanceof Iterator
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
output.isReturnValue()
}
}
/**
* An `operator++` or `operator--` member function for an iterator type.
*/
class IteratorCrementMemberOperator extends MemberFunction, DataFlowFunction, TaintFunction {
IteratorCrementMemberOperator() {
this.hasName(["operator++", "operator--"]) and
this.getDeclaringType() instanceof Iterator
}
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierAddress() and
output.isReturnValue()
or
input.isReturnValueDeref() and
output.isQualifierObject()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
output.isReturnValueDeref()
}
}
/**
* A member `operator->` function for an iterator type.
*/
class IteratorFieldMemberOperator extends Operator, TaintFunction {
IteratorFieldMemberOperator() {
this.hasName("operator->") and
this.getDeclaringType() instanceof Iterator
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
output.isReturnValue()
}
}
/**
* An `operator+` or `operator-` member function of an iterator class.
*/
class IteratorBinaryArithmeticMemberOperator extends MemberFunction, TaintFunction {
IteratorBinaryArithmeticMemberOperator() {
this.hasName(["operator+", "operator-"]) and
this.getDeclaringType() instanceof Iterator
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
output.isReturnValue()
}
}
/**
* An `operator+=` or `operator-=` member function of an iterator class.
*/
class IteratorAssignArithmeticMemberOperator extends MemberFunction, DataFlowFunction, TaintFunction {
IteratorAssignArithmeticMemberOperator() {
this.hasName(["operator+=", "operator-="]) and
this.getDeclaringType() instanceof Iterator
}
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierAddress() and
output.isReturnValue()
or
input.isReturnValueDeref() and
output.isQualifierObject()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
output.isReturnValueDeref()
}
}
/**
* An `operator[]` member function of an iterator class.
*/
class IteratorArrayMemberOperator extends MemberFunction, TaintFunction {
IteratorArrayMemberOperator() {
this.hasName("operator[]") and
this.getDeclaringType() instanceof Iterator
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
output.isReturnValue()
}
}

View File

@@ -3,6 +3,7 @@
*/
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.implementations.Iterator
/**
* Additional model for standard container constructors that reference the
@@ -14,10 +15,7 @@ import semmle.code.cpp.models.interfaces.Taint
*/
class StdSequenceContainerConstructor extends Constructor, TaintFunction {
StdSequenceContainerConstructor() {
this.getDeclaringType().hasQualifiedName("std", "vector") or
this.getDeclaringType().hasQualifiedName("std", "deque") or
this.getDeclaringType().hasQualifiedName("std", "list") or
this.getDeclaringType().hasQualifiedName("std", "forward_list")
this.getDeclaringType().hasQualifiedName("std", ["vector", "deque", "list", "forward_list"])
}
/**
@@ -26,26 +24,50 @@ class StdSequenceContainerConstructor extends Constructor, TaintFunction {
*/
int getAValueTypeParameterIndex() {
getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() =
getDeclaringType().getTemplateArgument(0) // i.e. the `T` of this `std::vector<T>`
getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector<T>`
}
/**
* Gets the index of a parameter to this function that is an iterator.
*/
int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// taint flow from any parameter of the value type to the returned object
input.isParameterDeref(getAValueTypeParameterIndex()) and
(
input.isParameterDeref(getAValueTypeParameterIndex()) or
input.isParameter(getAnIteratorParameterIndex())
) and
output.isReturnValue() // TODO: this should be `isQualifierObject` by our current definitions, but that flow is not yet supported.
}
}
/**
* The standard container function `data`.
*/
class StdSequenceContainerData extends TaintFunction {
StdSequenceContainerData() { this.hasQualifiedName("std", ["array", "vector"], "data") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from container itself (qualifier) to return value
input.isQualifierObject() and
output.isReturnValueDeref()
or
// reverse flow from returned reference to the qualifier (for writes to
// `data`)
input.isReturnValueDeref() and
output.isQualifierObject()
}
}
/**
* The standard container functions `push_back` and `push_front`.
*/
class StdSequenceContainerPush extends TaintFunction {
StdSequenceContainerPush() {
this.hasQualifiedName("std", "vector", "push_back") or
this.hasQualifiedName("std", "deque", "push_back") or
this.hasQualifiedName("std", "deque", "push_front") or
this.hasQualifiedName("std", "list", "push_back") or
this.hasQualifiedName("std", "list", "push_front") or
this.hasQualifiedName("std", "deque", ["push_back", "push_front"]) or
this.hasQualifiedName("std", "list", ["push_back", "push_front"]) or
this.hasQualifiedName("std", "forward_list", "push_front")
}
@@ -61,14 +83,10 @@ class StdSequenceContainerPush extends TaintFunction {
*/
class StdSequenceContainerFrontBack extends TaintFunction {
StdSequenceContainerFrontBack() {
this.hasQualifiedName("std", "array", "front") or
this.hasQualifiedName("std", "array", "back") or
this.hasQualifiedName("std", "vector", "front") or
this.hasQualifiedName("std", "vector", "back") or
this.hasQualifiedName("std", "deque", "front") or
this.hasQualifiedName("std", "deque", "back") or
this.hasQualifiedName("std", "list", "front") or
this.hasQualifiedName("std", "list", "back") or
this.hasQualifiedName("std", "array", ["front", "back"]) or
this.hasQualifiedName("std", "vector", ["front", "back"]) or
this.hasQualifiedName("std", "deque", ["front", "back"]) or
this.hasQualifiedName("std", "list", ["front", "back"]) or
this.hasQualifiedName("std", "forward_list", "front")
}
@@ -79,16 +97,101 @@ class StdSequenceContainerFrontBack extends TaintFunction {
}
}
/**
* The standard container functions `insert` and `insert_after`.
*/
class StdSequenceContainerInsert extends TaintFunction {
StdSequenceContainerInsert() {
this.hasQualifiedName("std", ["vector", "deque", "list"], "insert") or
this.hasQualifiedName("std", ["forward_list"], "insert_after")
}
/**
* Gets the index of a parameter to this function that is a reference to the
* value type of the container.
*/
int getAValueTypeParameterIndex() {
getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() =
getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector<T>`
}
/**
* Gets the index of a parameter to this function that is an iterator.
*/
int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from parameter to container itself (qualifier) and return value
(
input.isQualifierObject() or
input.isParameterDeref(getAValueTypeParameterIndex()) or
input.isParameter(getAnIteratorParameterIndex())
) and
(
output.isQualifierObject() or
output.isReturnValueDeref()
)
}
}
/**
* The standard container function `assign`.
*/
class StdSequenceContainerAssign extends TaintFunction {
StdSequenceContainerAssign() {
this.hasQualifiedName("std", ["vector", "deque", "list", "forward_list"], "assign")
}
/**
* Gets the index of a parameter to this function that is a reference to the
* value type of the container.
*/
int getAValueTypeParameterIndex() {
getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() =
getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector<T>`
}
/**
* Gets the index of a parameter to this function that is an iterator.
*/
int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from parameter to container itself (qualifier)
(
input.isParameterDeref(getAValueTypeParameterIndex()) or
input.isParameter(getAnIteratorParameterIndex())
) and
output.isQualifierObject()
}
}
/**
* The standard container `begin` and `end` functions and their
* variants.
*/
class StdSequenceContainerBeginEnd extends TaintFunction {
StdSequenceContainerBeginEnd() {
this
.hasQualifiedName("std", ["array", "vector", "deque", "list"],
["begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend"]) or
this
.hasQualifiedName("std", "forward_list",
["before_begin", "begin", "end", "cbefore_begin", "cbegin", "cend"])
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
output.isReturnValue()
}
}
/**
* The standard container `swap` functions.
*/
class StdSequenceContainerSwap extends TaintFunction {
StdSequenceContainerSwap() {
this.hasQualifiedName("std", "array", "swap") or
this.hasQualifiedName("std", "vector", "swap") or
this.hasQualifiedName("std", "deque", "swap") or
this.hasQualifiedName("std", "list", "swap") or
this.hasQualifiedName("std", "forward_list", "swap")
this.hasQualifiedName("std", ["array", "vector", "deque", "list", "forward_list"], "swap")
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -100,3 +203,22 @@ class StdSequenceContainerSwap extends TaintFunction {
output.isQualifierObject()
}
}
/**
* The standard container functions `at` and `operator[]`.
*/
class StdSequenceContainerAt extends TaintFunction {
StdSequenceContainerAt() {
this.hasQualifiedName("std", ["vector", "array", "deque"], ["at", "operator[]"])
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from qualifier to referenced return value
input.isQualifierObject() and
output.isReturnValueDeref()
or
// reverse flow from returned reference to the qualifier
input.isReturnValueDeref() and
output.isQualifierObject()
}
}

View File

@@ -1,4 +1,11 @@
/**
* Provides implementation classes modeling `std::string` (and other
* instantiations of `std::basic_string`) and `std::ostream`. See
* `semmle.code.cpp.models.Models` for usage information.
*/
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.implementations.Iterator
/**
* The `std::basic_string` template class.
@@ -8,18 +15,96 @@ class StdBasicString extends TemplateClass {
}
/**
* The `std::string` functions `c_str` and `data`.
* Additional model for `std::string` constructors that reference the character
* type of the container, or an iterator. For example construction from
* iterators:
* ```
* std::string b(a.begin(), a.end());
* ```
*/
class StdStringConstructor extends Constructor, TaintFunction {
StdStringConstructor() { this.getDeclaringType().hasQualifiedName("std", "basic_string") }
/**
* Gets the index of a parameter to this function that is a string (or
* character).
*/
int getAStringParameterIndex() {
getParameter(result).getType() instanceof PointerType or // e.g. `std::basic_string::CharT *`
getParameter(result).getType() instanceof ReferenceType or // e.g. `std::basic_string &`
getParameter(result).getUnspecifiedType() =
getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. `std::basic_string::CharT`
}
/**
* Gets the index of a parameter to this function that is an iterator.
*/
int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// taint flow from any parameter of the value type to the returned object
(
input.isParameterDeref(getAStringParameterIndex()) or
input.isParameter(getAnIteratorParameterIndex())
) and
output.isReturnValue() // TODO: this should be `isQualifierObject` by our current definitions, but that flow is not yet supported.
}
}
/**
* The `std::string` function `c_str`.
*/
class StdStringCStr extends TaintFunction {
StdStringCStr() {
this.hasQualifiedName("std", "basic_string", "c_str") or
this.hasQualifiedName("std", "basic_string", "data")
}
StdStringCStr() { this.hasQualifiedName("std", "basic_string", "c_str") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from string itself (qualifier) to return value
input.isQualifierObject() and
output.isReturnValue()
output.isReturnValueDeref()
}
}
/**
* The `std::string` function `data`.
*/
class StdStringData extends TaintFunction {
StdStringData() { this.hasQualifiedName("std", "basic_string", "data") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from string itself (qualifier) to return value
input.isQualifierObject() and
output.isReturnValueDeref()
or
// reverse flow from returned reference to the qualifier (for writes to
// `data`)
input.isReturnValueDeref() and
output.isQualifierObject()
}
}
/**
* The `std::string` function `push_back`.
*/
class StdStringPush extends TaintFunction {
StdStringPush() { this.hasQualifiedName("std", "basic_string", "push_back") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from parameter to qualifier
input.isParameterDeref(0) and
output.isQualifierObject()
}
}
/**
* The `std::string` functions `front` and `back`.
*/
class StdStringFrontBack extends TaintFunction {
StdStringFrontBack() { this.hasQualifiedName("std", "basic_string", ["front", "back"]) }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from object to returned reference
input.isQualifierObject() and
output.isReturnValueDeref()
}
}
@@ -49,32 +134,41 @@ class StdStringPlus extends TaintFunction {
*/
class StdStringAppend extends TaintFunction {
StdStringAppend() {
this.hasQualifiedName("std", "basic_string", "operator+=") or
this.hasQualifiedName("std", "basic_string", "append") or
this.hasQualifiedName("std", "basic_string", "insert") or
this.hasQualifiedName("std", "basic_string", "replace")
this.hasQualifiedName("std", "basic_string", ["operator+=", "append", "insert", "replace"])
}
/**
* Gets the index of a parameter to this function that is a string (or
* character).
*/
int getAStringParameter() {
getParameter(result).getType() instanceof PointerType or
getParameter(result).getType() instanceof ReferenceType or
getParameter(result).getType() = getDeclaringType().getTemplateArgument(0) // i.e. `std::basic_string::CharT`
int getAStringParameterIndex() {
getParameter(result).getType() instanceof PointerType or // e.g. `std::basic_string::CharT *`
getParameter(result).getType() instanceof ReferenceType or // e.g. `std::basic_string &`
getParameter(result).getUnspecifiedType() =
getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. `std::basic_string::CharT`
}
/**
* Gets the index of a parameter to this function that is an iterator.
*/
int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from string and parameter to string (qualifier) and return value
(
input.isQualifierObject() or
input.isParameterDeref(getAStringParameter())
input.isParameterDeref(getAStringParameterIndex()) or
input.isParameter(getAnIteratorParameterIndex())
) and
(
output.isQualifierObject() or
output.isReturnValueDeref()
)
or
// reverse flow from returned reference to the qualifier (for writes to
// the result)
input.isReturnValueDeref() and
output.isQualifierObject()
}
}
@@ -88,19 +182,50 @@ class StdStringAssign extends TaintFunction {
* Gets the index of a parameter to this function that is a string (or
* character).
*/
int getAStringParameter() {
getParameter(result).getType() instanceof PointerType or
getParameter(result).getType() instanceof ReferenceType or
getParameter(result).getType() = getDeclaringType().getTemplateArgument(0) // i.e. `std::basic_string::CharT`
int getAStringParameterIndex() {
getParameter(result).getType() instanceof PointerType or // e.g. `std::basic_string::CharT *`
getParameter(result).getType() instanceof ReferenceType or // e.g. `std::basic_string &`
getParameter(result).getUnspecifiedType() =
getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. `std::basic_string::CharT`
}
/**
* Gets the index of a parameter to this function that is an iterator.
*/
int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from parameter to string itself (qualifier) and return value
input.isParameterDeref(getAStringParameter()) and
(
input.isParameterDeref(getAStringParameterIndex()) or
input.isParameter(getAnIteratorParameterIndex())
) and
(
output.isQualifierObject() or
output.isReturnValueDeref()
)
or
// reverse flow from returned reference to the qualifier (for writes to
// the result)
input.isReturnValueDeref() and
output.isQualifierObject()
}
}
/**
* The standard functions `std::string.begin` and `std::string.end` and their
* variants.
*/
class StdStringBeginEnd extends TaintFunction {
StdStringBeginEnd() {
this
.hasQualifiedName("std", "basic_string",
["begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend"])
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isQualifierObject() and
output.isReturnValue()
}
}
@@ -145,3 +270,85 @@ class StdStringSwap extends TaintFunction {
output.isQualifierObject()
}
}
/**
* The `std::string` functions `at` and `operator[]`.
*/
class StdStringAt extends TaintFunction {
StdStringAt() { this.hasQualifiedName("std", "basic_string", ["at", "operator[]"]) }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from qualifier to referenced return value
input.isQualifierObject() and
output.isReturnValueDeref()
or
// reverse flow from returned reference to the qualifier
input.isReturnValueDeref() and
output.isQualifierObject()
}
}
/**
* The `std::basic_ostream` template class.
*/
class StdBasicOStream extends TemplateClass {
StdBasicOStream() { this.hasQualifiedName("std", "basic_ostream") }
}
/**
* The `std::ostream` function `operator<<` (defined as a member function).
*/
class StdOStreamOut extends DataFlowFunction, TaintFunction {
StdOStreamOut() { this.hasQualifiedName("std", "basic_ostream", "operator<<") }
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
// flow from qualifier to return value
input.isQualifierAddress() and
output.isReturnValue()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from parameter to qualifier
input.isParameter(0) and
output.isQualifierObject()
or
// flow from parameter to return value
input.isParameter(0) and
output.isReturnValueDeref()
or
// reverse flow from returned reference to the qualifier
input.isReturnValueDeref() and
output.isQualifierObject()
}
}
/**
* The `std::ostream` function `operator<<` (defined as a non-member function).
*/
class StdOStreamOutNonMember extends DataFlowFunction, TaintFunction {
StdOStreamOutNonMember() {
this.hasQualifiedName("std", "operator<<") and
this.getUnspecifiedType().(ReferenceType).getBaseType() =
any(StdBasicOStream s).getAnInstantiation()
}
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
// flow from first parameter to return value
input.isParameter(0) and
output.isReturnValue()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from second parameter to first parameter
input.isParameter(1) and
output.isParameterDeref(0)
or
// flow from second parameter to return value
input.isParameter(1) and
output.isReturnValueDeref()
or
// reverse flow from returned reference to the first parameter
input.isReturnValueDeref() and
output.isParameterDeref(0)
}
}

View File

@@ -10,7 +10,8 @@ private newtype TFunctionInput =
TInParameter(ParameterIndex i) or
TInParameterDeref(ParameterIndex i) or
TInQualifierObject() or
TInQualifierAddress()
TInQualifierAddress() or
TInReturnValueDeref()
/**
* An input to a function. This can be:
@@ -106,6 +107,31 @@ class FunctionInput extends TFunctionInput {
* (with type `C const *`) on entry to the function.
*/
predicate isQualifierAddress() { none() }
/**
* Holds if this is the input value pointed to by the return value of a
* function, if the function returns a pointer, or the input value referred
* to by the return value of a function, if the function returns a reference.
*
* Example:
* ```
* char* getPointer();
* float& getReference();
* int getInt();
* ```
* - `isReturnValueDeref()` holds for the `FunctionInput` that represents the
* value of `*getPointer()` (with type `char`).
* - `isReturnValueDeref()` holds for the `FunctionInput` that represents the
* value of `getReference()` (with type `float`).
* - There is no `FunctionInput` of `getInt()` for which
* `isReturnValueDeref()` holds because the return type of `getInt()` is
* neither a pointer nor a reference.
*
* Note that data flows in through function return values are relatively
* rare, but they do occur when a function returns a reference to itself,
* part of itself, or one of its other inputs.
*/
predicate isReturnValueDeref() { none() }
}
/**
@@ -199,6 +225,34 @@ class InQualifierAddress extends FunctionInput, TInQualifierAddress {
override predicate isQualifierAddress() { any() }
}
/**
* The input value pointed to by the return value of a function, if the
* function returns a pointer, or the input value referred to by the return
* value of a function, if the function returns a reference.
*
* Example:
* ```
* char* getPointer();
* float& getReference();
* int getInt();
* ```
* - `InReturnValueDeref` represents the value of `*getPointer()` (with type
* `char`).
* - `InReturnValueDeref` represents the value of `getReference()` (with type
* `float`).
* - `InReturnValueDeref` does not represent the return value of `getInt()`
* because the return type of `getInt()` is neither a pointer nor a reference.
*
* Note that data flows in through function return values are relatively
* rare, but they do occur when a function returns a reference to itself,
* part of itself, or one of its other inputs.
*/
class InReturnValueDeref extends FunctionInput, TInReturnValueDeref {
override string toString() { result = "InReturnValueDeref" }
override predicate isReturnValueDeref() { any() }
}
private newtype TFunctionOutput =
TOutParameterDeref(ParameterIndex i) or
TOutQualifierObject() or

View File

@@ -191,6 +191,8 @@ private predicate linearAccessImpl(Expr expr, VariableAccess v, float p, float q
// Base case
expr = v and p = 1.0 and q = 0.0
or
expr.(ReferenceDereferenceExpr).getExpr() = v and p = 1.0 and q = 0.0
or
// a+(p*v+b) == p*v + (a+b)
exists(AddExpr addExpr, float a, float b |
addExpr.getLeftOperand().isConstant() and
@@ -349,13 +351,20 @@ private predicate typeBounds(ArithmeticType t, float lb, float ub) {
t instanceof FloatingPointType and lb = -(1.0 / 0.0) and ub = 1.0 / 0.0
}
private Type stripReference(Type t) {
if t instanceof ReferenceType then result = t.(ReferenceType).getBaseType() else result = t
}
/** Gets the type used by range analysis for the given `StackVariable`. */
Type getVariableRangeType(StackVariable v) { result = stripReference(v.getUnspecifiedType()) }
/**
* Gets the lower bound for the unspecified type `t`.
*
* For example, if `t` is a signed 32-bit type then the result is
* `-2^31`.
*/
float typeLowerBound(ArithmeticType t) { typeBounds(t, result, _) }
float typeLowerBound(Type t) { typeBounds(stripReference(t), result, _) }
/**
* Gets the upper bound for the unspecified type `t`.
@@ -363,7 +372,7 @@ float typeLowerBound(ArithmeticType t) { typeBounds(t, result, _) }
* For example, if `t` is a signed 32-bit type then the result is
* `2^31 - 1`.
*/
float typeUpperBound(ArithmeticType t) { typeBounds(t, _, result) }
float typeUpperBound(Type t) { typeBounds(stripReference(t), _, result) }
/**
* Gets the minimum value that this expression could represent, based on

View File

@@ -319,28 +319,12 @@ private predicate defDependsOnDef(
// Definitions with a defining value.
exists(Expr expr | assignmentDef(def, v, expr) | exprDependsOnDef(expr, srcDef, srcVar))
or
exists(AssignAddExpr assignAdd |
def = assignAdd and
// Assignment operations with a defining value
exists(AssignOperation assignOp |
analyzableExpr(assignOp) and
def = assignOp and
def.getAVariable() = v and
exprDependsOnDef(assignAdd.getAnOperand(), srcDef, srcVar)
)
or
exists(AssignSubExpr assignSub |
def = assignSub and
def.getAVariable() = v and
exprDependsOnDef(assignSub.getAnOperand(), srcDef, srcVar)
)
or
exists(UnsignedAssignMulExpr assignMul |
def = assignMul and
def.getAVariable() = v and
exprDependsOnDef(assignMul.getAnOperand(), srcDef, srcVar)
)
or
exists(AssignMulByConstantExpr assignMul |
def = assignMul and
def.getAVariable() = v and
exprDependsOnDef(assignMul.getLValue(), srcDef, srcVar)
exprDependsOnDef(assignOp, srcDef, srcVar)
)
or
exists(CrementOperation crem |
@@ -485,7 +469,7 @@ private predicate isRecursiveDef(RangeSsaDefinition def, StackVariable v) {
* This predicate finds all the definitions in the first set.
*/
private predicate assignmentDef(RangeSsaDefinition def, StackVariable v, Expr expr) {
v.getUnspecifiedType() instanceof ArithmeticType and
getVariableRangeType(v) instanceof ArithmeticType and
(
def = v.getInitializer().getExpr() and def = expr
or
@@ -1160,6 +1144,17 @@ private float getPhiLowerBounds(StackVariable v, RangeSsaDefinition phi) {
if guardLB > defLB then result = guardLB else result = defLB
)
or
exists(VariableAccess access, float neConstant, float lower |
isNEPhi(v, phi, access, neConstant) and
lower = getFullyConvertedLowerBounds(access) and
if lower = neConstant then result = lower + 1 else result = lower
)
or
exists(VariableAccess access |
isUnsupportedGuardPhi(v, phi, access) and
result = getFullyConvertedLowerBounds(access)
)
or
result = getDefLowerBounds(phi.getAPhiInput(v), v)
}
@@ -1177,6 +1172,17 @@ private float getPhiUpperBounds(StackVariable v, RangeSsaDefinition phi) {
if guardUB < defUB then result = guardUB else result = defUB
)
or
exists(VariableAccess access, float neConstant, float upper |
isNEPhi(v, phi, access, neConstant) and
upper = getFullyConvertedUpperBounds(access) and
if upper = neConstant then result = upper - 1 else result = upper
)
or
exists(VariableAccess access |
isUnsupportedGuardPhi(v, phi, access) and
result = getFullyConvertedUpperBounds(access)
)
or
result = getDefUpperBounds(phi.getAPhiInput(v), v)
}
@@ -1185,42 +1191,11 @@ private float getDefLowerBoundsImpl(RangeSsaDefinition def, StackVariable v) {
// Definitions with a defining value.
exists(Expr expr | assignmentDef(def, v, expr) | result = getFullyConvertedLowerBounds(expr))
or
exists(AssignAddExpr assignAdd, RangeSsaDefinition nextDef, float lhsLB, float rhsLB |
def = assignAdd and
assignAdd.getLValue() = nextDef.getAUse(v) and
lhsLB = getDefLowerBounds(nextDef, v) and
rhsLB = getFullyConvertedLowerBounds(assignAdd.getRValue()) and
result = addRoundingDown(lhsLB, rhsLB)
)
or
exists(AssignSubExpr assignSub, RangeSsaDefinition nextDef, float lhsLB, float rhsUB |
def = assignSub and
assignSub.getLValue() = nextDef.getAUse(v) and
lhsLB = getDefLowerBounds(nextDef, v) and
rhsUB = getFullyConvertedUpperBounds(assignSub.getRValue()) and
result = addRoundingDown(lhsLB, -rhsUB)
)
or
exists(UnsignedAssignMulExpr assignMul, RangeSsaDefinition nextDef, float lhsLB, float rhsLB |
def = assignMul and
assignMul.getLValue() = nextDef.getAUse(v) and
lhsLB = getDefLowerBounds(nextDef, v) and
rhsLB = getFullyConvertedLowerBounds(assignMul.getRValue()) and
result = lhsLB * rhsLB
)
or
exists(AssignMulByPositiveConstantExpr assignMul, RangeSsaDefinition nextDef, float lhsLB |
def = assignMul and
assignMul.getLValue() = nextDef.getAUse(v) and
lhsLB = getDefLowerBounds(nextDef, v) and
result = lhsLB * assignMul.getConstant()
)
or
exists(AssignMulByNegativeConstantExpr assignMul, RangeSsaDefinition nextDef, float lhsUB |
def = assignMul and
assignMul.getLValue() = nextDef.getAUse(v) and
lhsUB = getDefUpperBounds(nextDef, v) and
result = lhsUB * assignMul.getConstant()
// Assignment operations with a defining value
exists(AssignOperation assignOp |
def = assignOp and
assignOp.getLValue() = v.getAnAccess() and
result = getTruncatedLowerBounds(assignOp)
)
or
exists(IncrementOperation incr, float newLB |
@@ -1249,42 +1224,11 @@ private float getDefUpperBoundsImpl(RangeSsaDefinition def, StackVariable v) {
// Definitions with a defining value.
exists(Expr expr | assignmentDef(def, v, expr) | result = getFullyConvertedUpperBounds(expr))
or
exists(AssignAddExpr assignAdd, RangeSsaDefinition nextDef, float lhsUB, float rhsUB |
def = assignAdd and
assignAdd.getLValue() = nextDef.getAUse(v) and
lhsUB = getDefUpperBounds(nextDef, v) and
rhsUB = getFullyConvertedUpperBounds(assignAdd.getRValue()) and
result = addRoundingUp(lhsUB, rhsUB)
)
or
exists(AssignSubExpr assignSub, RangeSsaDefinition nextDef, float lhsUB, float rhsLB |
def = assignSub and
assignSub.getLValue() = nextDef.getAUse(v) and
lhsUB = getDefUpperBounds(nextDef, v) and
rhsLB = getFullyConvertedLowerBounds(assignSub.getRValue()) and
result = addRoundingUp(lhsUB, -rhsLB)
)
or
exists(UnsignedAssignMulExpr assignMul, RangeSsaDefinition nextDef, float lhsUB, float rhsUB |
def = assignMul and
assignMul.getLValue() = nextDef.getAUse(v) and
lhsUB = getDefUpperBounds(nextDef, v) and
rhsUB = getFullyConvertedUpperBounds(assignMul.getRValue()) and
result = lhsUB * rhsUB
)
or
exists(AssignMulByPositiveConstantExpr assignMul, RangeSsaDefinition nextDef, float lhsUB |
def = assignMul and
assignMul.getLValue() = nextDef.getAUse(v) and
lhsUB = getDefUpperBounds(nextDef, v) and
result = lhsUB * assignMul.getConstant()
)
or
exists(AssignMulByNegativeConstantExpr assignMul, RangeSsaDefinition nextDef, float lhsLB |
def = assignMul and
assignMul.getLValue() = nextDef.getAUse(v) and
lhsLB = getDefLowerBounds(nextDef, v) and
result = lhsLB * assignMul.getConstant()
// Assignment operations with a defining value
exists(AssignOperation assignOp |
def = assignOp and
assignOp.getLValue() = v.getAnAccess() and
result = getTruncatedUpperBounds(assignOp)
)
or
exists(IncrementOperation incr, float newUB |
@@ -1329,7 +1273,7 @@ private float getDefLowerBounds(RangeSsaDefinition def, StackVariable v) {
// recursion from exploding.
result =
max(float widenLB |
widenLB = wideningLowerBounds(v.getUnspecifiedType()) and
widenLB = wideningLowerBounds(getVariableRangeType(v)) and
not widenLB > truncatedLB
|
widenLB
@@ -1359,7 +1303,7 @@ private float getDefUpperBounds(RangeSsaDefinition def, StackVariable v) {
// from exploding.
result =
min(float widenUB |
widenUB = wideningUpperBounds(v.getUnspecifiedType()) and
widenUB = wideningUpperBounds(getVariableRangeType(v)) and
not widenUB < truncatedUB
|
widenUB
@@ -1391,9 +1335,10 @@ private predicate unanalyzableDefBounds(RangeSsaDefinition def, StackVariable v,
*/
bindingset[guard, v, branch]
predicate nonNanGuardedVariable(ComparisonOperation guard, VariableAccess v, boolean branch) {
v.getUnspecifiedType() instanceof IntegralType
getVariableRangeType(v.getTarget()) instanceof IntegralType
or
v.getUnspecifiedType() instanceof FloatingPointType and v instanceof NonNanVariableAccess
getVariableRangeType(v.getTarget()) instanceof FloatingPointType and
v instanceof NonNanVariableAccess
or
// The reason the following case is here is to ensure that when we say
// `if (x > 5) { ...then... } else { ...else... }`
@@ -1418,7 +1363,7 @@ private predicate lowerBoundFromGuard(
then
if
strictness = Nonstrict() or
not v.getUnspecifiedType() instanceof IntegralType
not getVariableRangeType(v.getTarget()) instanceof IntegralType
then lb = childLB
else lb = childLB + 1
else lb = varMinVal(v.getTarget())
@@ -1440,7 +1385,7 @@ private predicate upperBoundFromGuard(
then
if
strictness = Nonstrict() or
not v.getUnspecifiedType() instanceof IntegralType
not getVariableRangeType(v.getTarget()) instanceof IntegralType
then ub = childUB
else ub = childUB - 1
else ub = varMaxVal(v.getTarget())
@@ -1500,22 +1445,13 @@ private predicate linearBoundFromGuard(
// 1. x <= upperbound(RHS)
// 2. x >= lowerbound(RHS)
//
// For x != RHS, we create trivial bounds:
//
// 1. x <= typeUpperBound(RHS.getUnspecifiedType())
// 2. x >= typeLowerBound(RHS.getUnspecifiedType())
//
exists(Expr lhs, Expr rhs, boolean isEQ |
exists(Expr lhs, Expr rhs |
linearAccess(lhs, v, p, q) and
eqOpWithSwapAndNegate(guard, lhs, rhs, isEQ, branch) and
eqOpWithSwapAndNegate(guard, lhs, rhs, true, branch) and
getBounds(rhs, boundValue, isLowerBound) and
strictness = Nonstrict()
|
// True branch
isEQ = true and getBounds(rhs, boundValue, isLowerBound)
or
// False branch: set the bounds to the min/max for the type.
isEQ = false and exprTypeBounds(rhs, boundValue, isLowerBound)
)
// x != RHS and !x are handled elsewhere
}
/** Utility for `linearBoundFromGuard`. */
@@ -1532,6 +1468,42 @@ private predicate exprTypeBounds(Expr expr, float boundValue, boolean isLowerBou
isLowerBound = false and boundValue = exprMaxVal(expr.getFullyConverted())
}
/**
* Holds if `(v, phi)` ensures that `access` is not equal to `neConstant`. For
* example, the condition `if (x + 1 != 3)` ensures that `x` is not equal to 2.
* Only integral types are supported.
*/
private predicate isNEPhi(
Variable v, RangeSsaDefinition phi, VariableAccess access, float neConstant
) {
exists(
ComparisonOperation cmp, boolean branch, Expr linearExpr, Expr rExpr, float p, float q, float r
|
access.getTarget() = v and
phi.isGuardPhi(access, cmp, branch) and
eqOpWithSwapAndNegate(cmp, linearExpr, rExpr, false, branch) and
v.getUnspecifiedType() instanceof IntegralOrEnumType and // Float `!=` is too imprecise
r = getValue(rExpr).toFloat() and
linearAccess(linearExpr, access, p, q) and
neConstant = (r - q) / p
)
}
/**
* Holds if `(v, phi)` constrains the value of `access` but in a way that
* doesn't allow this library to constrain the upper or lower bounds of
* `access`. An example is `if (x != y)` if neither `x` nor `y` is a
* compile-time constant.
*/
private predicate isUnsupportedGuardPhi(Variable v, RangeSsaDefinition phi, VariableAccess access) {
exists(ComparisonOperation cmp, boolean branch |
access.getTarget() = v and
phi.isGuardPhi(access, cmp, branch) and
eqOpWithSwapAndNegate(cmp, _, _, false, branch) and
not isNEPhi(v, phi, access, _)
)
}
cached
private module SimpleRangeAnalysisCached {
/**

View File

@@ -17,8 +17,8 @@ import semmle.code.cpp.stmts.Stmt
* }
* ```
*/
class Block extends Stmt, @stmt_block {
override string getAPrimaryQlClass() { result = "Block" }
class BlockStmt extends Stmt, @stmt_block {
override string getAPrimaryQlClass() { result = "BlockStmt" }
/**
* Gets a child declaration of this block.
@@ -76,8 +76,8 @@ class Block extends Stmt, @stmt_block {
* the result is the expression statement `a = b`.
*/
Stmt getLastStmtIn() {
if getLastStmt() instanceof Block
then result = getLastStmt().(Block).getLastStmtIn()
if getLastStmt() instanceof BlockStmt
then result = getLastStmt().(BlockStmt).getLastStmtIn()
else result = getLastStmt()
}
@@ -126,3 +126,9 @@ class Block extends Stmt, @stmt_block {
override predicate mayBeGloballyImpure() { this.getAStmt().mayBeGloballyImpure() }
}
/**
* DEPRECATED: This is now called `BlockStmt` to avoid confusion with
* `BasicBlock`.
*/
deprecated class Block = BlockStmt;

View File

@@ -25,10 +25,10 @@ class Stmt extends StmtParent, @stmt {
/**
* Gets the nearest enclosing block of this statement in the source, if any.
*/
Block getEnclosingBlock() {
BlockStmt getEnclosingBlock() {
if
getParentStmt() instanceof Block and
not getParentStmt().(Block).getLocation() instanceof UnknownLocation
getParentStmt() instanceof BlockStmt and
not getParentStmt().(BlockStmt).getLocation() instanceof UnknownLocation
then result = getParentStmt()
else result = getParentStmt().getEnclosingBlock()
}
@@ -53,7 +53,7 @@ class Stmt extends StmtParent, @stmt {
* to trace the flow of control instead.
*/
Stmt getFollowingStmt() {
exists(Block b, int i |
exists(BlockStmt b, int i |
this = b.getStmt(i) and
result = b.getStmt(i + 1)
)
@@ -240,7 +240,7 @@ class IfStmt extends ConditionalStmt, @stmt_if {
* ```
* if (b) { x = 1; }
* ```
* the result is the `Block` `{ x = 1; }`.
* the result is the `BlockStmt` `{ x = 1; }`.
*/
Stmt getThen() { if_then(underlyingElement(this), unresolveElement(result)) }
@@ -251,7 +251,7 @@ class IfStmt extends ConditionalStmt, @stmt_if {
* ```
* if (b) { x = 1; } else { x = 2; }
* ```
* the result is the `Block` `{ x = 2; }`, and for
* the result is the `BlockStmt` `{ x = 2; }`, and for
* ```
* if (b) { x = 1; }
* ```
@@ -326,7 +326,7 @@ class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if {
* ```
* if constexpr (b) { x = 1; }
* ```
* the result is the `Block` `{ x = 1; }`.
* the result is the `BlockStmt` `{ x = 1; }`.
*/
Stmt getThen() { constexpr_if_then(underlyingElement(this), unresolveElement(result)) }
@@ -337,7 +337,7 @@ class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if {
* ```
* if constexpr (b) { x = 1; } else { x = 2; }
* ```
* the result is the `Block` `{ x = 2; }`, and for
* the result is the `BlockStmt` `{ x = 2; }`, and for
* ```
* if constexpr (b) { x = 1; }
* ```
@@ -662,6 +662,67 @@ class LabelStmt extends Stmt, @stmt_label {
override predicate mayBeGloballyImpure() { none() }
}
/**
* A C/C++ `co_return` statement.
*
* For example:
* ```
* co_return 1+2;
* ```
* or
* ```
* co_return;
* ```
*/
class CoReturnStmt extends Stmt, @stmt_co_return {
override string getAPrimaryQlClass() { result = "CoReturnStmt" }
/**
* Gets the operand of this 'co_return' statement.
*
* For example, for
* ```
* co_return 1+2;
* ```
* the operand is a function call `return_value(1+2)`, and for
* ```
* co_return;
* ```
* the operand is a function call `return_void()`.
*/
FunctionCall getOperand() { result = this.getChild(0) }
/**
* Gets the expression of this 'co_return' statement, if any.
*
* For example, for
* ```
* co_return 1+2;
* ```
* the result is `1+2`, and there is no result for
* ```
* co_return;
* ```
*/
Expr getExpr() { result = this.getOperand().getArgument(0) }
/**
* Holds if this 'co_return' statement has an expression.
*
* For example, this holds for
* ```
* co_return 1+2;
* ```
* but not for
* ```
* co_return;
* ```
*/
predicate hasExpr() { exists(this.getExpr()) }
override string toString() { result = "co_return ..." }
}
/**
* A C/C++ 'return' statement.
*
@@ -781,7 +842,7 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for {
* ```
* for (int x : xs) { y += x; }
* ```
* the result is the `Block` `{ y += x; }`.
* the result is the `BlockStmt` `{ y += x; }`.
*/
override Stmt getStmt() { result = this.getChild(5) }
@@ -1168,7 +1229,7 @@ class SwitchCase extends Stmt, @stmt_switch_case {
* DEPRECATED: use `SwitchCase.getAStmt` or `ControlFlowNode.getASuccessor`
* rather than this predicate.
*
* Gets the `Block` statement immediately following this 'switch case'
* Gets the `BlockStmt` statement immediately following this 'switch case'
* statement, if any.
*
* For example, for
@@ -1189,7 +1250,7 @@ class SwitchCase extends Stmt, @stmt_switch_case {
* the `case 7:` has result `{ x = 2; break; }`, `default:` has result
* `{ x = 3; }`, and the others have no result.
*/
deprecated Block getLabelledStmt() {
deprecated BlockStmt getLabelledStmt() {
exists(int i, Stmt parent |
this = parent.getChild(i) and
result = parent.getChild(i + 1)
@@ -1270,7 +1331,7 @@ class SwitchCase extends Stmt, @stmt_switch_case {
* `default:` has results `{ x = 3; }, `x = 4;` and `break;`.
*/
Stmt getAStmt() {
exists(Block b, int i, int j |
exists(BlockStmt b, int i, int j |
b.getStmt(i) = this and
b.getStmt(j) = result and
i < j and
@@ -1309,8 +1370,8 @@ class SwitchCase extends Stmt, @stmt_switch_case {
exists(Stmt lastStmt |
lastStmt = this.getAStmt() and
not lastStmt.getFollowingStmt() = this.getAStmt() and
if lastStmt instanceof Block
then result = lastStmt.(Block).getLastStmtIn()
if lastStmt instanceof BlockStmt
then result = lastStmt.(BlockStmt).getLastStmtIn()
else result = lastStmt
)
}
@@ -1467,7 +1528,7 @@ class SwitchStmt extends ConditionalStmt, @stmt_switch {
/**
* Gets the body statement of this 'switch' statement.
*
* In almost all cases the result will be a `Block`, but there are
* In almost all cases the result will be a `BlockStmt`, but there are
* other syntactically valid constructions.
*
* For example, for
@@ -1798,7 +1859,7 @@ class FunctionTryStmt extends TryStmt {
* }
* ```
*/
class CatchBlock extends Block {
class CatchBlock extends BlockStmt {
override string getAPrimaryQlClass() { result = "CatchBlock" }
CatchBlock() { ishandler(underlyingElement(this)) }
@@ -1864,7 +1925,7 @@ class MicrosoftTryExceptStmt extends MicrosoftTryStmt {
/** Gets the expression guarding the `__except` statement. */
Expr getCondition() { result = getChild(1) }
/** Gets the `__except` statement (usually a `Block`). */
/** Gets the `__except` statement (usually a `BlockStmt`). */
Stmt getExcept() { result = getChild(2) }
override string getAPrimaryQlClass() { result = "MicrosoftTryExceptStmt" }
@@ -1888,7 +1949,7 @@ class MicrosoftTryFinallyStmt extends MicrosoftTryStmt {
override string toString() { result = "__try { ... } __finally { ... }" }
/** Gets the `__finally` statement (usually a `Block`). */
/** Gets the `__finally` statement (usually a `BlockStmt`). */
Stmt getFinally() { result = getChild(1) }
override string getAPrimaryQlClass() { result = "MicrosoftTryFinallyStmt" }
@@ -2047,7 +2108,7 @@ class VlaDeclStmt extends Stmt, @stmt_vla_decl {
* declaration statement.
*/
int getNumberOfVlaDimensionStmts() {
exists(Block b, int j |
exists(BlockStmt b, int j |
this = b.getStmt(j) and
result =
j - 1 -
@@ -2064,7 +2125,7 @@ class VlaDeclStmt extends Stmt, @stmt_vla_decl {
*/
VlaDimensionStmt getVlaDimensionStmt(int i) {
i in [0 .. this.getNumberOfVlaDimensionStmts() - 1] and
exists(Block b, int j |
exists(BlockStmt b, int j |
this = b.getStmt(j) and
result = b.getStmt(j - this.getNumberOfVlaDimensionStmts() + i)
)

View File

@@ -1228,6 +1228,8 @@ funbind(
| @builtinaddressof
| @vec_fill
| @un_log_op_expr
| @co_await
| @co_yield
;
@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr;
@@ -1647,6 +1649,8 @@ case @expr.kind of
| 324 = @builtinconvertvector
| 325 = @builtincomplex
| 326 = @spaceshipexpr
| 327 = @co_await
| 328 = @co_yield
;
@var_args_expr = @vastartexpr
@@ -1851,6 +1855,7 @@ case @stmt.kind of
| 33 = @stmt_handler
// ... 34 @stmt_finally_end deprecated
| 35 = @stmt_constexpr_if
| 37 = @stmt_co_return
;
type_vla(
@@ -1935,20 +1940,6 @@ stmtparents(
ishandler(unique int block: @stmt_block ref);
@cfgnode = @stmt | @expr | @function | @initialiser ;
successors(
int from: @cfgnode ref,
int to: @cfgnode ref
);
truecond(
unique int from: @cfgnode ref,
int to: @cfgnode ref
);
falsecond(
unique int from: @cfgnode ref,
int to: @cfgnode ref
);
stmt_decl_bind(
int stmt: @stmt_decl ref,

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,11 @@
* Provides a library for writing QL tests whose success or failure is based on expected results
* embedded in the test source code as comments, rather than a `.expected` file.
*
* To add this framework to a new language:
* - Add a file `InlineExpectationsTestPrivate.qll` that defines a `LineComment` class. This class
* must support a `getContents` method that returns the contents of the given comment, _excluding_
* the comment indicator itself. It should also define `toString` and `getLocation` as usual.
*
* To create a new inline expectations test:
* - Declare a class that extends `InlineExpectationsTest`. In the characteristic predicate of the
* new class, bind `this` to a unique string (usually the name of the test).
@@ -13,7 +18,7 @@
* `hasActualResult()`. Often this is just a single tag.
*
* Example:
* ```
* ```ql
* class ConstantValueTest extends InlineExpectationsTest {
* ConstantValueTest() { this = "ConstantValueTest" }
*
@@ -23,11 +28,11 @@
* }
*
* override predicate hasActualResult(
* Location location, string element, string tag, string valuesasas
* Location location, string element, string tag, string value
* ) {
* exists(Expr e |
* tag = "const" and // The tag for this test.
* valuesasas = e.getValue() and // The expected value. Will only hold for constant expressions.
* value = e.getValue() and // The expected value. Will only hold for constant expressions.
* location = e.getLocation() and // The location of the result to be reported.
* element = e.toString() // The display text for the result.
* )
@@ -38,10 +43,10 @@
* There is no need to write a `select` clause or query predicate. All of the differences between
* expected results and actual results will be reported in the `failures()` query predicate.
*
* To annotate the test source code with an expected result, place a C++-style (`//`) comment on the
* To annotate the test source code with an expected result, place a comment on the
* same line as the expected result, with text of the following format as the body of the comment:
*
* `// $tag=expected-value`
* `$tag=expected-value`
*
* Where `tag` is the value of the `tag` parameter from `hasActualResult()`, and `expected-value` is
* the value of the `value` parameter from `hasActualResult()`. The `=expected-value` portion may be
@@ -53,7 +58,7 @@
* "Missing result: tag=expected-value".
*
* Example:
* ```
* ```cpp
* int i = x + 5; // $const=5
* int j = y + (7 - 3) // $const=7 $const=3 $const=4 // The result of the subtraction is a constant.
* ```
@@ -62,8 +67,8 @@
* annotate that a particular expected result is known to be a false positive, or that a particular
* missing result is known to be a false negative:
*
* `// $f+:tag=expected-value` // False positive
* `// $f-:tag=expected-value` // False negative
* `$f+:tag=expected-value` // False positive
* `$f-:tag=expected-value` // False negative
*
* A false positive expectation is treated as any other expected result, except that if there is no
* matching actual result, the message will be of the form "Fixed false positive: tag=value". A
@@ -74,14 +79,14 @@
* If the same result value is expected for two or more tags on the same line, there is a shorthand
* notation available:
*
* `// $tag1,tag2=expected-value`
* `$tag1,tag2=expected-value`
*
* is equivalent to:
*
* `// $tag1=expected-value $tag2=expected-value`
* `$tag1=expected-value $tag2=expected-value`
*/
import cpp
private import InlineExpectationsTestPrivate
/**
* Base class for tests with inline expectations. The test extends this class to provide the actual
@@ -150,12 +155,12 @@ abstract class InlineExpectationsTest extends string {
}
/**
* RegEx pattern to match a comment containing one or more expected results. The comment must be a
* C++-style (`//`) comment with `$` as its first non-whitespace character. Any subsequent character
* RegEx pattern to match a comment containing one or more expected results. The comment must have
* `$` as its first non-whitespace character. Any subsequent character
* is treated as part of the expected results, except that the comment may contain a `//` sequence
* to treat the remainder of the line as a regular (non-interpreted) comment.
*/
private string expectationCommentPattern() { result = "//\\s*(\\$(?:[^/]|/[^/])*)(?://.*)?" }
private string expectationCommentPattern() { result = "\\s*(\\$(?:[^/]|/[^/])*)(?://.*)?" }
/**
* RegEx pattern to match a single expected result, not including the leading `$`. It starts with an
@@ -166,7 +171,7 @@ private string expectationPattern() {
result = "(?:(f(?:\\+|-)):)?((?:[A-Za-z-_]+)(?:\\s*,\\s*[A-Za-z-_]+)*)(?:=(.*))?"
}
private string getAnExpectation(CppStyleComment comment) {
private string getAnExpectation(LineComment comment) {
result = comment.getContents().regexpCapture(expectationCommentPattern(), 1).splitAt("$").trim() and
result != ""
}
@@ -177,7 +182,7 @@ private newtype TFailureLocatable =
) {
test.hasActualResult(location, element, tag, value)
} or
TValidExpectation(CppStyleComment comment, string tag, string value, string knownFailure) {
TValidExpectation(LineComment comment, string tag, string value, string knownFailure) {
exists(string expectation |
expectation = getAnExpectation(comment) and
expectation.regexpMatch(expectationPattern()) and
@@ -194,7 +199,7 @@ private newtype TFailureLocatable =
)
)
} or
TInvalidExpectation(CppStyleComment comment, string expectation) {
TInvalidExpectation(LineComment comment, string expectation) {
expectation = getAnExpectation(comment) and
not expectation.regexpMatch(expectationPattern())
}
@@ -232,7 +237,7 @@ class ActualResult extends FailureLocatable, TActualResult {
}
abstract private class Expectation extends FailureLocatable {
CppStyleComment comment;
LineComment comment;
override string toString() { result = comment.toString() }

View File

@@ -0,0 +1,23 @@
import cpp
private newtype TLineComment = MkLineComment(CppStyleComment c)
/**
* Represents a line comment in the CPP style.
* Unlike the `CppStyleComment` class, however, the string returned by `getContents` does _not_
* include the preceding comment marker (`//`).
*/
class LineComment extends TLineComment {
CppStyleComment comment;
LineComment() { this = MkLineComment(comment) }
/** Returns the contents of the given comment, _without_ the preceding comment marker (`//`). */
string getContents() { result = comment.getContents().suffix(2) }
/** Gets a textual representation of this element. */
string toString() { result = comment.toString() }
/** Gets the location of this comment. */
Location getLocation() { result = comment.getLocation() }
}

View File

@@ -19,7 +19,7 @@ AddressOf.c:
# 1| params:
# 1| 0: [Parameter] i
# 1| Type = [IntType] int
# 1| body: [Block] { ... }
# 1| body: [BlockStmt] { ... }
# 2| 0: [DeclStmt] declaration
# 2| 0: [VariableDeclarationEntry] definition of j
# 2| Type = [IntPointerType] int *
@@ -34,7 +34,7 @@ AddressOf.c:
ArrayToPointer.c:
# 5| [TopLevelFunction] void ArrayToPointer()
# 5| params:
# 6| body: [Block] { ... }
# 6| body: [BlockStmt] { ... }
# 7| 0: [DeclStmt] declaration
# 7| 0: [VariableDeclarationEntry] definition of c
# 7| Type = [ArrayType] char[]
@@ -70,7 +70,7 @@ Cast.c:
# 1| Type = [CharPointerType] char *
# 1| 1: [Parameter] v
# 1| Type = [VoidPointerType] void *
# 1| body: [Block] { ... }
# 1| body: [BlockStmt] { ... }
# 2| 0: [ExprStmt] ExprStmt
# 2| 0: [AssignExpr] ... = ...
# 2| Type = [CharPointerType] char *
@@ -89,7 +89,7 @@ Cast.c:
ConditionDecl.cpp:
# 1| [TopLevelFunction] void ConditionDecl()
# 1| params:
# 1| body: [Block] { ... }
# 1| body: [BlockStmt] { ... }
# 2| 0: [DeclStmt] declaration
# 2| 0: [VariableDeclarationEntry] definition of j
# 2| Type = [IntType] int
@@ -109,7 +109,7 @@ ConditionDecl.cpp:
# 3| expr: [VariableAccess] k
# 3| Type = [IntType] int
# 3| ValueCategory = prvalue(load)
# 3| 1: [Block] { ... }
# 3| 1: [BlockStmt] { ... }
# 5| 2: [ReturnStmt] return ...
ConstructorCall.cpp:
# 1| [CopyAssignmentOperator] C& C::operator=(C const&)
@@ -133,7 +133,7 @@ ConstructorCall.cpp:
# 3| 0: [Parameter] i
# 3| Type = [IntType] int
# 3| initializations:
# 3| body: [Block] { ... }
# 3| body: [BlockStmt] { ... }
# 4| 0: [ReturnStmt] return ...
# 7| [CopyAssignmentOperator] D& D::operator=(D const&)
# 7| params:
@@ -154,7 +154,7 @@ ConstructorCall.cpp:
# 9| [Constructor] void D::D()
# 9| params:
# 9| initializations:
# 9| body: [Block] { ... }
# 9| body: [BlockStmt] { ... }
# 10| 0: [ReturnStmt] return ...
# 13| [CopyAssignmentOperator] E& E::operator=(E const&)
# 13| params:
@@ -172,7 +172,7 @@ ConstructorCall.cpp:
# 17| Type = [PointerType] D *
# 17| 2: [Parameter] e
# 17| Type = [PointerType] E *
# 17| body: [Block] { ... }
# 17| body: [BlockStmt] { ... }
# 18| 0: [ExprStmt] ExprStmt
# 18| 0: [AssignExpr] ... = ...
# 18| Type = [PointerType] C *
@@ -221,7 +221,7 @@ ConstructorCall.cpp:
Conversion1.c:
# 1| [TopLevelFunction] void Conversion1()
# 1| params:
# 1| body: [Block] { ... }
# 1| body: [BlockStmt] { ... }
# 2| 0: [DeclStmt] declaration
# 2| 0: [VariableDeclarationEntry] definition of i
# 2| Type = [IntType] int
@@ -241,7 +241,7 @@ Conversion2.c:
# 1| params:
# 1| 0: [Parameter] x
# 1| Type = [IntType] int
# 1| body: [Block] { ... }
# 1| body: [BlockStmt] { ... }
# 2| 0: [ExprStmt] ExprStmt
# 2| 0: [AssignExpr] ... = ...
# 2| Type = [IntType] int
@@ -277,7 +277,7 @@ Conversion3.cpp:
# 1| params:
# 1| 0: [Parameter] x
# 1| Type = [IntType] int
# 1| body: [Block] { ... }
# 1| body: [BlockStmt] { ... }
# 2| 0: [ExprStmt] ExprStmt
# 2| 0: [AssignExpr] ... = ...
# 2| Type = [IntType] int
@@ -327,7 +327,7 @@ Conversion4.c:
# 1| params:
# 1| 0: [Parameter] x
# 1| Type = [IntType] int
# 1| body: [Block] { ... }
# 1| body: [BlockStmt] { ... }
# 2| 0: [ExprStmt] ExprStmt
# 2| 0: [AssignExpr] ... = ...
# 2| Type = [IntType] int
@@ -352,7 +352,7 @@ Conversion4.c:
DestructorCall.cpp:
# 3| [Destructor] void C::~C()
# 3| params:
# 3| body: [Block] { ... }
# 3| body: [BlockStmt] { ... }
# 4| 0: [ReturnStmt] return ...
# 3| destructions:
# 11| [TopLevelFunction] void DestructorCall(C*, D*)
@@ -361,7 +361,7 @@ DestructorCall.cpp:
# 11| Type = [PointerType] C *
# 11| 1: [Parameter] d
# 11| Type = [PointerType] D *
# 11| body: [Block] { ... }
# 11| body: [BlockStmt] { ... }
# 12| 0: [ExprStmt] ExprStmt
# 12| 0: [DeleteExpr] delete
# 12| Type = [VoidType] void
@@ -385,7 +385,7 @@ DynamicCast.cpp:
# 1| params:
#-----| 0: [Parameter] p#0
#-----| Type = [LValueReferenceType] const Base &
#-----| body: [Block] { ... }
#-----| body: [BlockStmt] { ... }
#-----| 0: [ReturnStmt] return ...
#-----| 0: [ReferenceToExpr] (reference to)
#-----| Type = [LValueReferenceType] Base &
@@ -412,13 +412,13 @@ DynamicCast.cpp:
#-----| Type = [RValueReferenceType] Base &&
# 2| [VirtualFunction] void Base::f()
# 2| params:
# 2| body: [Block] { ... }
# 2| body: [BlockStmt] { ... }
# 2| 0: [ReturnStmt] return ...
# 4| [CopyAssignmentOperator] Derived& Derived::operator=(Derived const&)
# 4| params:
#-----| 0: [Parameter] p#0
#-----| Type = [LValueReferenceType] const Derived &
#-----| body: [Block] { ... }
#-----| body: [BlockStmt] { ... }
#-----| 0: [ExprStmt] ExprStmt
#-----| 0: [ReferenceDereferenceExpr] (reference dereference)
#-----| Type = [Class] Base
@@ -478,7 +478,7 @@ DynamicCast.cpp:
#-----| Type = [RValueReferenceType] Derived &&
# 5| [VirtualFunction] void Derived::f()
# 5| params:
# 5| body: [Block] { ... }
# 5| body: [BlockStmt] { ... }
# 5| 0: [ReturnStmt] return ...
# 8| [TopLevelFunction] void DynamicCast(Base*, Derived*)
# 8| params:
@@ -486,7 +486,7 @@ DynamicCast.cpp:
# 8| Type = [PointerType] Base *
# 8| 1: [Parameter] d
# 8| Type = [PointerType] Derived *
# 8| body: [Block] { ... }
# 8| body: [BlockStmt] { ... }
# 9| 0: [ExprStmt] ExprStmt
# 9| 0: [AssignExpr] ... = ...
# 9| Type = [PointerType] Derived *
@@ -508,7 +508,7 @@ DynamicCast.cpp:
# 12| Type = [LValueReferenceType] Base &
# 12| 1: [Parameter] d
# 12| Type = [LValueReferenceType] Derived &
# 12| body: [Block] { ... }
# 12| body: [BlockStmt] { ... }
# 13| 0: [ExprStmt] ExprStmt
# 13| 0: [ReferenceDereferenceExpr] (reference dereference)
# 13| Type = [Class] Derived
@@ -545,7 +545,7 @@ Parenthesis.c:
# 1| params:
# 1| 0: [Parameter] i
# 1| Type = [IntType] int
# 1| body: [Block] { ... }
# 1| body: [BlockStmt] { ... }
# 2| 0: [ExprStmt] ExprStmt
# 2| 0: [AssignExpr] ... = ...
# 2| Type = [IntType] int
@@ -581,7 +581,7 @@ PointerDereference.c:
# 1| Type = [IntPointerType] int *
# 1| 1: [Parameter] j
# 1| Type = [IntType] int
# 1| body: [Block] { ... }
# 1| body: [BlockStmt] { ... }
# 2| 0: [ExprStmt] ExprStmt
# 2| 0: [AssignExpr] ... = ...
# 2| Type = [IntType] int
@@ -603,7 +603,7 @@ ReferenceDereference.cpp:
# 4| Type = [LValueReferenceType] int &
# 4| 1: [Parameter] j
# 4| Type = [IntType] int
# 4| body: [Block] { ... }
# 4| body: [BlockStmt] { ... }
# 5| 0: [ExprStmt] ExprStmt
# 5| 0: [AssignExpr] ... = ...
# 5| Type = [IntType] int
@@ -623,7 +623,7 @@ ReferenceTo.cpp:
# 1| params:
# 1| 0: [Parameter] i
# 1| Type = [IntPointerType] int *
# 1| body: [Block] { ... }
# 1| body: [BlockStmt] { ... }
# 2| 0: [ReturnStmt] return ...
# 2| 0: [ReferenceToExpr] (reference to)
# 2| Type = [LValueReferenceType] int &
@@ -639,7 +639,7 @@ Sizeof.c:
# 1| params:
# 1| 0: [Parameter] array
# 1| Type = [ArrayType] int[]
# 1| body: [Block] { ... }
# 1| body: [BlockStmt] { ... }
# 2| 0: [DeclStmt] declaration
# 2| 0: [VariableDeclarationEntry] definition of i
# 2| Type = [IntType] int
@@ -676,7 +676,7 @@ Sizeof.c:
StatementExpr.c:
# 1| [TopLevelFunction] void StatementExpr()
# 1| params:
# 1| body: [Block] { ... }
# 1| body: [BlockStmt] { ... }
# 2| 0: [DeclStmt] declaration
# 2| 0: [VariableDeclarationEntry] definition of j
# 2| Type = [IntType] int
@@ -700,7 +700,7 @@ StaticMemberAccess.cpp:
# 5| Type = [IntType] int
# 5| 1: [Parameter] xref
# 5| Type = [LValueReferenceType] X &
# 5| body: [Block] { ... }
# 5| body: [BlockStmt] { ... }
# 7| 0: [ExprStmt] ExprStmt
# 7| 0: [AssignExpr] ... = ...
# 7| Type = [IntType] int
@@ -725,7 +725,7 @@ Subscript.c:
# 1| Type = [ArrayType] int[]
# 1| 1: [Parameter] j
# 1| Type = [IntType] int
# 1| body: [Block] { ... }
# 1| body: [BlockStmt] { ... }
# 2| 0: [ExprStmt] ExprStmt
# 2| 0: [AssignExpr] ... = ...
# 2| Type = [IntType] int
@@ -762,20 +762,20 @@ Throw.cpp:
#-----| 0: [Parameter] p#0
#-----| Type = [RValueReferenceType] F &&
# 2| initializations:
# 2| body: [Block] { ... }
# 2| body: [BlockStmt] { ... }
# 2| 0: [ReturnStmt] return ...
# 4| [Constructor] void F::F()
# 4| params:
# 4| initializations:
# 4| body: [Block] { ... }
# 4| body: [BlockStmt] { ... }
# 4| 0: [ReturnStmt] return ...
# 6| [TopLevelFunction] void Throw(int)
# 6| params:
# 6| 0: [Parameter] i
# 6| Type = [IntType] int
# 6| body: [Block] { ... }
# 6| body: [BlockStmt] { ... }
# 7| 0: [TryStmt] try { ... }
# 7| 0: [Block] { ... }
# 7| 0: [BlockStmt] { ... }
# 8| 0: [IfStmt] if (...) ...
# 8| 0: [CStyleCast] (bool)...
# 8| Conversion = [BoolConversion] conversion to bool
@@ -818,13 +818,13 @@ Typeid.cpp:
# 7| params:
# 13| [VirtualFunction] void Base::v()
# 13| params:
# 13| body: [Block] { ... }
# 13| body: [BlockStmt] { ... }
# 13| 0: [ReturnStmt] return ...
# 18| [TopLevelFunction] void TypeId(Base*)
# 18| params:
# 18| 0: [Parameter] bp
# 18| Type = [PointerType] Base *
# 18| body: [Block] { ... }
# 18| body: [BlockStmt] { ... }
# 19| 0: [DeclStmt] declaration
# 19| 0: [VariableDeclarationEntry] definition of name
# 19| Type = [PointerType] const char *
@@ -846,7 +846,7 @@ VacuousDestructorCall.cpp:
# 2| Type = [TemplateParameter] T
# 2| 1: [Parameter] y
# 2| Type = [PointerType] T *
# 2| body: [Block] { ... }
# 2| body: [BlockStmt] { ... }
# 3| 0: [ExprStmt] ExprStmt
# 3| 0: [ExprCall] call to expression
# 3| Type = [UnknownType] unknown
@@ -874,7 +874,7 @@ VacuousDestructorCall.cpp:
# 2| Type = [IntType] int
# 2| 1: [Parameter] y
# 2| Type = [IntPointerType] int *
# 2| body: [Block] { ... }
# 2| body: [BlockStmt] { ... }
# 3| 0: [ExprStmt] ExprStmt
# 3| 0: [VacuousDestructorCall] (vacuous destructor call)
# 3| Type = [VoidType] void
@@ -894,7 +894,7 @@ VacuousDestructorCall.cpp:
# 7| params:
# 7| 0: [Parameter] i
# 7| Type = [IntType] int
# 7| body: [Block] { ... }
# 7| body: [BlockStmt] { ... }
# 10| 0: [ExprStmt] ExprStmt
# 10| 0: [FunctionCall] call to CallDestructor
# 10| Type = [VoidType] void
@@ -914,7 +914,7 @@ Varargs.c:
# 8| params:
# 8| 0: [Parameter] text
# 8| Type = [PointerType] const char *
# 8| body: [Block] { ... }
# 8| body: [BlockStmt] { ... }
# 9| 0: [DeclStmt] declaration
# 9| 0: [VariableDeclarationEntry] definition of args
# 9| Type = [CTypedefType] va_list
@@ -947,7 +947,7 @@ macro_etc.c:
# 3| params:
# 3| 0: [Parameter] i
# 3| Type = [IntType] int
# 3| body: [Block] { ... }
# 3| body: [BlockStmt] { ... }
# 4| 0: [DeclStmt] declaration
# 4| 0: [TypeDeclarationEntry] definition of u
# 4| Type = [LocalUnion] u
@@ -997,7 +997,7 @@ macro_etc.c:
# 10| ValueCategory = prvalue
# 22| [TopLevelFunction] int foo()
# 22| params:
# 22| body: [Block] { ... }
# 22| body: [BlockStmt] { ... }
# 23| 0: [DeclStmt] declaration
# 23| 0: [VariableDeclarationEntry] definition of t
# 23| Type = [IntType] int
@@ -1059,7 +1059,7 @@ macro_etc.c:
# 27| 0: [VariableAccess] i
# 27| Type = [PlainCharType] char
# 27| ValueCategory = lvalue
# 27| 3: [Block] { ... }
# 27| 3: [BlockStmt] { ... }
# 27| 0: [ExprStmt] ExprStmt
# 27| 0: [AssignAddExpr] ... += ...
# 27| Type = [IntType] int
@@ -1111,7 +1111,7 @@ macro_etc.c:
# 28| 0: [VariableAccess] i
# 28| Type = [PlainCharType] char
# 28| ValueCategory = lvalue
# 28| 3: [Block] { ... }
# 28| 3: [BlockStmt] { ... }
# 28| 0: [ExprStmt] ExprStmt
# 28| 0: [AssignAddExpr] ... += ...
# 28| Type = [IntType] int
@@ -1210,7 +1210,7 @@ union_etc.cpp:
# 2| [Constructor] void S::S()
# 2| params:
# 2| initializations:
# 2| body: [Block] { ... }
# 2| body: [BlockStmt] { ... }
# 2| 0: [ReturnStmt] return ...
# 2| [CopyConstructor] void S::S(S const&)
# 2| params:
@@ -1240,7 +1240,7 @@ union_etc.cpp:
# 6| params:
# 6| 0: [Parameter] val
# 6| Type = [IntType] int
# 6| body: [Block] { ... }
# 6| body: [BlockStmt] { ... }
# 6| 0: [ExprStmt] ExprStmt
# 6| 0: [AssignExpr] ... = ...
# 6| Type = [IntType] int
@@ -1305,7 +1305,7 @@ union_etc.cpp:
#-----| Type = [RValueReferenceType] C &&
# 22| [TopLevelFunction] int foo()
# 22| params:
# 22| body: [Block] { ... }
# 22| body: [BlockStmt] { ... }
# 23| 0: [DeclStmt] declaration
# 23| 0: [VariableDeclarationEntry] definition of s
# 23| Type = [Struct] S
@@ -1423,7 +1423,7 @@ union_etc.cpp:
# 33| params:
# 33| 0: [Parameter] val
# 33| Type = [IntType] int
# 33| body: [Block] { ... }
# 33| body: [BlockStmt] { ... }
# 33| 0: [ExprStmt] ExprStmt
# 33| 0: [AssignExpr] ... = ...
# 33| Type = [IntType] int
@@ -1440,7 +1440,7 @@ union_etc.cpp:
# 33| 1: [ReturnStmt] return ...
# 36| [TopLevelFunction] int bar()
# 36| params:
# 36| body: [Block] { ... }
# 36| body: [BlockStmt] { ... }
# 37| 0: [DeclStmt] declaration
# 37| 0: [VariableDeclarationEntry] definition of s
# 37| Type = [PointerType] const T *

View File

@@ -149,3 +149,63 @@ void test_conflated_fields2() {
taint_x(&p);
y_to_sink(&p);
}
void sink(Point*);
void sink(Point);
void test_field_to_obj_taint_object(Point p) {
p.x = getenv("VAR")[0];
sink(p); // not tainted
sink(p.x); // tainted
}
void test_field_to_obj_taint_object_addrof(Point p) {
taint_x(&p);
sink(p); // tainted [field -> object]
sink(&p); // tainted [field -> object]
sink(p.x); // tainted
}
void test_field_to_obj_taint_pointer(Point* pp) {
pp->x = getenv("VAR")[0];
sink(pp); // tainted [field -> object]
sink(*pp); // not tainted
}
void call_sink_on_object(Point* pp) {
sink(pp); // tainted [field -> object]
sink(*pp); // tainted [field -> object]
}
void test_field_to_obj_taint_call_sink(Point* pp) {
pp->x = getenv("VAR")[0];
call_sink_on_object(pp);
}
void test_field_to_obj_taint_through_setter(Point* pp) {
taint_x(pp);
sink(pp); // tainted [field -> object]
sink(*pp); // not tainted
}
Point* getPoint();
void test_field_to_obj_local_variable() {
Point* pp = getPoint();
pp->x = getenv("VAR")[0];
sink(pp); // not tainted
sink(*pp); // not tainted
}
void test_field_to_obj_taint_array(Point* pp, int i) {
pp[0].x = getenv("VAR")[0];
sink(pp[i]); // not tainted
sink(pp); // tainted [field -> object]
sink(*pp); // not tainted
}
void test_field_to_obj_test_pointer_arith(Point* pp) {
(pp + sizeof(*pp))->x = getenv("VAR")[0];
sink(pp); // tainted [field -> object]
sink(pp + sizeof(*pp)); // tainted [field -> object]
}

View File

@@ -88,14 +88,14 @@ void test_stringstream()
ss5 << t;
sink(ss1);
sink(ss2); // tainted [NOT DETECTED]
sink(ss2); // tainted
sink(ss3); // tainted [NOT DETECTED]
sink(ss4); // tainted [NOT DETECTED]
sink(ss4); // tainted
sink(ss5); // tainted [NOT DETECTED]
sink(ss1.str());
sink(ss2.str()); // tainted [NOT DETECTED]
sink(ss2.str()); // tainted
sink(ss3.str()); // tainted [NOT DETECTED]
sink(ss4.str()); // tainted [NOT DETECTED]
sink(ss4.str()); // tainted
sink(ss5.str()); // tainted [NOT DETECTED]
}

View File

@@ -115,6 +115,48 @@
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:143:23:143:24 | pp |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:144:8:144:9 | pp |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:150:13:150:14 | & ... |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:153:11:153:15 | p#0 |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:154:11:154:15 | p#0 |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:162:50:162:50 | p |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:164:8:164:8 | p |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:165:8:165:9 | & ... |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:166:10:166:10 | x |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:187:8:187:9 | pp |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | shared.h:6:15:6:23 | sinkparam |
| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:157:9:157:14 | call to getenv |
| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:157:9:157:24 | (int)... |
| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:157:9:157:24 | access to array |
| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:159:10:159:10 | x |
| defaulttainttracking.cpp:157:9:157:14 | call to getenv | shared.h:6:15:6:23 | sinkparam |
| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:153:11:153:15 | p#0 |
| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:170:11:170:16 | call to getenv |
| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:170:11:170:26 | (int)... |
| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:170:11:170:26 | access to array |
| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:171:8:171:9 | pp |
| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:153:11:153:15 | p#0 |
| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:154:11:154:15 | p#0 |
| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:175:33:175:34 | pp |
| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:176:8:176:9 | pp |
| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:177:8:177:10 | * ... |
| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:177:9:177:10 | pp |
| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:181:11:181:16 | call to getenv |
| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:181:11:181:26 | (int)... |
| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:181:11:181:26 | access to array |
| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:182:23:182:24 | pp |
| defaulttainttracking.cpp:195:11:195:16 | call to getenv | defaulttainttracking.cpp:195:11:195:16 | call to getenv |
| defaulttainttracking.cpp:195:11:195:16 | call to getenv | defaulttainttracking.cpp:195:11:195:26 | (int)... |
| defaulttainttracking.cpp:195:11:195:16 | call to getenv | defaulttainttracking.cpp:195:11:195:26 | access to array |
| defaulttainttracking.cpp:201:13:201:18 | call to getenv | defaulttainttracking.cpp:153:11:153:15 | p#0 |
| defaulttainttracking.cpp:201:13:201:18 | call to getenv | defaulttainttracking.cpp:201:13:201:18 | call to getenv |
| defaulttainttracking.cpp:201:13:201:18 | call to getenv | defaulttainttracking.cpp:201:13:201:28 | (int)... |
| defaulttainttracking.cpp:201:13:201:18 | call to getenv | defaulttainttracking.cpp:201:13:201:28 | access to array |
| defaulttainttracking.cpp:201:13:201:18 | call to getenv | defaulttainttracking.cpp:203:8:203:9 | pp |
| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:153:11:153:15 | p#0 |
| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:208:27:208:32 | call to getenv |
| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:208:27:208:42 | (int)... |
| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:208:27:208:42 | access to array |
| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:209:8:209:9 | pp |
| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:210:8:210:23 | ... + ... |
| dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:28:24:28:27 | call to atoi |
| dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:28:29:28:34 | call to getenv |
| dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:28:29:28:45 | (const char *)... |
@@ -155,8 +197,10 @@
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:23:15:23:20 | call to getenv |
| stl.cpp:62:25:62:30 | call to getenv | shared.h:5:23:5:31 | sinkparam |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:21:29:21:29 | s |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:43:78:43:104 | p#0 |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:43:114:43:118 | p#1 |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:62:25:62:30 | call to getenv |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:64:36:64:36 | s |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:68:8:68:8 | a |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:68:12:68:17 | call to source |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:21 | call to source |
@@ -167,12 +211,31 @@
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:21 | call to source |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:23 | (const char *)... |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:24 | call to basic_string |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:6:85:6 | call to operator<< |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:6:85:17 | (reference dereference) |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:9:85:14 | call to source |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:9:85:16 | (const char *)... |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:15:86:15 | call to operator<< |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:15:86:26 | (reference dereference) |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:18:86:23 | call to source |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:18:86:25 | (const char *)... |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:6 | call to operator<< |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:19 | (reference dereference) |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:19 | (reference to) |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:9:87:14 | call to source |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:9:87:16 | (const char *)... |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:18:87:18 | call to operator<< |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:18:87:26 | (reference dereference) |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | (const stringstream)... |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | (reference to) |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | ss2 |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | (const stringstream)... |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | (reference to) |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | ss4 |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:9 | (const basic_stringstream<char, char_traits<char>, allocator<char>>)... |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:9 | ss2 |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:9 | (const basic_stringstream<char, char_traits<char>, allocator<char>>)... |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:9 | ss4 |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:118:10:118:15 | call to source |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:16:125:28 | call to basic_string |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:17:125:26 | call to user_input |

View File

@@ -29,12 +29,63 @@
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:143:23:143:24 | pp | IR only |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:144:8:144:9 | pp | IR only |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:150:13:150:14 | & ... | IR only |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:153:11:153:15 | p#0 | IR only |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:154:11:154:15 | p#0 | IR only |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:162:50:162:50 | p | IR only |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:164:8:164:8 | p | IR only |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:165:8:165:9 | & ... | IR only |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:166:10:166:10 | x | IR only |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:187:8:187:9 | pp | IR only |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | shared.h:6:15:6:23 | sinkparam | IR only |
| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:157:5:157:5 | x | AST only |
| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:159:10:159:10 | x | IR only |
| defaulttainttracking.cpp:157:9:157:14 | call to getenv | shared.h:6:15:6:23 | sinkparam | IR only |
| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:153:11:153:15 | p#0 | IR only |
| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:170:7:170:7 | x | AST only |
| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:171:8:171:9 | pp | IR only |
| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:153:11:153:15 | p#0 | IR only |
| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:154:11:154:15 | p#0 | IR only |
| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:175:33:175:34 | pp | IR only |
| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:176:8:176:9 | pp | IR only |
| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:177:8:177:10 | * ... | IR only |
| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:177:9:177:10 | pp | IR only |
| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:181:7:181:7 | x | AST only |
| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:182:23:182:24 | pp | IR only |
| defaulttainttracking.cpp:195:11:195:16 | call to getenv | defaulttainttracking.cpp:195:7:195:7 | x | AST only |
| defaulttainttracking.cpp:201:13:201:18 | call to getenv | defaulttainttracking.cpp:153:11:153:15 | p#0 | IR only |
| defaulttainttracking.cpp:201:13:201:18 | call to getenv | defaulttainttracking.cpp:201:9:201:9 | x | AST only |
| defaulttainttracking.cpp:201:13:201:18 | call to getenv | defaulttainttracking.cpp:203:8:203:9 | pp | IR only |
| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:153:11:153:15 | p#0 | IR only |
| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:208:23:208:23 | x | AST only |
| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:209:8:209:9 | pp | IR only |
| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:210:8:210:23 | ... + ... | IR only |
| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:13:5:13:11 | global1 | AST only |
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:23:5:23:11 | global2 | AST only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:43:78:43:104 | p#0 | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:62:7:62:12 | source | AST only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:64:36:64:36 | s | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:24 | call to basic_string | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:24 | call to basic_string | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:6:85:6 | call to operator<< | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:6:85:17 | (reference dereference) | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:15:86:15 | call to operator<< | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:15:86:26 | (reference dereference) | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:6 | call to operator<< | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:19 | (reference dereference) | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:19 | (reference to) | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:9:87:16 | (const char *)... | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:18:87:18 | call to operator<< | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:18:87:26 | (reference dereference) | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | (const stringstream)... | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | (reference to) | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | ss2 | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | (const stringstream)... | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | (reference to) | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | ss4 | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:9 | (const basic_stringstream<char, char_traits<char>, allocator<char>>)... | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:9 | ss2 | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:9 | (const basic_stringstream<char, char_traits<char>, allocator<char>>)... | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:9 | ss4 | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:117:7:117:16 | user_input | AST only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:16:125:28 | call to basic_string | IR only |
| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:128:9:128:13 | path2 | IR only |

View File

@@ -14,7 +14,6 @@ postHasUniquePre
uniquePostUpdate
postIsInSameCallable
reverseRead
storeIsPostUpdate
argHasPostUpdate
| lambdas.cpp:18:7:18:7 | a | ArgumentNode is missing PostUpdateNode. |
| lambdas.cpp:25:2:25:2 | b | ArgumentNode is missing PostUpdateNode. |

Some files were not shown because too many files have changed in this diff Show More