mirror of
https://github.com/github/codeql.git
synced 2026-05-02 04:05:14 +02:00
Merge branch 'main' of github.com:github/codeql into SharedDataflow_NestedComprehensions
This commit is contained in:
@@ -4,6 +4,6 @@
|
||||
"slevesque.vscode-zipexplorer"
|
||||
],
|
||||
"settings": {
|
||||
"codeQL.experimentalBqrsParsing": true
|
||||
"codeQL.runningQueries.memory": 2048
|
||||
}
|
||||
}
|
||||
|
||||
11
.github/workflows/labeler.yml
vendored
Normal file
11
.github/workflows/labeler.yml
vendored
Normal 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 }}"
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
21
change-notes/1.26/analysis-java.md
Normal file
21
change-notes/1.26/analysis-java.md
Normal 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`.
|
||||
@@ -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.
|
||||
|
||||
22
change-notes/1.26/analysis-python.md
Normal file
22
change-notes/1.26/analysis-python.md
Normal 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.
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
|
||||
import cpp
|
||||
|
||||
from Block blk
|
||||
from BlockStmt blk
|
||||
where blk.getNumStmt() = 0
|
||||
select blk
|
||||
|
||||
@@ -13,5 +13,5 @@
|
||||
import cpp
|
||||
|
||||
from IfStmt i
|
||||
where i.getThen().(Block).getNumStmt() = 0
|
||||
where i.getThen().(BlockStmt).getNumStmt() = 0
|
||||
select i
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
|
||||
import cpp
|
||||
|
||||
from Block b
|
||||
from BlockStmt b
|
||||
where b.getNumStmt() = 1
|
||||
select b
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
11
cpp/ql/src/Critical/aliasAnalysisWarning.qhelp
Normal file
11
cpp/ql/src/Critical/aliasAnalysisWarning.qhelp
Normal 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>
|
||||
12
cpp/ql/src/Critical/callGraphWarning.qhelp
Normal file
12
cpp/ql/src/Critical/callGraphWarning.qhelp
Normal 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>
|
||||
13
cpp/ql/src/Critical/dataFlowWarning.qhelp
Normal file
13
cpp/ql/src/Critical/dataFlowWarning.qhelp
Normal 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>
|
||||
11
cpp/ql/src/Critical/pointsToWarning.qhelp
Normal file
11
cpp/ql/src/Critical/pointsToWarning.qhelp
Normal 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>
|
||||
@@ -3,5 +3,5 @@
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<include src="CommentedOutCodeQuery.qhelp" />
|
||||
<include src="CommentedOutCodeReferences.qhelp" />
|
||||
<include src="../Metrics/Files/CommentedOutCodeReferences.qhelp" />
|
||||
</qhelp>
|
||||
|
||||
25
cpp/ql/src/Documentation/CommentedOutCodeQuery.qhelp
Normal file
25
cpp/ql/src/Documentation/CommentedOutCodeQuery.qhelp
Normal 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>
|
||||
@@ -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()]
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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>
|
||||
12
cpp/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp
Normal file
12
cpp/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp
Normal 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>
|
||||
16
cpp/ql/src/Metrics/Files/DuplicationProblems.qhelp
Normal file
16
cpp/ql/src/Metrics/Files/DuplicationProblems.qhelp
Normal 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>
|
||||
35
cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.qhelp
Normal file
35
cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.qhelp
Normal 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>
|
||||
@@ -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()
|
||||
|
||||
@@ -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...
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
2
cpp/ql/src/external/CodeDuplication.qll
vendored
2
cpp/ql/src/external/CodeDuplication.qll
vendored
@@ -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(
|
||||
|
||||
@@ -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>.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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*/-->
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
18
cpp/ql/src/jsf/jsfNote.qhelp
Normal file
18
cpp/ql/src/jsf/jsfNote.qhelp
Normal 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>
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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, _)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) }
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
273
cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll
Normal file
273
cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll
Normal 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()
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
|
||||
@@ -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
@@ -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() }
|
||||
|
||||
|
||||
23
cpp/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll
Normal file
23
cpp/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll
Normal 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() }
|
||||
}
|
||||
@@ -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 *
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
@@ -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]
|
||||
}
|
||||
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user