C++: merge main and accept test changes

This commit is contained in:
Robert Marsh
2022-05-20 14:37:09 -04:00
1952 changed files with 131613 additions and 22440 deletions

View File

@@ -3,6 +3,8 @@
"rust-lang.rust", "rust-lang.rust",
"bungcip.better-toml", "bungcip.better-toml",
"github.vscode-codeql", "github.vscode-codeql",
"hbenl.vscode-test-explorer",
"ms-vscode.test-adapter-converter",
"slevesque.vscode-zipexplorer" "slevesque.vscode-zipexplorer"
], ],
"settings": { "settings": {

2
.github/labeler.yml vendored
View File

@@ -11,7 +11,7 @@ Java:
- change-notes/**/*java.* - change-notes/**/*java.*
JS: JS:
- javascript/**/* - any: [ 'javascript/**/*', '!javascript/ql/experimental/adaptivethreatmodeling/**/*' ]
- change-notes/**/*javascript* - change-notes/**/*javascript*
Python: Python:

View File

@@ -30,7 +30,7 @@ jobs:
- name: Setup dotnet - name: Setup dotnet
uses: actions/setup-dotnet@v2 uses: actions/setup-dotnet@v2
with: with:
dotnet-version: 6.0.101 dotnet-version: 6.0.202
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v3

View File

@@ -38,7 +38,7 @@ jobs:
path: metrics-java.sarif path: metrics-java.sarif
retention-days: 20 retention-days: 20
- name: Upload SARIF file - name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v1 uses: github/codeql-action/upload-sarif@main
with: with:
sarif_file: metrics-java.sarif sarif_file: metrics-java.sarif
@@ -65,6 +65,6 @@ jobs:
path: metrics-csharp.sarif path: metrics-csharp.sarif
retention-days: 20 retention-days: 20
- name: Upload SARIF file - name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v1 uses: github/codeql-action/upload-sarif@main
with: with:
sarif_file: metrics-csharp.sarif sarif_file: metrics-csharp.sarif

View File

@@ -30,20 +30,15 @@ jobs:
with: with:
python-version: 3.8 python-version: 3.8
- name: Download CodeQL CLI - name: Download CodeQL CLI
uses: dsaltares/fetch-gh-release-asset@aa37ae5c44d3c9820bc12fe675e8670ecd93bd1c # Look under the `codeql` directory, as this is where we checked out the `github/codeql` repo
with: uses: ./codeql/.github/actions/fetch-codeql
repo: "github/codeql-cli-binaries"
version: "latest"
file: "codeql-linux64.zip"
token: ${{ secrets.GITHUB_TOKEN }}
- name: Unzip CodeQL CLI - name: Unzip CodeQL CLI
run: unzip -d codeql-cli codeql-linux64.zip run: unzip -d codeql-cli codeql-linux64.zip
- name: Build code scanning query list - name: Build code scanning query list
run: | run: |
PATH="$PATH:codeql-cli/codeql" python codeql/misc/scripts/generate-code-scanning-query-list.py > code-scanning-query-list.csv python codeql/misc/scripts/generate-code-scanning-query-list.py > code-scanning-query-list.csv
- name: Upload code scanning query list - name: Upload code scanning query list
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: code-scanning-query-list name: code-scanning-query-list
path: code-scanning-query-list.csv path: code-scanning-query-list.csv

View File

@@ -18,8 +18,16 @@ jobs:
- name: Run unit tests - name: Run unit tests
run: | run: |
bazel test //swift/codegen/test --test_output=errors bazel test //swift/codegen/test --test_output=errors
- name: Check that code was generated - name: Check that QL generated code was checked in
run: | run: |
bazel run //swift/codegen bazel run //swift/codegen
git add swift git add swift
git diff --exit-code --stat HEAD git diff --exit-code --stat HEAD
- name: Generate C++ files
run: |
bazel run //swift/codegen:trapgen -- --cpp-output=$PWD/swift-generated-headers
bazel run //swift/codegen:cppgen -- --cpp-output=$PWD/swift-generated-headers
- uses: actions/upload-artifact@v3
with:
name: swift-generated-headers
path: swift-generated-headers/*.h

3
.gitignore vendored
View File

@@ -37,5 +37,8 @@ csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json
# links created by bazel # links created by bazel
/bazel-* /bazel-*
# local bazel options
/local.bazelrc
# CLion project files # CLion project files
/.clwb /.clwb

View File

@@ -4,6 +4,9 @@
/javascript/ @github/codeql-javascript /javascript/ @github/codeql-javascript
/python/ @github/codeql-python /python/ @github/codeql-python
/ruby/ @github/codeql-ruby /ruby/ @github/codeql-ruby
/swift/ @github/codeql-c
/java/kotlin-extractor/ @github/codeql-kotlin
/java/kotlin-explorer/ @github/codeql-kotlin
# ML-powered queries # ML-powered queries
/javascript/ql/experimental/adaptivethreatmodeling/ @github/codeql-ml-powered-queries-reviewers /javascript/ql/experimental/adaptivethreatmodeling/ @github/codeql-ml-powered-queries-reviewers

View File

@@ -475,20 +475,23 @@
"python/ql/lib/semmle/python/security/internal/SensitiveDataHeuristics.qll", "python/ql/lib/semmle/python/security/internal/SensitiveDataHeuristics.qll",
"ruby/ql/lib/codeql/ruby/security/internal/SensitiveDataHeuristics.qll" "ruby/ql/lib/codeql/ruby/security/internal/SensitiveDataHeuristics.qll"
], ],
"ReDoS Util Python/JS/Ruby": [ "ReDoS Util Python/JS/Ruby/Java": [
"javascript/ql/lib/semmle/javascript/security/performance/ReDoSUtil.qll", "javascript/ql/lib/semmle/javascript/security/performance/ReDoSUtil.qll",
"python/ql/lib/semmle/python/security/performance/ReDoSUtil.qll", "python/ql/lib/semmle/python/security/performance/ReDoSUtil.qll",
"ruby/ql/lib/codeql/ruby/security/performance/ReDoSUtil.qll" "ruby/ql/lib/codeql/ruby/security/performance/ReDoSUtil.qll",
"java/ql/lib/semmle/code/java/security/performance/ReDoSUtil.qll"
], ],
"ReDoS Exponential Python/JS/Ruby": [ "ReDoS Exponential Python/JS/Ruby/Java": [
"javascript/ql/lib/semmle/javascript/security/performance/ExponentialBackTracking.qll", "javascript/ql/lib/semmle/javascript/security/performance/ExponentialBackTracking.qll",
"python/ql/lib/semmle/python/security/performance/ExponentialBackTracking.qll", "python/ql/lib/semmle/python/security/performance/ExponentialBackTracking.qll",
"ruby/ql/lib/codeql/ruby/security/performance/ExponentialBackTracking.qll" "ruby/ql/lib/codeql/ruby/security/performance/ExponentialBackTracking.qll",
"java/ql/lib/semmle/code/java/security/performance/ExponentialBackTracking.qll"
], ],
"ReDoS Polynomial Python/JS/Ruby": [ "ReDoS Polynomial Python/JS/Ruby/Java": [
"javascript/ql/lib/semmle/javascript/security/performance/SuperlinearBackTracking.qll", "javascript/ql/lib/semmle/javascript/security/performance/SuperlinearBackTracking.qll",
"python/ql/lib/semmle/python/security/performance/SuperlinearBackTracking.qll", "python/ql/lib/semmle/python/security/performance/SuperlinearBackTracking.qll",
"ruby/ql/lib/codeql/ruby/security/performance/SuperlinearBackTracking.qll" "ruby/ql/lib/codeql/ruby/security/performance/SuperlinearBackTracking.qll",
"java/ql/lib/semmle/code/java/security/performance/SuperlinearBackTracking.qll"
], ],
"BadTagFilterQuery Python/JS/Ruby": [ "BadTagFilterQuery Python/JS/Ruby": [
"javascript/ql/lib/semmle/javascript/security/BadTagFilterQuery.qll", "javascript/ql/lib/semmle/javascript/security/BadTagFilterQuery.qll",
@@ -550,5 +553,9 @@
"HttpToFileAccessCustomizations JS/Ruby": [ "HttpToFileAccessCustomizations JS/Ruby": [
"javascript/ql/lib/semmle/javascript/security/dataflow/HttpToFileAccessCustomizations.qll", "javascript/ql/lib/semmle/javascript/security/dataflow/HttpToFileAccessCustomizations.qll",
"ruby/ql/lib/codeql/ruby/security/HttpToFileAccessCustomizations.qll" "ruby/ql/lib/codeql/ruby/security/HttpToFileAccessCustomizations.qll"
],
"Typo database": [
"javascript/ql/src/Expressions/TypoDatabase.qll",
"ql/ql/src/codeql_ql/style/TypoDatabase.qll"
] ]
} }

View File

@@ -0,0 +1,21 @@
class Element extends @element {
string toString() { none() }
}
class Expr extends @expr {
string toString() { none() }
}
class Stmt extends @stmt {
string toString() { none() }
}
predicate isStmtWithInitializer(Stmt stmt) {
exists(int kind | stmts(stmt, kind, _) | kind = 2 or kind = 11 or kind = 35)
}
from Expr child, int index, int index_new, Element parent
where
exprparents(child, index, parent) and
if isStmtWithInitializer(parent) then index_new = index - 1 else index_new = index
select child, index_new, parent

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,22 @@
class Element extends @element {
string toString() { none() }
}
class Stmt extends @stmt {
string toString() { none() }
}
predicate isStmtWithInitializer(Stmt stmt) {
exists(int kind | stmts(stmt, kind, _) | kind = 2 or kind = 11 or kind = 35)
}
from Stmt child, int index, int index_new, Element parent
where
stmtparents(child, index, parent) and
(
not isStmtWithInitializer(parent)
or
index > 0
) and
if isStmtWithInitializer(parent) then index_new = index - 1 else index_new = index
select child, index_new, parent

View File

@@ -0,0 +1,6 @@
description: Support C++17 if and switch initializers
compatibility: partial
if_initialization.rel: delete
switch_initialization.rel: delete
exprparents.rel: run exprparents.qlo
stmtparents.rel: run stmtparents.qlo

View File

@@ -1,3 +1,16 @@
## 0.2.1
## 0.2.0
### Breaking Changes
* The signature of `allowImplicitRead` on `DataFlow::Configuration` and `TaintTracking::Configuration` has changed from `allowImplicitRead(DataFlow::Node node, DataFlow::Content c)` to `allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c)`.
### Minor Analysis Improvements
* More Windows pool allocation functions are now detected as `AllocationFunction`s.
* The `semmle.code.cpp.commons.Buffer` library has been enhanced to handle array members of classes that do not specify a size.
## 0.1.0 ## 0.1.0
### Breaking Changes ### Breaking Changes

View File

@@ -0,0 +1,4 @@
---
category: feature
---
* A `getInitialization` predicate was added to the `ConstexprIfStmt`, `IfStmt`, and `SwitchStmt` classes that yields the C++17-style initializer of the `if` or `switch` statement when it exists.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* The `semmle.code.cpp.commons.Buffer` library has been enhanced to handle array members of classes that do not specify a size.

View File

@@ -1,4 +0,0 @@
---
category: breaking
---
The signature of `allowImplicitRead` on `DataFlow::Configuration` and `TaintTracking::Configuration` has changed from `allowImplicitRead(DataFlow::Node node, DataFlow::Content c)` to `allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c)`.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* More Windows pool allocation functions are now detected as `AllocationFunction`s.

View File

@@ -0,0 +1,4 @@
---
category: deprecated
---
* The `AnalysedString` class in the `StringAnalysis` module has been replaced with `AnalyzedString`, to follow our style guide. The old name still exists as a deprecated alias.

View File

@@ -0,0 +1,10 @@
## 0.2.0
### Breaking Changes
* The signature of `allowImplicitRead` on `DataFlow::Configuration` and `TaintTracking::Configuration` has changed from `allowImplicitRead(DataFlow::Node node, DataFlow::Content c)` to `allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c)`.
### Minor Analysis Improvements
* More Windows pool allocation functions are now detected as `AllocationFunction`s.
* The `semmle.code.cpp.commons.Buffer` library has been enhanced to handle array members of classes that do not specify a size.

View File

@@ -0,0 +1 @@
## 0.2.1

View File

@@ -1,2 +1,2 @@
--- ---
lastReleaseVersion: 0.1.0 lastReleaseVersion: 0.2.1

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-all name: codeql/cpp-all
version: 0.1.1-dev version: 0.2.2-dev
groups: cpp groups: cpp
dbscheme: semmlecode.cpp.dbscheme dbscheme: semmlecode.cpp.dbscheme
extractor: cpp extractor: cpp

View File

@@ -31,7 +31,7 @@ class Field extends MemberVariable {
int getByteOffset() { fieldoffsets(underlyingElement(this), result, _) } int getByteOffset() { fieldoffsets(underlyingElement(this), result, _) }
/** /**
* Gets the byte offset within `mostDerivedClass` of each occurence of this * Gets the byte offset within `mostDerivedClass` of each occurrence of this
* field within `mostDerivedClass` itself or a base class subobject of * field within `mostDerivedClass` itself or a base class subobject of
* `mostDerivedClass`. * `mostDerivedClass`.
* Note that for fields of virtual base classes, and non-virtual base classes * Note that for fields of virtual base classes, and non-virtual base classes

View File

@@ -38,8 +38,8 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
* int z = min(5, 7); * int z = min(5, 7);
* ``` * ```
* The full signature of the function called on the last line would be * The full signature of the function called on the last line would be
* "min<int>(int, int) -> int", and the full signature of the uninstantiated * `min<int>(int, int) -> int`, and the full signature of the uninstantiated
* template on the first line would be "min<T>(T, T) -> T". * template on the first line would be `min<T>(T, T) -> T`.
*/ */
string getFullSignature() { string getFullSignature() {
exists(string name, string templateArgs, string args | exists(string name, string templateArgs, string args |

View File

@@ -663,18 +663,24 @@ private predicate namedStmtChildPredicates(Locatable s, Element e, string pred)
or or
s.(ComputedGotoStmt).getExpr() = e and pred = "getExpr()" s.(ComputedGotoStmt).getExpr() = e and pred = "getExpr()"
or or
s.(ConstexprIfStmt).getInitialization() = e and pred = "getInitialization()"
or
s.(ConstexprIfStmt).getCondition() = e and pred = "getCondition()" s.(ConstexprIfStmt).getCondition() = e and pred = "getCondition()"
or or
s.(ConstexprIfStmt).getThen() = e and pred = "getThen()" s.(ConstexprIfStmt).getThen() = e and pred = "getThen()"
or or
s.(ConstexprIfStmt).getElse() = e and pred = "getElse()" s.(ConstexprIfStmt).getElse() = e and pred = "getElse()"
or or
s.(IfStmt).getInitialization() = e and pred = "getInitialization()"
or
s.(IfStmt).getCondition() = e and pred = "getCondition()" s.(IfStmt).getCondition() = e and pred = "getCondition()"
or or
s.(IfStmt).getThen() = e and pred = "getThen()" s.(IfStmt).getThen() = e and pred = "getThen()"
or or
s.(IfStmt).getElse() = e and pred = "getElse()" s.(IfStmt).getElse() = e and pred = "getElse()"
or or
s.(SwitchStmt).getInitialization() = e and pred = "getInitialization()"
or
s.(SwitchStmt).getExpr() = e and pred = "getExpr()" s.(SwitchStmt).getExpr() = e and pred = "getExpr()"
or or
s.(SwitchStmt).getStmt() = e and pred = "getStmt()" s.(SwitchStmt).getStmt() = e and pred = "getStmt()"

View File

@@ -872,7 +872,7 @@ class FormatLiteral extends Literal {
private Type getConversionType1(int n) { private Type getConversionType1(int n) {
exists(string cnv | cnv = this.getConversionChar(n) | exists(string cnv | cnv = this.getConversionChar(n) |
cnv.regexpMatch("d|i") and cnv = ["d", "i"] and
result = this.getIntegralConversion(n) and result = this.getIntegralConversion(n) and
not result.getUnderlyingType().(IntegralType).isExplicitlySigned() and not result.getUnderlyingType().(IntegralType).isExplicitlySigned() and
not result.getUnderlyingType().(IntegralType).isExplicitlyUnsigned() not result.getUnderlyingType().(IntegralType).isExplicitlyUnsigned()
@@ -912,7 +912,7 @@ class FormatLiteral extends Literal {
private Type getConversionType2(int n) { private Type getConversionType2(int n) {
exists(string cnv | cnv = this.getConversionChar(n) | exists(string cnv | cnv = this.getConversionChar(n) |
cnv.regexpMatch("o|u|x|X") and cnv = ["o", "u", "x", "X"] and
result = this.getIntegralConversion(n) and result = this.getIntegralConversion(n) and
result.getUnderlyingType().(IntegralType).isUnsigned() result.getUnderlyingType().(IntegralType).isUnsigned()
) )
@@ -920,7 +920,7 @@ class FormatLiteral extends Literal {
private Type getConversionType3(int n) { private Type getConversionType3(int n) {
exists(string cnv | cnv = this.getConversionChar(n) | exists(string cnv | cnv = this.getConversionChar(n) |
cnv.regexpMatch("a|A|e|E|f|F|g|G") and result = this.getFloatingPointConversion(n) cnv = ["a", "A", "e", "E", "f", "F", "g", "G"] and result = this.getFloatingPointConversion(n)
) )
} }
@@ -1312,7 +1312,7 @@ class FormatLiteral extends Literal {
len = len =
min(int v | min(int v |
v = this.getPrecision(n) or v = this.getPrecision(n) or
v = this.getUse().getFormatArgument(n).(AnalysedString).getMaxLength() - 1 // (don't count null terminator) v = this.getUse().getFormatArgument(n).(AnalyzedString).getMaxLength() - 1 // (don't count null terminator)
) and ) and
reason = TValueFlowAnalysis() reason = TValueFlowAnalysis()
) )

View File

@@ -27,11 +27,14 @@ predicate canValueFlow(Expr fromExpr, Expr toExpr) {
fromExpr = toExpr.(ConditionalExpr).getElse() fromExpr = toExpr.(ConditionalExpr).getElse()
} }
/** DEPRECATED: Alias for AnalyzedString */
deprecated class AnalysedString = AnalyzedString;
/** /**
* An analysed null terminated string. * An analyzed null terminated string.
*/ */
class AnalysedString extends Expr { class AnalyzedString extends Expr {
AnalysedString() { AnalyzedString() {
this.getUnspecifiedType() instanceof ArrayType or this.getUnspecifiedType() instanceof ArrayType or
this.getUnspecifiedType() instanceof PointerType this.getUnspecifiedType() instanceof PointerType
} }
@@ -41,15 +44,15 @@ class AnalysedString extends Expr {
* can be calculated. * can be calculated.
*/ */
int getMaxLength() { int getMaxLength() {
// take the longest AnalysedString it's value could 'flow' from; however if even one doesn't // take the longest AnalyzedString its value could 'flow' from; however if even one doesn't
// return a value (this essentially means 'infinity') we can't return a value either. // return a value (this essentially means 'infinity') we can't return a value either.
result = result =
max(AnalysedString expr, int toMax | max(AnalyzedString expr, int toMax |
canValueFlow*(expr, this) and toMax = expr.(StringLiteral).getOriginalLength() canValueFlow*(expr, this) and toMax = expr.(StringLiteral).getOriginalLength()
| |
toMax toMax
) and // maximum length ) and // maximum length
forall(AnalysedString expr | canValueFlow(expr, this) | exists(expr.getMaxLength())) // all sources return a value (recursive) forall(AnalyzedString expr | canValueFlow(expr, this) | exists(expr.getMaxLength())) // all sources return a value (recursive)
} }
} }

View File

@@ -708,30 +708,33 @@ private predicate straightLineSparse(Node scope, int i, Node ni, Spec spec) {
or or
scope = scope =
any(SwitchStmt s | any(SwitchStmt s |
// SwitchStmt [-> init] -> expr
i = -1 and ni = s and spec.isAt() i = -1 and ni = s and spec.isAt()
or or
i = 0 and ni = s.getExpr() and spec.isAround() i = 0 and ni = s.getInitialization() and spec.isAround()
or
i = 1 and ni = s.getExpr() and spec.isAround()
or or
// If the switch body is not a block then this step is skipped, and the // If the switch body is not a block then this step is skipped, and the
// expression jumps directly to the cases. // expression jumps directly to the cases.
i = 1 and ni = s.getStmt().(BlockStmt) and spec.isAt() i = 2 and ni = s.getStmt().(BlockStmt) and spec.isAt()
or or
i = 2 and ni = s.getASwitchCase() and spec.isBefore() i = 3 and ni = s.getASwitchCase() and spec.isBefore()
or or
// If there is no default case, we can jump to after the block. Note: `i` // If there is no default case, we can jump to after the block. Note: `i`
// is same value as above. // is same value as above.
not s.getASwitchCase() instanceof DefaultCase and not s.getASwitchCase() instanceof DefaultCase and
i = 2 and i = 3 and
ni = s.getStmt() and ni = s.getStmt() and
spec.isAfter() spec.isAfter()
or or
i = 3 and /* BARRIER */ ni = s and spec.isBarrier() i = 4 and /* BARRIER */ ni = s and spec.isBarrier()
or or
i = 4 and ni = s.getStmt() and spec.isAfter() i = 5 and ni = s.getStmt() and spec.isAfter()
or or
i = 5 and ni = s and spec.isAroundDestructors() i = 6 and ni = s and spec.isAroundDestructors()
or or
i = 6 and ni = s and spec.isAfter() i = 7 and ni = s and spec.isAfter()
) )
or or
scope = scope =
@@ -836,8 +839,15 @@ private predicate subEdge(Pos p1, Node n1, Node n2, Pos p2) {
p2.nodeAt(n2, f) p2.nodeAt(n2, f)
) )
or or
// IfStmt -> condition ; { then, else } -> // IfStmt -> [ init -> ] condition ; { then, else } ->
exists(IfStmt s | exists(IfStmt s |
p1.nodeAt(n1, s) and
p2.nodeBefore(n2, s.getInitialization())
or
p1.nodeAfter(n1, s.getInitialization()) and
p2.nodeBefore(n2, s.getCondition())
or
not exists(s.getInitialization()) and
p1.nodeAt(n1, s) and p1.nodeAt(n1, s) and
p2.nodeBefore(n2, s.getCondition()) p2.nodeBefore(n2, s.getCondition())
or or
@@ -851,8 +861,15 @@ private predicate subEdge(Pos p1, Node n1, Node n2, Pos p2) {
p2.nodeAfter(n2, s) p2.nodeAfter(n2, s)
) )
or or
// ConstexprIfStmt -> condition ; { then, else } -> // same as IfStmt // ConstexprIfStmt -> [ init -> ] condition ; { then, else } -> // same as IfStmt
exists(ConstexprIfStmt s | exists(ConstexprIfStmt s |
p1.nodeAt(n1, s) and
p2.nodeBefore(n2, s.getInitialization())
or
p1.nodeAfter(n1, s.getInitialization()) and
p2.nodeBefore(n2, s.getCondition())
or
not exists(s.getInitialization()) and
p1.nodeAt(n1, s) and p1.nodeAt(n1, s) and
p2.nodeBefore(n2, s.getCondition()) p2.nodeBefore(n2, s.getCondition())
or or
@@ -953,7 +970,7 @@ private predicate subEdge(Pos p1, Node n1, Node n2, Pos p2) {
private predicate subEdgeIncludingDestructors(Pos p1, Node n1, Node n2, Pos p2) { private predicate subEdgeIncludingDestructors(Pos p1, Node n1, Node n2, Pos p2) {
subEdge(p1, n1, n2, p2) subEdge(p1, n1, n2, p2)
or or
// If `n1` has sub-nodes to accomodate destructors, but there are none to be // If `n1` has sub-nodes to accommodate destructors, but there are none to be
// called, connect the "before destructors" node directly to the "after // called, connect the "before destructors" node directly to the "after
// destructors" node. For performance, only do this when the nodes exist. // destructors" node. For performance, only do this when the nodes exist.
exists(Pos afterDtors | afterDtors.isAfterDestructors() | subEdge(afterDtors, n1, _, _)) and exists(Pos afterDtors | afterDtors.isAfterDestructors() | subEdge(afterDtors, n1, _, _)) and

View File

@@ -170,6 +170,14 @@ abstract class Configuration extends string {
*/ */
int explorationLimit() { none() } int explorationLimit() { none() }
/**
* Holds if hidden nodes should be included in the data flow graph.
*
* This feature should only be used for debugging or when the data flow graph
* is not visualized (for example in a `path-problem` query).
*/
predicate includeHiddenNodes() { none() }
/** /**
* Holds if there is a partial data flow path from `source` to `node`. The * Holds if there is a partial data flow path from `source` to `node`. The
* approximate distance between `node` and the closest source is `dist` and * approximate distance between `node` and the closest source is `dist` and
@@ -498,23 +506,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and readSet(node1, cs, node2, config) and
c = cs.getAReadContent() pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
) )
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and clearsContentCached(n.asNode(), cs) and
c = cs.getAReadContent() pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
) )
} }
// inline to reduce fan-out via `getAReadContent`
bindingset[c]
private predicate expectsContentEx(NodeEx n, Content c) {
exists(ContentSet cs |
expectsContentCached(n.asNode(), cs) and
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
)
}
pragma[nomagic]
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
pragma[nomagic] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +813,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +911,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
read(n1, c, n2, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1201,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; private predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
expectsContentEx(node, c)
)
}
bindingset[node, state, ap, config] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and exists(ap) and
not stateBarrier(node, state, config) not stateBarrier(node, state, config) and
(
notExpectsContent(node)
or
ap = true and
expectsContentCand(node, config)
)
} }
bindingset[ap, contentType] bindingset[ap, contentType]
@@ -1646,10 +1681,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1789,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2029,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) clearContent(node, ap.getHead().getContent(), config)
} }
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
expectsContentEx(node, c) and
c = ap.getHead().getContent()
)
}
pragma[nomagic] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2047,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, config) and not clear(node, ap, config) and
if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
(
notExpectsContent(node)
or
expectsContentCand(node, ap, config)
)
} }
bindingset[ap, contentType] bindingset[ap, contentType]
@@ -2452,10 +2517,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3358,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3444,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c n.getEnclosingCallable() = c
) )
} }
pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
)
}
private newtype TSummaryCtx = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -3557,7 +3661,7 @@ private newtype TPathNode =
* of dereference operations needed to get from the value in the node to the * of dereference operations needed to get from the value in the node to the
* tracked object. The final type indicates the type of the tracked object. * tracked object. The final type indicates the type of the tracked object.
*/ */
abstract private class AccessPath extends TAccessPath { private class AccessPath extends TAccessPath {
/** Gets the head of this access path, if any. */ /** Gets the head of this access path, if any. */
abstract TypedContent getHead(); abstract TypedContent getHead();
@@ -3772,11 +3876,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx(); abstract NodeEx getNodeEx();
predicate isHidden() { predicate isHidden() {
not this.getConfiguration().includeHiddenNodes() and
(
hiddenNode(this.getNodeEx().asNode()) and hiddenNode(this.getNodeEx().asNode()) and
not this.isSource() and not this.isSource() and
not this instanceof PathNodeSink not this instanceof PathNodeSink
or or
this.getNodeEx() instanceof TNodeImplicitRead this.getNodeEx() instanceof TNodeImplicitRead
)
} }
private string ppAp() { private string ppAp() {
@@ -4257,6 +4364,12 @@ private module Subpaths {
) )
} }
pragma[nomagic]
private predicate hasSuccessor(PathNode pred, PathNodeMid succ, NodeEx succNode) {
succ = pred.getASuccessor() and
succNode = succ.getNodeEx()
}
/** /**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4377,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
pragma[only_bind_into](arg).getASuccessor() = par and pragma[only_bind_into](arg).getASuccessor() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4720,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4740,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -170,6 +170,14 @@ abstract class Configuration extends string {
*/ */
int explorationLimit() { none() } int explorationLimit() { none() }
/**
* Holds if hidden nodes should be included in the data flow graph.
*
* This feature should only be used for debugging or when the data flow graph
* is not visualized (for example in a `path-problem` query).
*/
predicate includeHiddenNodes() { none() }
/** /**
* Holds if there is a partial data flow path from `source` to `node`. The * Holds if there is a partial data flow path from `source` to `node`. The
* approximate distance between `node` and the closest source is `dist` and * approximate distance between `node` and the closest source is `dist` and
@@ -498,23 +506,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and readSet(node1, cs, node2, config) and
c = cs.getAReadContent() pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
) )
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and clearsContentCached(n.asNode(), cs) and
c = cs.getAReadContent() pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
) )
} }
// inline to reduce fan-out via `getAReadContent`
bindingset[c]
private predicate expectsContentEx(NodeEx n, Content c) {
exists(ContentSet cs |
expectsContentCached(n.asNode(), cs) and
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
)
}
pragma[nomagic]
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
pragma[nomagic] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +813,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +911,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
read(n1, c, n2, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1201,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; private predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
expectsContentEx(node, c)
)
}
bindingset[node, state, ap, config] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and exists(ap) and
not stateBarrier(node, state, config) not stateBarrier(node, state, config) and
(
notExpectsContent(node)
or
ap = true and
expectsContentCand(node, config)
)
} }
bindingset[ap, contentType] bindingset[ap, contentType]
@@ -1646,10 +1681,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1789,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2029,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) clearContent(node, ap.getHead().getContent(), config)
} }
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
expectsContentEx(node, c) and
c = ap.getHead().getContent()
)
}
pragma[nomagic] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2047,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, config) and not clear(node, ap, config) and
if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
(
notExpectsContent(node)
or
expectsContentCand(node, ap, config)
)
} }
bindingset[ap, contentType] bindingset[ap, contentType]
@@ -2452,10 +2517,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3358,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3444,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c n.getEnclosingCallable() = c
) )
} }
pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
)
}
private newtype TSummaryCtx = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -3557,7 +3661,7 @@ private newtype TPathNode =
* of dereference operations needed to get from the value in the node to the * of dereference operations needed to get from the value in the node to the
* tracked object. The final type indicates the type of the tracked object. * tracked object. The final type indicates the type of the tracked object.
*/ */
abstract private class AccessPath extends TAccessPath { private class AccessPath extends TAccessPath {
/** Gets the head of this access path, if any. */ /** Gets the head of this access path, if any. */
abstract TypedContent getHead(); abstract TypedContent getHead();
@@ -3772,11 +3876,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx(); abstract NodeEx getNodeEx();
predicate isHidden() { predicate isHidden() {
not this.getConfiguration().includeHiddenNodes() and
(
hiddenNode(this.getNodeEx().asNode()) and hiddenNode(this.getNodeEx().asNode()) and
not this.isSource() and not this.isSource() and
not this instanceof PathNodeSink not this instanceof PathNodeSink
or or
this.getNodeEx() instanceof TNodeImplicitRead this.getNodeEx() instanceof TNodeImplicitRead
)
} }
private string ppAp() { private string ppAp() {
@@ -4257,6 +4364,12 @@ private module Subpaths {
) )
} }
pragma[nomagic]
private predicate hasSuccessor(PathNode pred, PathNodeMid succ, NodeEx succNode) {
succ = pred.getASuccessor() and
succNode = succ.getNodeEx()
}
/** /**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4377,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
pragma[only_bind_into](arg).getASuccessor() = par and pragma[only_bind_into](arg).getASuccessor() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4720,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4740,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -170,6 +170,14 @@ abstract class Configuration extends string {
*/ */
int explorationLimit() { none() } int explorationLimit() { none() }
/**
* Holds if hidden nodes should be included in the data flow graph.
*
* This feature should only be used for debugging or when the data flow graph
* is not visualized (for example in a `path-problem` query).
*/
predicate includeHiddenNodes() { none() }
/** /**
* Holds if there is a partial data flow path from `source` to `node`. The * Holds if there is a partial data flow path from `source` to `node`. The
* approximate distance between `node` and the closest source is `dist` and * approximate distance between `node` and the closest source is `dist` and
@@ -498,23 +506,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and readSet(node1, cs, node2, config) and
c = cs.getAReadContent() pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
) )
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and clearsContentCached(n.asNode(), cs) and
c = cs.getAReadContent() pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
) )
} }
// inline to reduce fan-out via `getAReadContent`
bindingset[c]
private predicate expectsContentEx(NodeEx n, Content c) {
exists(ContentSet cs |
expectsContentCached(n.asNode(), cs) and
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
)
}
pragma[nomagic]
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
pragma[nomagic] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +813,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +911,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
read(n1, c, n2, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1201,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; private predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
expectsContentEx(node, c)
)
}
bindingset[node, state, ap, config] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and exists(ap) and
not stateBarrier(node, state, config) not stateBarrier(node, state, config) and
(
notExpectsContent(node)
or
ap = true and
expectsContentCand(node, config)
)
} }
bindingset[ap, contentType] bindingset[ap, contentType]
@@ -1646,10 +1681,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1789,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2029,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) clearContent(node, ap.getHead().getContent(), config)
} }
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
expectsContentEx(node, c) and
c = ap.getHead().getContent()
)
}
pragma[nomagic] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2047,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, config) and not clear(node, ap, config) and
if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
(
notExpectsContent(node)
or
expectsContentCand(node, ap, config)
)
} }
bindingset[ap, contentType] bindingset[ap, contentType]
@@ -2452,10 +2517,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3358,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3444,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c n.getEnclosingCallable() = c
) )
} }
pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
)
}
private newtype TSummaryCtx = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -3557,7 +3661,7 @@ private newtype TPathNode =
* of dereference operations needed to get from the value in the node to the * of dereference operations needed to get from the value in the node to the
* tracked object. The final type indicates the type of the tracked object. * tracked object. The final type indicates the type of the tracked object.
*/ */
abstract private class AccessPath extends TAccessPath { private class AccessPath extends TAccessPath {
/** Gets the head of this access path, if any. */ /** Gets the head of this access path, if any. */
abstract TypedContent getHead(); abstract TypedContent getHead();
@@ -3772,11 +3876,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx(); abstract NodeEx getNodeEx();
predicate isHidden() { predicate isHidden() {
not this.getConfiguration().includeHiddenNodes() and
(
hiddenNode(this.getNodeEx().asNode()) and hiddenNode(this.getNodeEx().asNode()) and
not this.isSource() and not this.isSource() and
not this instanceof PathNodeSink not this instanceof PathNodeSink
or or
this.getNodeEx() instanceof TNodeImplicitRead this.getNodeEx() instanceof TNodeImplicitRead
)
} }
private string ppAp() { private string ppAp() {
@@ -4257,6 +4364,12 @@ private module Subpaths {
) )
} }
pragma[nomagic]
private predicate hasSuccessor(PathNode pred, PathNodeMid succ, NodeEx succNode) {
succ = pred.getASuccessor() and
succNode = succ.getNodeEx()
}
/** /**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4377,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
pragma[only_bind_into](arg).getASuccessor() = par and pragma[only_bind_into](arg).getASuccessor() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4720,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4740,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -170,6 +170,14 @@ abstract class Configuration extends string {
*/ */
int explorationLimit() { none() } int explorationLimit() { none() }
/**
* Holds if hidden nodes should be included in the data flow graph.
*
* This feature should only be used for debugging or when the data flow graph
* is not visualized (for example in a `path-problem` query).
*/
predicate includeHiddenNodes() { none() }
/** /**
* Holds if there is a partial data flow path from `source` to `node`. The * Holds if there is a partial data flow path from `source` to `node`. The
* approximate distance between `node` and the closest source is `dist` and * approximate distance between `node` and the closest source is `dist` and
@@ -498,23 +506,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and readSet(node1, cs, node2, config) and
c = cs.getAReadContent() pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
) )
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and clearsContentCached(n.asNode(), cs) and
c = cs.getAReadContent() pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
) )
} }
// inline to reduce fan-out via `getAReadContent`
bindingset[c]
private predicate expectsContentEx(NodeEx n, Content c) {
exists(ContentSet cs |
expectsContentCached(n.asNode(), cs) and
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
)
}
pragma[nomagic]
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
pragma[nomagic] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +813,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +911,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
read(n1, c, n2, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1201,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; private predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
expectsContentEx(node, c)
)
}
bindingset[node, state, ap, config] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and exists(ap) and
not stateBarrier(node, state, config) not stateBarrier(node, state, config) and
(
notExpectsContent(node)
or
ap = true and
expectsContentCand(node, config)
)
} }
bindingset[ap, contentType] bindingset[ap, contentType]
@@ -1646,10 +1681,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1789,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2029,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) clearContent(node, ap.getHead().getContent(), config)
} }
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
expectsContentEx(node, c) and
c = ap.getHead().getContent()
)
}
pragma[nomagic] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2047,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, config) and not clear(node, ap, config) and
if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
(
notExpectsContent(node)
or
expectsContentCand(node, ap, config)
)
} }
bindingset[ap, contentType] bindingset[ap, contentType]
@@ -2452,10 +2517,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3358,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3444,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c n.getEnclosingCallable() = c
) )
} }
pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
)
}
private newtype TSummaryCtx = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -3557,7 +3661,7 @@ private newtype TPathNode =
* of dereference operations needed to get from the value in the node to the * of dereference operations needed to get from the value in the node to the
* tracked object. The final type indicates the type of the tracked object. * tracked object. The final type indicates the type of the tracked object.
*/ */
abstract private class AccessPath extends TAccessPath { private class AccessPath extends TAccessPath {
/** Gets the head of this access path, if any. */ /** Gets the head of this access path, if any. */
abstract TypedContent getHead(); abstract TypedContent getHead();
@@ -3772,11 +3876,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx(); abstract NodeEx getNodeEx();
predicate isHidden() { predicate isHidden() {
not this.getConfiguration().includeHiddenNodes() and
(
hiddenNode(this.getNodeEx().asNode()) and hiddenNode(this.getNodeEx().asNode()) and
not this.isSource() and not this.isSource() and
not this instanceof PathNodeSink not this instanceof PathNodeSink
or or
this.getNodeEx() instanceof TNodeImplicitRead this.getNodeEx() instanceof TNodeImplicitRead
)
} }
private string ppAp() { private string ppAp() {
@@ -4257,6 +4364,12 @@ private module Subpaths {
) )
} }
pragma[nomagic]
private predicate hasSuccessor(PathNode pred, PathNodeMid succ, NodeEx succNode) {
succ = pred.getASuccessor() and
succNode = succ.getNodeEx()
}
/** /**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4377,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
pragma[only_bind_into](arg).getASuccessor() = par and pragma[only_bind_into](arg).getASuccessor() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4720,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4740,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -305,7 +305,7 @@ cached
private module Cached { private module Cached {
/** /**
* If needed, call this predicate from `DataFlowImplSpecific.qll` in order to * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to
* force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby * force a stage-dependency on the `DataFlowImplCommon.qll` stage and thereby
* collapsing the two stages. * collapsing the two stages.
*/ */
cached cached
@@ -328,6 +328,9 @@ private module Cached {
cached cached
predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) } predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) }
cached
predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) }
cached cached
predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) }

View File

@@ -170,6 +170,14 @@ abstract class Configuration extends string {
*/ */
int explorationLimit() { none() } int explorationLimit() { none() }
/**
* Holds if hidden nodes should be included in the data flow graph.
*
* This feature should only be used for debugging or when the data flow graph
* is not visualized (for example in a `path-problem` query).
*/
predicate includeHiddenNodes() { none() }
/** /**
* Holds if there is a partial data flow path from `source` to `node`. The * Holds if there is a partial data flow path from `source` to `node`. The
* approximate distance between `node` and the closest source is `dist` and * approximate distance between `node` and the closest source is `dist` and
@@ -498,23 +506,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and readSet(node1, cs, node2, config) and
c = cs.getAReadContent() pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
) )
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and clearsContentCached(n.asNode(), cs) and
c = cs.getAReadContent() pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
) )
} }
// inline to reduce fan-out via `getAReadContent`
bindingset[c]
private predicate expectsContentEx(NodeEx n, Content c) {
exists(ContentSet cs |
expectsContentCached(n.asNode(), cs) and
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
)
}
pragma[nomagic]
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
pragma[nomagic] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +813,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +911,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
read(n1, c, n2, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1201,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; private predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
expectsContentEx(node, c)
)
}
bindingset[node, state, ap, config] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and exists(ap) and
not stateBarrier(node, state, config) not stateBarrier(node, state, config) and
(
notExpectsContent(node)
or
ap = true and
expectsContentCand(node, config)
)
} }
bindingset[ap, contentType] bindingset[ap, contentType]
@@ -1646,10 +1681,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1789,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2029,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) clearContent(node, ap.getHead().getContent(), config)
} }
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
expectsContentEx(node, c) and
c = ap.getHead().getContent()
)
}
pragma[nomagic] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2047,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, config) and not clear(node, ap, config) and
if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
(
notExpectsContent(node)
or
expectsContentCand(node, ap, config)
)
} }
bindingset[ap, contentType] bindingset[ap, contentType]
@@ -2452,10 +2517,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3358,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3444,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c n.getEnclosingCallable() = c
) )
} }
pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
)
}
private newtype TSummaryCtx = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -3557,7 +3661,7 @@ private newtype TPathNode =
* of dereference operations needed to get from the value in the node to the * of dereference operations needed to get from the value in the node to the
* tracked object. The final type indicates the type of the tracked object. * tracked object. The final type indicates the type of the tracked object.
*/ */
abstract private class AccessPath extends TAccessPath { private class AccessPath extends TAccessPath {
/** Gets the head of this access path, if any. */ /** Gets the head of this access path, if any. */
abstract TypedContent getHead(); abstract TypedContent getHead();
@@ -3772,11 +3876,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx(); abstract NodeEx getNodeEx();
predicate isHidden() { predicate isHidden() {
not this.getConfiguration().includeHiddenNodes() and
(
hiddenNode(this.getNodeEx().asNode()) and hiddenNode(this.getNodeEx().asNode()) and
not this.isSource() and not this.isSource() and
not this instanceof PathNodeSink not this instanceof PathNodeSink
or or
this.getNodeEx() instanceof TNodeImplicitRead this.getNodeEx() instanceof TNodeImplicitRead
)
} }
private string ppAp() { private string ppAp() {
@@ -4257,6 +4364,12 @@ private module Subpaths {
) )
} }
pragma[nomagic]
private predicate hasSuccessor(PathNode pred, PathNodeMid succ, NodeEx succNode) {
succ = pred.getASuccessor() and
succNode = succ.getNodeEx()
}
/** /**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4377,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
pragma[only_bind_into](arg).getASuccessor() = par and pragma[only_bind_into](arg).getASuccessor() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4720,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4740,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -198,6 +198,12 @@ predicate clearsContent(Node n, Content c) {
none() // stub implementation none() // stub implementation
} }
/**
* Holds if the value that is being tracked is expected to be stored inside content `c`
* at node `n`.
*/
predicate expectsContent(Node n, ContentSet c) { none() }
/** Gets the type of `n` used for type pruning. */ /** Gets the type of `n` used for type pruning. */
Type getNodeType(Node n) { Type getNodeType(Node n) {
suppressUnusedNode(n) and suppressUnusedNode(n) and

View File

@@ -170,6 +170,14 @@ abstract class Configuration extends string {
*/ */
int explorationLimit() { none() } int explorationLimit() { none() }
/**
* Holds if hidden nodes should be included in the data flow graph.
*
* This feature should only be used for debugging or when the data flow graph
* is not visualized (for example in a `path-problem` query).
*/
predicate includeHiddenNodes() { none() }
/** /**
* Holds if there is a partial data flow path from `source` to `node`. The * Holds if there is a partial data flow path from `source` to `node`. The
* approximate distance between `node` and the closest source is `dist` and * approximate distance between `node` and the closest source is `dist` and
@@ -498,23 +506,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and readSet(node1, cs, node2, config) and
c = cs.getAReadContent() pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
) )
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and clearsContentCached(n.asNode(), cs) and
c = cs.getAReadContent() pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
) )
} }
// inline to reduce fan-out via `getAReadContent`
bindingset[c]
private predicate expectsContentEx(NodeEx n, Content c) {
exists(ContentSet cs |
expectsContentCached(n.asNode(), cs) and
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
)
}
pragma[nomagic]
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
pragma[nomagic] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +813,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +911,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
read(n1, c, n2, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1201,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; private predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
expectsContentEx(node, c)
)
}
bindingset[node, state, ap, config] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and exists(ap) and
not stateBarrier(node, state, config) not stateBarrier(node, state, config) and
(
notExpectsContent(node)
or
ap = true and
expectsContentCand(node, config)
)
} }
bindingset[ap, contentType] bindingset[ap, contentType]
@@ -1646,10 +1681,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1789,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2029,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) clearContent(node, ap.getHead().getContent(), config)
} }
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
expectsContentEx(node, c) and
c = ap.getHead().getContent()
)
}
pragma[nomagic] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2047,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, config) and not clear(node, ap, config) and
if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
(
notExpectsContent(node)
or
expectsContentCand(node, ap, config)
)
} }
bindingset[ap, contentType] bindingset[ap, contentType]
@@ -2452,10 +2517,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3358,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3444,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c n.getEnclosingCallable() = c
) )
} }
pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
)
}
private newtype TSummaryCtx = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -3557,7 +3661,7 @@ private newtype TPathNode =
* of dereference operations needed to get from the value in the node to the * of dereference operations needed to get from the value in the node to the
* tracked object. The final type indicates the type of the tracked object. * tracked object. The final type indicates the type of the tracked object.
*/ */
abstract private class AccessPath extends TAccessPath { private class AccessPath extends TAccessPath {
/** Gets the head of this access path, if any. */ /** Gets the head of this access path, if any. */
abstract TypedContent getHead(); abstract TypedContent getHead();
@@ -3772,11 +3876,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx(); abstract NodeEx getNodeEx();
predicate isHidden() { predicate isHidden() {
not this.getConfiguration().includeHiddenNodes() and
(
hiddenNode(this.getNodeEx().asNode()) and hiddenNode(this.getNodeEx().asNode()) and
not this.isSource() and not this.isSource() and
not this instanceof PathNodeSink not this instanceof PathNodeSink
or or
this.getNodeEx() instanceof TNodeImplicitRead this.getNodeEx() instanceof TNodeImplicitRead
)
} }
private string ppAp() { private string ppAp() {
@@ -4257,6 +4364,12 @@ private module Subpaths {
) )
} }
pragma[nomagic]
private predicate hasSuccessor(PathNode pred, PathNodeMid succ, NodeEx succNode) {
succ = pred.getASuccessor() and
succNode = succ.getNodeEx()
}
/** /**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4377,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
pragma[only_bind_into](arg).getASuccessor() = par and pragma[only_bind_into](arg).getASuccessor() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4720,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4740,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -170,6 +170,14 @@ abstract class Configuration extends string {
*/ */
int explorationLimit() { none() } int explorationLimit() { none() }
/**
* Holds if hidden nodes should be included in the data flow graph.
*
* This feature should only be used for debugging or when the data flow graph
* is not visualized (for example in a `path-problem` query).
*/
predicate includeHiddenNodes() { none() }
/** /**
* Holds if there is a partial data flow path from `source` to `node`. The * Holds if there is a partial data flow path from `source` to `node`. The
* approximate distance between `node` and the closest source is `dist` and * approximate distance between `node` and the closest source is `dist` and
@@ -498,23 +506,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and readSet(node1, cs, node2, config) and
c = cs.getAReadContent() pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
) )
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and clearsContentCached(n.asNode(), cs) and
c = cs.getAReadContent() pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
) )
} }
// inline to reduce fan-out via `getAReadContent`
bindingset[c]
private predicate expectsContentEx(NodeEx n, Content c) {
exists(ContentSet cs |
expectsContentCached(n.asNode(), cs) and
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
)
}
pragma[nomagic]
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
pragma[nomagic] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +813,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +911,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
read(n1, c, n2, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1201,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; private predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
expectsContentEx(node, c)
)
}
bindingset[node, state, ap, config] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and exists(ap) and
not stateBarrier(node, state, config) not stateBarrier(node, state, config) and
(
notExpectsContent(node)
or
ap = true and
expectsContentCand(node, config)
)
} }
bindingset[ap, contentType] bindingset[ap, contentType]
@@ -1646,10 +1681,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1789,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2029,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) clearContent(node, ap.getHead().getContent(), config)
} }
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
expectsContentEx(node, c) and
c = ap.getHead().getContent()
)
}
pragma[nomagic] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2047,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, config) and not clear(node, ap, config) and
if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
(
notExpectsContent(node)
or
expectsContentCand(node, ap, config)
)
} }
bindingset[ap, contentType] bindingset[ap, contentType]
@@ -2452,10 +2517,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3358,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3444,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c n.getEnclosingCallable() = c
) )
} }
pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
)
}
private newtype TSummaryCtx = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -3557,7 +3661,7 @@ private newtype TPathNode =
* of dereference operations needed to get from the value in the node to the * of dereference operations needed to get from the value in the node to the
* tracked object. The final type indicates the type of the tracked object. * tracked object. The final type indicates the type of the tracked object.
*/ */
abstract private class AccessPath extends TAccessPath { private class AccessPath extends TAccessPath {
/** Gets the head of this access path, if any. */ /** Gets the head of this access path, if any. */
abstract TypedContent getHead(); abstract TypedContent getHead();
@@ -3772,11 +3876,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx(); abstract NodeEx getNodeEx();
predicate isHidden() { predicate isHidden() {
not this.getConfiguration().includeHiddenNodes() and
(
hiddenNode(this.getNodeEx().asNode()) and hiddenNode(this.getNodeEx().asNode()) and
not this.isSource() and not this.isSource() and
not this instanceof PathNodeSink not this instanceof PathNodeSink
or or
this.getNodeEx() instanceof TNodeImplicitRead this.getNodeEx() instanceof TNodeImplicitRead
)
} }
private string ppAp() { private string ppAp() {
@@ -4257,6 +4364,12 @@ private module Subpaths {
) )
} }
pragma[nomagic]
private predicate hasSuccessor(PathNode pred, PathNodeMid succ, NodeEx succNode) {
succ = pred.getASuccessor() and
succNode = succ.getNodeEx()
}
/** /**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4377,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
pragma[only_bind_into](arg).getASuccessor() = par and pragma[only_bind_into](arg).getASuccessor() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4720,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4740,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -170,6 +170,14 @@ abstract class Configuration extends string {
*/ */
int explorationLimit() { none() } int explorationLimit() { none() }
/**
* Holds if hidden nodes should be included in the data flow graph.
*
* This feature should only be used for debugging or when the data flow graph
* is not visualized (for example in a `path-problem` query).
*/
predicate includeHiddenNodes() { none() }
/** /**
* Holds if there is a partial data flow path from `source` to `node`. The * Holds if there is a partial data flow path from `source` to `node`. The
* approximate distance between `node` and the closest source is `dist` and * approximate distance between `node` and the closest source is `dist` and
@@ -498,23 +506,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and readSet(node1, cs, node2, config) and
c = cs.getAReadContent() pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
) )
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and clearsContentCached(n.asNode(), cs) and
c = cs.getAReadContent() pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
) )
} }
// inline to reduce fan-out via `getAReadContent`
bindingset[c]
private predicate expectsContentEx(NodeEx n, Content c) {
exists(ContentSet cs |
expectsContentCached(n.asNode(), cs) and
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
)
}
pragma[nomagic]
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
pragma[nomagic] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +813,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +911,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
read(n1, c, n2, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1201,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; private predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
expectsContentEx(node, c)
)
}
bindingset[node, state, ap, config] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and exists(ap) and
not stateBarrier(node, state, config) not stateBarrier(node, state, config) and
(
notExpectsContent(node)
or
ap = true and
expectsContentCand(node, config)
)
} }
bindingset[ap, contentType] bindingset[ap, contentType]
@@ -1646,10 +1681,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1789,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2029,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) clearContent(node, ap.getHead().getContent(), config)
} }
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
expectsContentEx(node, c) and
c = ap.getHead().getContent()
)
}
pragma[nomagic] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2047,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, config) and not clear(node, ap, config) and
if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
(
notExpectsContent(node)
or
expectsContentCand(node, ap, config)
)
} }
bindingset[ap, contentType] bindingset[ap, contentType]
@@ -2452,10 +2517,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3358,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3444,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c n.getEnclosingCallable() = c
) )
} }
pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
)
}
private newtype TSummaryCtx = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -3557,7 +3661,7 @@ private newtype TPathNode =
* of dereference operations needed to get from the value in the node to the * of dereference operations needed to get from the value in the node to the
* tracked object. The final type indicates the type of the tracked object. * tracked object. The final type indicates the type of the tracked object.
*/ */
abstract private class AccessPath extends TAccessPath { private class AccessPath extends TAccessPath {
/** Gets the head of this access path, if any. */ /** Gets the head of this access path, if any. */
abstract TypedContent getHead(); abstract TypedContent getHead();
@@ -3772,11 +3876,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx(); abstract NodeEx getNodeEx();
predicate isHidden() { predicate isHidden() {
not this.getConfiguration().includeHiddenNodes() and
(
hiddenNode(this.getNodeEx().asNode()) and hiddenNode(this.getNodeEx().asNode()) and
not this.isSource() and not this.isSource() and
not this instanceof PathNodeSink not this instanceof PathNodeSink
or or
this.getNodeEx() instanceof TNodeImplicitRead this.getNodeEx() instanceof TNodeImplicitRead
)
} }
private string ppAp() { private string ppAp() {
@@ -4257,6 +4364,12 @@ private module Subpaths {
) )
} }
pragma[nomagic]
private predicate hasSuccessor(PathNode pred, PathNodeMid succ, NodeEx succNode) {
succ = pred.getASuccessor() and
succNode = succ.getNodeEx()
}
/** /**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4377,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
pragma[only_bind_into](arg).getASuccessor() = par and pragma[only_bind_into](arg).getASuccessor() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4720,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4740,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -170,6 +170,14 @@ abstract class Configuration extends string {
*/ */
int explorationLimit() { none() } int explorationLimit() { none() }
/**
* Holds if hidden nodes should be included in the data flow graph.
*
* This feature should only be used for debugging or when the data flow graph
* is not visualized (for example in a `path-problem` query).
*/
predicate includeHiddenNodes() { none() }
/** /**
* Holds if there is a partial data flow path from `source` to `node`. The * Holds if there is a partial data flow path from `source` to `node`. The
* approximate distance between `node` and the closest source is `dist` and * approximate distance between `node` and the closest source is `dist` and
@@ -498,23 +506,35 @@ private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuratio
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
exists(ContentSet cs | exists(ContentSet cs |
readSet(node1, cs, node2, config) and readSet(node1, cs, node2, config) and
c = cs.getAReadContent() pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
) )
} }
// inline to reduce fan-out via `getAReadContent` // inline to reduce fan-out via `getAReadContent`
pragma[inline] bindingset[c]
private predicate clearsContentEx(NodeEx n, Content c) { private predicate clearsContentEx(NodeEx n, Content c) {
exists(ContentSet cs | exists(ContentSet cs |
clearsContentCached(n.asNode(), cs) and clearsContentCached(n.asNode(), cs) and
c = cs.getAReadContent() pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
) )
} }
// inline to reduce fan-out via `getAReadContent`
bindingset[c]
private predicate expectsContentEx(NodeEx n, Content c) {
exists(ContentSet cs |
expectsContentCached(n.asNode(), cs) and
pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent()
)
}
pragma[nomagic]
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
pragma[nomagic] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
@@ -793,7 +813,7 @@ private module Stage1 {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
@@ -891,7 +911,7 @@ private module Stage1 {
pragma[nomagic] pragma[nomagic]
predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) {
revFlowIsReadAndStored(pragma[only_bind_into](c), pragma[only_bind_into](config)) and revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
read(n1, c, n2, pragma[only_bind_into](config)) and read(n1, c, n2, pragma[only_bind_into](config)) and
revFlow(n2, pragma[only_bind_into](config)) revFlow(n2, pragma[only_bind_into](config))
} }
@@ -1181,11 +1201,26 @@ private module Stage2 {
private predicate flowIntoCall = flowIntoCallNodeCand1/5; private predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and
expectsContentEx(node, c)
)
}
bindingset[node, state, ap, config] bindingset[node, state, ap, config]
private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and exists(ap) and
not stateBarrier(node, state, config) not stateBarrier(node, state, config) and
(
notExpectsContent(node)
or
ap = true and
expectsContentCand(node, config)
)
} }
bindingset[ap, contentType] bindingset[ap, contentType]
@@ -1646,10 +1681,24 @@ private module Stage2 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -1740,7 +1789,8 @@ private module LocalFlowBigStep {
private class FlowCheckNode extends NodeEx { private class FlowCheckNode extends NodeEx {
FlowCheckNode() { FlowCheckNode() {
castNode(this.asNode()) or castNode(this.asNode()) or
clearsContentCached(this.asNode(), _) clearsContentCached(this.asNode(), _) or
expectsContentCached(this.asNode(), _)
} }
} }
@@ -1979,6 +2029,16 @@ private module Stage3 {
clearContent(node, ap.getHead().getContent(), config) clearContent(node, ap.getHead().getContent(), config)
} }
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) {
exists(Content c |
PrevStage::revFlow(node, pragma[only_bind_into](config)) and
PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and
expectsContentEx(node, c) and
c = ap.getHead().getContent()
)
}
pragma[nomagic] pragma[nomagic]
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
@@ -1987,7 +2047,12 @@ private module Stage3 {
exists(state) and exists(state) and
exists(config) and exists(config) and
not clear(node, ap, config) and not clear(node, ap, config) and
if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any() (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and
(
notExpectsContent(node)
or
expectsContentCand(node, ap, config)
)
} }
bindingset[ap, contentType] bindingset[ap, contentType]
@@ -2452,10 +2517,24 @@ private module Stage3 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3279,10 +3358,24 @@ private module Stage4 {
storeStepFwd(_, ap, tc, _, _, config) storeStepFwd(_, ap, tc, _, _, config)
} }
predicate consCand(TypedContent tc, Ap ap, Configuration config) { private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
storeStepCand(_, ap, tc, _, _, config) storeStepCand(_, ap, tc, _, _, config)
} }
private predicate validAp(Ap ap, Configuration config) {
revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
or
exists(TypedContent head, Ap tail |
consCand(head, tail, config) and
ap = apCons(head, tail)
)
}
predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and
validAp(ap, config)
}
pragma[noinline] pragma[noinline]
private predicate parameterFlow( private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
@@ -3351,17 +3444,28 @@ private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
} }
private predicate nodeMayUseSummary( pragma[nomagic]
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
) { ) {
exists(DataFlowCallable c, AccessPathApprox apa0 | exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, apa, _) and Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c n.getEnclosingCallable() = c
) )
} }
pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
)
}
private newtype TSummaryCtx = private newtype TSummaryCtx =
TSummaryCtxNone() or TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
@@ -3557,7 +3661,7 @@ private newtype TPathNode =
* of dereference operations needed to get from the value in the node to the * of dereference operations needed to get from the value in the node to the
* tracked object. The final type indicates the type of the tracked object. * tracked object. The final type indicates the type of the tracked object.
*/ */
abstract private class AccessPath extends TAccessPath { private class AccessPath extends TAccessPath {
/** Gets the head of this access path, if any. */ /** Gets the head of this access path, if any. */
abstract TypedContent getHead(); abstract TypedContent getHead();
@@ -3772,11 +3876,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx(); abstract NodeEx getNodeEx();
predicate isHidden() { predicate isHidden() {
not this.getConfiguration().includeHiddenNodes() and
(
hiddenNode(this.getNodeEx().asNode()) and hiddenNode(this.getNodeEx().asNode()) and
not this.isSource() and not this.isSource() and
not this instanceof PathNodeSink not this instanceof PathNodeSink
or or
this.getNodeEx() instanceof TNodeImplicitRead this.getNodeEx() instanceof TNodeImplicitRead
)
} }
private string ppAp() { private string ppAp() {
@@ -4257,6 +4364,12 @@ private module Subpaths {
) )
} }
pragma[nomagic]
private predicate hasSuccessor(PathNode pred, PathNodeMid succ, NodeEx succNode) {
succ = pred.getASuccessor() and
succNode = succ.getNodeEx()
}
/** /**
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
@@ -4264,15 +4377,13 @@ private module Subpaths {
*/ */
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
pragma[only_bind_into](arg).getASuccessor() = par and pragma[only_bind_into](arg).getASuccessor() = pragma[only_bind_into](out0) and
pragma[only_bind_into](arg).getASuccessor() = out0 and subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
subpaths03(arg, p, localStepToHidden*(ret), o, sout, apout) and hasSuccessor(pragma[only_bind_into](arg), par, p) and
not ret.isHidden() and not ret.isHidden() and
par.getNodeEx() = p and pathNode(out0, o, sout, _, _, apout, _, _)
out0.getNodeEx() = o and |
out0.getState() = sout and out = out0 or out = out0.projectToSink()
out0.getAp() = apout and
(out = out0 or out = out0.projectToSink())
) )
} }
@@ -4609,6 +4720,10 @@ private module FlowExploration {
exists(PartialPathNodeRev mid | exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
not clearsContentEx(node, ap.getHead()) and not clearsContentEx(node, ap.getHead()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead())
) and
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
@@ -4625,6 +4740,10 @@ private module FlowExploration {
not fullBarrier(node, config) and not fullBarrier(node, config) and
not stateBarrier(node, state, config) and not stateBarrier(node, state, config) and
not clearsContentEx(node, ap.getHead().getContent()) and not clearsContentEx(node, ap.getHead().getContent()) and
(
notExpectsContent(node) or
expectsContentEx(node, ap.getHead().getContent())
) and
if node.asNode() instanceof CastingNode if node.asNode() instanceof CastingNode
then compatibleTypes(node.getDataFlowType(), ap.getType()) then compatibleTypes(node.getDataFlowType(), ap.getType())
else any() else any()

View File

@@ -305,7 +305,7 @@ cached
private module Cached { private module Cached {
/** /**
* If needed, call this predicate from `DataFlowImplSpecific.qll` in order to * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to
* force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby * force a stage-dependency on the `DataFlowImplCommon.qll` stage and thereby
* collapsing the two stages. * collapsing the two stages.
*/ */
cached cached
@@ -328,6 +328,9 @@ private module Cached {
cached cached
predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) } predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) }
cached
predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) }
cached cached
predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) }

View File

@@ -279,6 +279,12 @@ predicate clearsContent(Node n, Content c) {
none() // stub implementation none() // stub implementation
} }
/**
* Holds if the value that is being tracked is expected to be stored inside content `c`
* at node `n`.
*/
predicate expectsContent(Node n, ContentSet c) { none() }
/** Gets the type of `n` used for type pruning. */ /** Gets the type of `n` used for type pruning. */
IRType getNodeType(Node n) { IRType getNodeType(Node n) {
suppressUnusedNode(n) and suppressUnusedNode(n) and

View File

@@ -37,7 +37,7 @@ private module Cached {
* along the chain of addresses computed by `StoreNodeInstr.getInner` to identify field writes * along the chain of addresses computed by `StoreNodeInstr.getInner` to identify field writes
* and call `storeStep` accordingly (i.e., for an expression like `a.b.c = x`, we visit `c`, then * and call `storeStep` accordingly (i.e., for an expression like `a.b.c = x`, we visit `c`, then
* `b`, then `a`). * `b`, then `a`).
* 2. Flow is transfered from a `WriteSideEffectInstruction` to a `StoreNodeOperand` after flow * 2. Flow is transferred from a `WriteSideEffectInstruction` to a `StoreNodeOperand` after flow
* returns to a caller. Flow will then proceed to the defining instruction of the operand (because * returns to a caller. Flow will then proceed to the defining instruction of the operand (because
* the `StoreNodeInstr` computed by `StoreNodeOperand.getInner()` is the `StoreNode` containing * the `StoreNodeInstr` computed by `StoreNodeOperand.getInner()` is the `StoreNode` containing
* the defining instruction), and then along the chain computed by `StoreNodeInstr.getInner` like * the defining instruction), and then along the chain computed by `StoreNodeInstr.getInner` like

View File

@@ -67,7 +67,7 @@ class DefaultEdge extends EdgeKind, TDefaultEdge {
/** /**
* A "case" edge, representing the successor of a `Switch` instruction when the * A "case" edge, representing the successor of a `Switch` instruction when the
* the condition value matches a correponding `case` label. * the condition value matches a corresponding `case` label.
*/ */
class CaseEdge extends EdgeKind, TCaseEdge { class CaseEdge extends EdgeKind, TCaseEdge {
string minValue; string minValue;

View File

@@ -34,7 +34,7 @@ class ValueNumber extends TValueNumber {
final Instruction getAnInstruction() { this = valueNumber(result) } final Instruction getAnInstruction() { this = valueNumber(result) }
/** /**
* Gets one of the instructions that was assigned this value number. The chosen instuction is * Gets one of the instructions that was assigned this value number. The chosen instruction is
* deterministic but arbitrary. Intended for use only in debugging. * deterministic but arbitrary. Intended for use only in debugging.
*/ */
final Instruction getExampleInstruction() { final Instruction getExampleInstruction() {

View File

@@ -1005,7 +1005,7 @@ predicate canReuseSsaForMemoryResult(Instruction instruction) {
deprecated predicate canReuseSSAForMemoryResult = canReuseSsaForMemoryResult/1; deprecated predicate canReuseSSAForMemoryResult = canReuseSsaForMemoryResult/1;
/** /**
* Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the * Expose some of the internal predicates to PrintSSA.qll. We do this by publicly importing those modules in the
* `DebugSSA` module, which is then imported by PrintSSA. * `DebugSSA` module, which is then imported by PrintSSA.
*/ */
module DebugSsa { module DebugSsa {

View File

@@ -34,7 +34,7 @@ class ValueNumber extends TValueNumber {
final Instruction getAnInstruction() { this = valueNumber(result) } final Instruction getAnInstruction() { this = valueNumber(result) }
/** /**
* Gets one of the instructions that was assigned this value number. The chosen instuction is * Gets one of the instructions that was assigned this value number. The chosen instruction is
* deterministic but arbitrary. Intended for use only in debugging. * deterministic but arbitrary. Intended for use only in debugging.
*/ */
final Instruction getExampleInstruction() { final Instruction getExampleInstruction() {

View File

@@ -421,20 +421,36 @@ class TranslatedCatchAnyHandler extends TranslatedHandler {
class TranslatedIfStmt extends TranslatedStmt, ConditionContext { class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
override IfStmt stmt; override IfStmt stmt;
override Instruction getFirstInstruction() { result = getCondition().getFirstInstruction() } override Instruction getFirstInstruction() {
if hasInitialization()
then result = getInitialization().getFirstInstruction()
else result = getFirstConditionInstruction()
}
override TranslatedElement getChild(int id) { override TranslatedElement getChild(int id) {
id = 0 and result = getCondition() id = 0 and result = getInitialization()
or or
id = 1 and result = getThen() id = 1 and result = getCondition()
or or
id = 2 and result = getElse() id = 2 and result = getThen()
or
id = 3 and result = getElse()
}
private predicate hasInitialization() { exists(stmt.getInitialization()) }
private TranslatedStmt getInitialization() {
result = getTranslatedStmt(stmt.getInitialization())
} }
private TranslatedCondition getCondition() { private TranslatedCondition getCondition() {
result = getTranslatedCondition(stmt.getCondition().getFullyConverted()) result = getTranslatedCondition(stmt.getCondition().getFullyConverted())
} }
private Instruction getFirstConditionInstruction() {
result = getCondition().getFirstInstruction()
}
private TranslatedStmt getThen() { result = getTranslatedStmt(stmt.getThen()) } private TranslatedStmt getThen() { result = getTranslatedStmt(stmt.getThen()) }
private TranslatedStmt getElse() { result = getTranslatedStmt(stmt.getElse()) } private TranslatedStmt getElse() { result = getTranslatedStmt(stmt.getElse()) }
@@ -456,6 +472,9 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
} }
override Instruction getChildSuccessor(TranslatedElement child) { override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitialization() and
result = getFirstConditionInstruction()
or
(child = getThen() or child = getElse()) and (child = getThen() or child = getElse()) and
result = getParent().getChildSuccessor(this) result = getParent().getChildSuccessor(this)
} }
@@ -698,14 +717,28 @@ class TranslatedSwitchStmt extends TranslatedStmt {
result = getTranslatedExpr(stmt.getExpr().getFullyConverted()) result = getTranslatedExpr(stmt.getExpr().getFullyConverted())
} }
private Instruction getFirstExprInstruction() { result = getExpr().getFirstInstruction() }
private TranslatedStmt getBody() { result = getTranslatedStmt(stmt.getStmt()) } private TranslatedStmt getBody() { result = getTranslatedStmt(stmt.getStmt()) }
override Instruction getFirstInstruction() { result = getExpr().getFirstInstruction() } override Instruction getFirstInstruction() {
if hasInitialization()
then result = getInitialization().getFirstInstruction()
else result = getFirstExprInstruction()
}
override TranslatedElement getChild(int id) { override TranslatedElement getChild(int id) {
id = 0 and result = getExpr() id = 0 and result = getInitialization()
or or
id = 1 and result = getBody() id = 1 and result = getExpr()
or
id = 2 and result = getBody()
}
private predicate hasInitialization() { exists(stmt.getInitialization()) }
private TranslatedStmt getInitialization() {
result = getTranslatedStmt(stmt.getInitialization())
} }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
@@ -735,6 +768,8 @@ class TranslatedSwitchStmt extends TranslatedStmt {
} }
override Instruction getChildSuccessor(TranslatedElement child) { override Instruction getChildSuccessor(TranslatedElement child) {
child = getInitialization() and result = getFirstExprInstruction()
or
child = getExpr() and result = getInstruction(SwitchBranchTag()) child = getExpr() and result = getInstruction(SwitchBranchTag())
or or
child = getBody() and result = getParent().getChildSuccessor(this) child = getBody() and result = getParent().getChildSuccessor(this)

View File

@@ -34,7 +34,7 @@ class ValueNumber extends TValueNumber {
final Instruction getAnInstruction() { this = valueNumber(result) } final Instruction getAnInstruction() { this = valueNumber(result) }
/** /**
* Gets one of the instructions that was assigned this value number. The chosen instuction is * Gets one of the instructions that was assigned this value number. The chosen instruction is
* deterministic but arbitrary. Intended for use only in debugging. * deterministic but arbitrary. Intended for use only in debugging.
*/ */
final Instruction getExampleInstruction() { final Instruction getExampleInstruction() {

View File

@@ -1005,7 +1005,7 @@ predicate canReuseSsaForMemoryResult(Instruction instruction) {
deprecated predicate canReuseSSAForMemoryResult = canReuseSsaForMemoryResult/1; deprecated predicate canReuseSSAForMemoryResult = canReuseSsaForMemoryResult/1;
/** /**
* Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the * Expose some of the internal predicates to PrintSSA.qll. We do this by publicly importing those modules in the
* `DebugSSA` module, which is then imported by PrintSSA. * `DebugSSA` module, which is then imported by PrintSSA.
*/ */
module DebugSsa { module DebugSsa {

View File

@@ -155,7 +155,7 @@ class StrCopyBW extends BufferWriteCall {
// when result exists, it is an exact flow analysis // when result exists, it is an exact flow analysis
reason instanceof ValueFlowAnalysis and reason instanceof ValueFlowAnalysis and
result = result =
this.getArgument(this.getParamSrc()).(AnalysedString).getMaxLength() * this.getCharSize() this.getArgument(this.getParamSrc()).(AnalyzedString).getMaxLength() * this.getCharSize()
} }
override int getMaxData(BufferWriteEstimationReason reason) { override int getMaxData(BufferWriteEstimationReason reason) {
@@ -201,7 +201,7 @@ class StrCatBW extends BufferWriteCall {
// when result exists, it is an exact flow analysis // when result exists, it is an exact flow analysis
reason instanceof ValueFlowAnalysis and reason instanceof ValueFlowAnalysis and
result = result =
this.getArgument(this.getParamSrc()).(AnalysedString).getMaxLength() * this.getCharSize() this.getArgument(this.getParamSrc()).(AnalyzedString).getMaxLength() * this.getCharSize()
} }
override int getMaxData(BufferWriteEstimationReason reason) { override int getMaxData(BufferWriteEstimationReason reason) {

View File

@@ -59,7 +59,7 @@ predicate isInsecureEncryption(string name) { name.regexpMatch(getInsecureAlgori
/** /**
* Holds if there is additional evidence that `name` looks like it might be * Holds if there is additional evidence that `name` looks like it might be
* related to operations with an encyption algorithm, besides the name of a * related to operations with an encyption algorithm, besides the name of a
* specific algorithm. This can be used in conjuction with * specific algorithm. This can be used in conjunction with
* `isInsecureEncryption` to produce a stronger heuristic. * `isInsecureEncryption` to produce a stronger heuristic.
*/ */
bindingset[name] bindingset[name]

View File

@@ -5,8 +5,6 @@
import cpp import cpp
import semmle.code.cpp.controlflow.Dominance import semmle.code.cpp.controlflow.Dominance
// `GlobalValueNumbering` is only imported to prevent IR re-evaluation.
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
import semmle.code.cpp.controlflow.Guards import semmle.code.cpp.controlflow.Guards

View File

@@ -213,6 +213,26 @@ class ConditionalStmt extends ControlStructure, TConditionalStmt { }
class IfStmt extends ConditionalStmt, @stmt_if { class IfStmt extends ConditionalStmt, @stmt_if {
override string getAPrimaryQlClass() { result = "IfStmt" } override string getAPrimaryQlClass() { result = "IfStmt" }
/**
* Gets the initialization statement of this 'if' statement, if any.
*
* For example, for
* ```
* if (int x = y; b) { f(); }
* ```
* the result is `int x = y;`.
*
* Does not hold if the initialization statement is missing or an empty statement, as in
* ```
* if (b) { f(); }
* ```
* or
* ```
* if (; b) { f(); }
* ```
*/
Stmt getInitialization() { if_initialization(underlyingElement(this), unresolveElement(result)) }
/** /**
* Gets the condition expression of this 'if' statement. * Gets the condition expression of this 'if' statement.
* *
@@ -222,7 +242,7 @@ class IfStmt extends ConditionalStmt, @stmt_if {
* ``` * ```
* the result is `b`. * the result is `b`.
*/ */
Expr getCondition() { result = this.getChild(0) } Expr getCondition() { result = this.getChild(1) }
override Expr getControllingExpr() { result = this.getCondition() } override Expr getControllingExpr() { result = this.getCondition() }
@@ -299,6 +319,28 @@ class IfStmt extends ConditionalStmt, @stmt_if {
class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if { class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if {
override string getAPrimaryQlClass() { result = "ConstexprIfStmt" } override string getAPrimaryQlClass() { result = "ConstexprIfStmt" }
/**
* Gets the initialization statement of this 'constexpr if' statement, if any.
*
* For example, for
* ```
* if constexpr (int x = y; b) { f(); }
* ```
* the result is `int x = y;`.
*
* Does not hold if the initialization statement is missing or an empty statement, as in
* ```
* if constexpr (b) { f(); }
* ```
* or
* ```
* if constexpr (; b) { f(); }
* ```
*/
Stmt getInitialization() {
constexpr_if_initialization(underlyingElement(this), unresolveElement(result))
}
/** /**
* Gets the condition expression of this 'constexpr if' statement. * Gets the condition expression of this 'constexpr if' statement.
* *
@@ -308,7 +350,7 @@ class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if {
* ``` * ```
* the result is `b`. * the result is `b`.
*/ */
Expr getCondition() { result = this.getChild(0) } Expr getCondition() { result = this.getChild(1) }
override Expr getControllingExpr() { result = this.getCondition() } override Expr getControllingExpr() { result = this.getCondition() }
@@ -926,7 +968,7 @@ class ForStmt extends Loop, @stmt_for {
* *
* Does not hold if the initialization statement is an empty statement, as in * Does not hold if the initialization statement is an empty statement, as in
* ``` * ```
* for (; i < 10; i++) { j++ } * for (; i < 10; i++) { j++; }
* ``` * ```
*/ */
Stmt getInitialization() { for_initialization(underlyingElement(this), unresolveElement(result)) } Stmt getInitialization() { for_initialization(underlyingElement(this), unresolveElement(result)) }
@@ -1470,6 +1512,28 @@ class DefaultCase extends SwitchCase {
class SwitchStmt extends ConditionalStmt, @stmt_switch { class SwitchStmt extends ConditionalStmt, @stmt_switch {
override string getAPrimaryQlClass() { result = "SwitchStmt" } override string getAPrimaryQlClass() { result = "SwitchStmt" }
/**
* Gets the initialization statement of this 'switch' statement, if any.
*
* For example, for
* ```
* switch (int x = y; b) { }
* ```
* the result is `int x = y;`.
*
* Does not hold if the initialization statement is missing or an empty statement, as in
* ```
* switch (b) { }
* ```
* or
* ```
* switch (; b) { }
* ```
*/
Stmt getInitialization() {
switch_initialization(underlyingElement(this), unresolveElement(result))
}
/** /**
* Gets the expression that this 'switch' statement switches on. * Gets the expression that this 'switch' statement switches on.
* *
@@ -1485,7 +1549,7 @@ class SwitchStmt extends ConditionalStmt, @stmt_switch {
* ``` * ```
* the result is `i`. * the result is `i`.
*/ */
Expr getExpr() { result = this.getChild(0) } Expr getExpr() { result = this.getChild(1) }
override Expr getControllingExpr() { result = this.getExpr() } override Expr getControllingExpr() { result = this.getExpr() }

View File

@@ -1863,6 +1863,11 @@ variable_vla(
int decl: @stmt_vla_decl ref int decl: @stmt_vla_decl ref
); );
if_initialization(
unique int if_stmt: @stmt_if ref,
int init_id: @stmt ref
);
if_then( if_then(
unique int if_stmt: @stmt_if ref, unique int if_stmt: @stmt_if ref,
int then_id: @stmt ref int then_id: @stmt ref
@@ -1873,6 +1878,11 @@ if_else(
int else_id: @stmt ref int else_id: @stmt ref
); );
constexpr_if_initialization(
unique int constexpr_if_stmt: @stmt_constexpr_if ref,
int init_id: @stmt ref
);
constexpr_if_then( constexpr_if_then(
unique int constexpr_if_stmt: @stmt_constexpr_if ref, unique int constexpr_if_stmt: @stmt_constexpr_if ref,
int then_id: @stmt ref int then_id: @stmt ref
@@ -1893,6 +1903,11 @@ do_body(
int body_id: @stmt ref int body_id: @stmt ref
); );
switch_initialization(
unique int switch_stmt: @stmt_switch ref,
int init_id: @stmt ref
);
#keyset[switch_stmt, index] #keyset[switch_stmt, index]
switch_case( switch_case(
int switch_stmt: @stmt_switch ref, int switch_stmt: @stmt_switch ref,

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,21 @@
class Element extends @element {
string toString() { none() }
}
class Expr extends @expr {
string toString() { none() }
}
class Stmt extends @stmt {
string toString() { none() }
}
predicate isStmtWithInitializer(Stmt stmt) {
exists(int kind | stmts(stmt, kind, _) | kind = 2 or kind = 11 or kind = 35)
}
from Expr child, int index, int index_new, Element parent
where
exprparents(child, index, parent) and
if isStmtWithInitializer(parent) then index_new = index + 1 else index_new = index
select child, index_new, parent

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,17 @@
class Element extends @element {
string toString() { none() }
}
class Stmt extends @stmt {
string toString() { none() }
}
predicate isStmtWithInitializer(Stmt stmt) {
exists(int kind | stmts(stmt, kind, _) | kind = 2 or kind = 11 or kind = 35)
}
from Stmt child, int index, int index_new, Element parent
where
stmtparents(child, index, parent) and
if isStmtWithInitializer(parent) then index_new = index + 1 else index_new = index
select child, index_new, parent

View File

@@ -0,0 +1,4 @@
description: Support C++17 if and switch initializers
compatibility: partial
exprparents.rel: run exprparents.qlo
stmtparents.rel: run stmtparents.qlo

View File

@@ -57,6 +57,5 @@ where
not declarationHasSideEffects(v) and not declarationHasSideEffects(v) and
not exists(AsmStmt s | f = s.getEnclosingFunction()) and not exists(AsmStmt s | f = s.getEnclosingFunction()) and
not v.getAnAttribute().getName() = "unused" and not v.getAnAttribute().getName() = "unused" and
not any(ErrorExpr e).getEnclosingFunction() = f and // unextracted expr may use `v` not any(ErrorExpr e).getEnclosingFunction() = f // unextracted expr may use `v`
not any(ConditionDeclExpr cde).getEnclosingFunction() = f // this case can be removed when the `if (a = b; a)` and `switch (a = b; a)` test cases don't depend on this exclusion
select v, "Variable " + v.getName() + " is not used" select v, "Variable " + v.getName() + " is not used"

View File

@@ -1,3 +1,15 @@
## 0.1.2
### Minor Analysis Improvements
* The "XML external entity expansion" (`cpp/external-entity-expansion`) query has been extended to support a broader selection of XML libraries and interfaces.
## 0.1.1
### New Queries
* An new query `cpp/external-entity-expansion` has been added. The query detects XML objects that are vulnerable to external entity expansion (XXE) attacks.
## 0.1.0 ## 0.1.0
### Minor Analysis Improvements ### Minor Analysis Improvements

View File

@@ -57,10 +57,10 @@ class ExtractionProblem extends TExtractionProblem {
/** Gets the problem message for this problem. */ /** Gets the problem message for this problem. */
string getProblemMessage() { none() } string getProblemMessage() { none() }
/** Gets the file this problem occured in. */ /** Gets the file this problem occurred in. */
File getFile() { none() } File getFile() { none() }
/** Gets the location this problem occured in. */ /** Gets the location this problem occurred in. */
Location getLocation() { none() } Location getLocation() { none() }
/** Gets the SARIF severity of this problem. */ /** Gets the SARIF severity of this problem. */

View File

@@ -57,10 +57,10 @@ class ExtractionError extends TExtractionError {
/** Gets the error message for this error. */ /** Gets the error message for this error. */
string getErrorMessage() { none() } string getErrorMessage() { none() }
/** Gets the file this error occured in. */ /** Gets the file this error occurred in. */
File getFile() { none() } File getFile() { none() }
/** Gets the location this error occured in. */ /** Gets the location this error occurred in. */
Location getLocation() { none() } Location getLocation() { none() }
/** Gets the SARIF severity of this error. */ /** Gets the SARIF severity of this error. */

View File

@@ -19,7 +19,7 @@ predicate whitelist(Function f) {
"nearbyintl", "rint", "rintf", "rintl", "round", "roundf", "roundl", "trunc", "truncf", "nearbyintl", "rint", "rintf", "rintl", "round", "roundf", "roundl", "trunc", "truncf",
"truncl" "truncl"
] or ] or
f.getName().matches("__builtin_%") f.getName().matches("\\_\\_builtin\\_%")
} }
predicate whitelistPow(FunctionCall fc) { predicate whitelistPow(FunctionCall fc) {

View File

@@ -14,9 +14,6 @@
*/ */
import cpp import cpp
// We don't actually use the global value numbering library in this query, but without it we end up
// recomputing the IR.
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
import semmle.code.cpp.ir.IR import semmle.code.cpp.ir.IR
import semmle.code.cpp.ir.dataflow.MustFlow import semmle.code.cpp.ir.dataflow.MustFlow
import PathGraph import PathGraph

View File

@@ -2,7 +2,7 @@
* @name Suspicious call to memset * @name Suspicious call to memset
* @description Use of memset where the size argument is computed as the size of * @description Use of memset where the size argument is computed as the size of
* some non-struct type. When initializing a buffer, you should specify * some non-struct type. When initializing a buffer, you should specify
* its size as <number of elements> * <size of one element> to ensure * its size as `<number of elements> * <size of one element>` to ensure
* portability. * portability.
* @kind problem * @kind problem
* @id cpp/suspicious-call-to-memset * @id cpp/suspicious-call-to-memset

View File

@@ -15,9 +15,6 @@
*/ */
import cpp import cpp
// We don't actually use the global value numbering library in this query, but without it we end up
// recomputing the IR.
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
import semmle.code.cpp.ir.IR import semmle.code.cpp.ir.IR
import semmle.code.cpp.ir.dataflow.MustFlow import semmle.code.cpp.ir.dataflow.MustFlow
import PathGraph import PathGraph

View File

@@ -0,0 +1,78 @@
/**
* Models the libxml2 XML library.
*/
import cpp
import XML
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
/**
* A call to a `libxml2` function that parses XML.
*/
class Libxml2ParseCall extends FunctionCall {
int optionsArg;
Libxml2ParseCall() {
exists(string fname | this.getTarget().getName() = fname |
fname = "xmlCtxtUseOptions" and optionsArg = 1
or
fname = "xmlReadFile" and optionsArg = 2
or
fname = ["xmlCtxtReadFile", "xmlParseInNodeContext", "xmlReadDoc", "xmlReadFd"] and
optionsArg = 3
or
fname = ["xmlCtxtReadDoc", "xmlCtxtReadFd", "xmlReadMemory"] and optionsArg = 4
or
fname = ["xmlCtxtReadMemory", "xmlReadIO"] and optionsArg = 5
or
fname = "xmlCtxtReadIO" and optionsArg = 6
)
}
/**
* Gets the argument that specifies `xmlParserOption`s.
*/
Expr getOptions() { result = this.getArgument(optionsArg) }
}
/**
* An `xmlParserOption` for `libxml2` that is considered unsafe.
*/
class Libxml2BadOption extends EnumConstant {
Libxml2BadOption() { this.getName() = ["XML_PARSE_NOENT", "XML_PARSE_DTDLOAD"] }
}
/**
* The libxml2 XML library.
*/
class LibXml2Library extends XmlLibrary {
LibXml2Library() { this = "LibXml2Library" }
override predicate configurationSource(DataFlow::Node node, string flowstate) {
// source is an `options` argument on a libxml2 parse call that specifies
// at least one unsafe option.
//
// note: we don't need to track an XML object for libxml2, so we don't
// really need data flow. Nevertheless we jam it into this configuration,
// with matching sources and sinks. This allows results to be presented by
// the same query, in a consistent way as other results with flow paths.
exists(Libxml2ParseCall call, Expr options |
options = call.getOptions() and
node.asExpr() = options and
flowstate = "libxml2" and
exists(Libxml2BadOption opt |
globalValueNumber(options).getAnExpr().getValue().toInt().bitAnd(opt.getValue().toInt()) !=
0
)
)
}
override predicate configurationSink(DataFlow::Node node, string flowstate) {
// sink is the `options` argument on a `libxml2` parse call.
exists(Libxml2ParseCall call, Expr options |
options = call.getOptions() and
node.asExpr() = options and
flowstate = "libxml2"
)
}
}

View File

@@ -0,0 +1,55 @@
/**
* Provides a abstract classes for modeling XML libraries. This design is
* currently specialized for the purposes of the XXE query.
*/
import cpp
import semmle.code.cpp.ir.dataflow.DataFlow
import Xerces
import Libxml2
/**
* A flow state representing a possible configuration of an XML object.
*/
abstract class XxeFlowState extends DataFlow::FlowState {
bindingset[this]
XxeFlowState() { any() } // required characteristic predicate
}
/**
* An XML library or interface.
*/
abstract class XmlLibrary extends string {
bindingset[this]
XmlLibrary() { any() } // required characteristic predicate
/**
* Holds if `node` is the source node for a potentially unsafe configuration
* object for this XML library, along with `flowstate` representing its
* initial state.
*/
abstract predicate configurationSource(DataFlow::Node node, string flowstate);
/**
* Holds if `node` is the sink node where an unsafe configuration object is
* used to interpret XML.
*/
abstract predicate configurationSink(DataFlow::Node node, string flowstate);
}
/**
* An `Expr` that changes the configuration of an XML object, transforming the
* `XxeFlowState` that flows through it.
*/
abstract class XxeFlowStateTransformer extends Expr {
/**
* Gets the flow state that `flowstate` is transformed into.
*
* Due to limitations of the implementation the transformation defined by this
* predicate must be idempotent, that is, for any input `x` it must be that:
* ```
* transform(transform(x)) = transform(x)
* ```
*/
abstract XxeFlowState transform(XxeFlowState flowstate);
}

View File

@@ -7,169 +7,14 @@
* @id cpp/external-entity-expansion * @id cpp/external-entity-expansion
* @problem.severity warning * @problem.severity warning
* @security-severity 9.1 * @security-severity 9.1
* @precision medium * @precision high
* @tags security * @tags security
* external/cwe/cwe-611 * external/cwe/cwe-611
*/ */
import cpp import cpp
import semmle.code.cpp.ir.dataflow.DataFlow import XML
import DataFlow::PathGraph import DataFlow::PathGraph
import semmle.code.cpp.ir.IR
/**
* A flow state representing a possible configuration of an XML object.
*/
abstract class XXEFlowState extends DataFlow::FlowState {
bindingset[this]
XXEFlowState() { any() } // required characteristic predicate
}
/**
* An `Expr` that changes the configuration of an XML object, transforming the
* `XXEFlowState` that flows through it.
*/
abstract class XXEFlowStateTranformer extends Expr {
/**
* Gets the flow state that `flowstate` is transformed into.
*
* Due to limitations of the implementation the transformation defined by this
* predicate must be idempotent, that is, for any input `x` it must be that:
* ```
* transform(tranform(x)) = tranform(x)
* ```
*/
abstract XXEFlowState transform(XXEFlowState flowstate);
}
/**
* The `AbstractDOMParser` class.
*/
class AbstractDOMParserClass extends Class {
AbstractDOMParserClass() { this.hasName("AbstractDOMParser") }
}
/**
* The `XercesDOMParser` class.
*/
class XercesDOMParserClass extends Class {
XercesDOMParserClass() { this.hasName("XercesDOMParser") }
}
/**
* Gets a valid flow state for `XercesDOMParser` flow.
*
* These flow states take the form `XercesDOM-A-B`, where:
* - A is 1 if `setDisableDefaultEntityResolution` is `true`, 0 otherwise.
* - B is 1 if `setCreateEntityReferenceNodes` is `true`, 0 otherwise.
*/
predicate encodeXercesDOMFlowState(
string flowstate, int disabledDefaultEntityResolution, int createEntityReferenceNodes
) {
flowstate = "XercesDOM-0-0" and
disabledDefaultEntityResolution = 0 and
createEntityReferenceNodes = 0
or
flowstate = "XercesDOM-0-1" and
disabledDefaultEntityResolution = 0 and
createEntityReferenceNodes = 1
or
flowstate = "XercesDOM-1-0" and
disabledDefaultEntityResolution = 1 and
createEntityReferenceNodes = 0
or
flowstate = "XercesDOM-1-1" and
disabledDefaultEntityResolution = 1 and
createEntityReferenceNodes = 1
}
/**
* A flow state representing the configuration of a `XercesDOMParser` object.
*/
class XercesDOMParserFlowState extends XXEFlowState {
XercesDOMParserFlowState() { encodeXercesDOMFlowState(this, _, _) }
}
/**
* A flow state transformer for a call to
* `AbstractDOMParser.setDisableDefaultEntityResolution`. Transforms the flow
* state through the qualifier according to the setting in the parameter.
*/
class DisableDefaultEntityResolutionTranformer extends XXEFlowStateTranformer {
Expr newValue;
DisableDefaultEntityResolutionTranformer() {
exists(Call call, Function f |
call.getTarget() = f and
f.getDeclaringType() instanceof AbstractDOMParserClass and
f.hasName("setDisableDefaultEntityResolution") and
this = call.getQualifier() and
newValue = call.getArgument(0)
)
}
final override XXEFlowState transform(XXEFlowState flowstate) {
exists(int createEntityReferenceNodes |
encodeXercesDOMFlowState(flowstate, _, createEntityReferenceNodes) and
(
newValue.getValue().toInt() = 1 and // true
encodeXercesDOMFlowState(result, 1, createEntityReferenceNodes)
or
not newValue.getValue().toInt() = 1 and // false or unknown
encodeXercesDOMFlowState(result, 0, createEntityReferenceNodes)
)
)
}
}
/**
* A flow state transformer for a call to
* `AbstractDOMParser.setCreateEntityReferenceNodes`. Transforms the flow
* state through the qualifier according to the setting in the parameter.
*/
class CreateEntityReferenceNodesTranformer extends XXEFlowStateTranformer {
Expr newValue;
CreateEntityReferenceNodesTranformer() {
exists(Call call, Function f |
call.getTarget() = f and
f.getDeclaringType() instanceof AbstractDOMParserClass and
f.hasName("setCreateEntityReferenceNodes") and
this = call.getQualifier() and
newValue = call.getArgument(0)
)
}
final override XXEFlowState transform(XXEFlowState flowstate) {
exists(int disabledDefaultEntityResolution |
encodeXercesDOMFlowState(flowstate, disabledDefaultEntityResolution, _) and
(
newValue.getValue().toInt() = 1 and // true
encodeXercesDOMFlowState(result, disabledDefaultEntityResolution, 1)
or
not newValue.getValue().toInt() = 1 and // false or unknown
encodeXercesDOMFlowState(result, disabledDefaultEntityResolution, 0)
)
)
}
}
/**
* The `AbstractDOMParser.parse` method.
*/
class ParseFunction extends Function {
ParseFunction() { this.getClassAndName("parse") instanceof AbstractDOMParserClass }
}
/**
* The `createLSParser` function that returns a newly created `LSParser` object.
*/
class CreateLSParser extends Function {
CreateLSParser() {
this.hasName("createLSParser") and
this.getUnspecifiedType().(PointerType).getBaseType().getName() = "DOMLSParser" // returns a `DOMLSParser *`.
}
}
/** /**
* A configuration for tracking XML objects and their states. * A configuration for tracking XML objects and their states.
@@ -178,45 +23,25 @@ class XXEConfiguration extends DataFlow::Configuration {
XXEConfiguration() { this = "XXEConfiguration" } XXEConfiguration() { this = "XXEConfiguration" }
override predicate isSource(DataFlow::Node node, string flowstate) { override predicate isSource(DataFlow::Node node, string flowstate) {
// source is the write on `this` of a call to the `XercesDOMParser` any(XmlLibrary l).configurationSource(node, flowstate)
// constructor.
exists(CallInstruction call |
call.getStaticCallTarget() = any(XercesDOMParserClass c).getAConstructor() and
node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
call.getThisArgument() and
encodeXercesDOMFlowState(flowstate, 0, 1) // default configuration
)
or
// source is the result of a call to `createLSParser`.
exists(Call call |
call.getTarget() instanceof CreateLSParser and
call = node.asExpr() and
encodeXercesDOMFlowState(flowstate, 0, 1) // default configuration
)
} }
override predicate isSink(DataFlow::Node node, string flowstate) { override predicate isSink(DataFlow::Node node, string flowstate) {
// sink is the read of the qualifier of a call to `parse`. any(XmlLibrary l).configurationSink(node, flowstate)
exists(Call call |
call.getTarget() instanceof ParseFunction and
call.getQualifier() = node.asConvertedExpr()
) and
flowstate instanceof XercesDOMParserFlowState and
not encodeXercesDOMFlowState(flowstate, 1, 1) // safe configuration
} }
override predicate isAdditionalFlowStep( override predicate isAdditionalFlowStep(
DataFlow::Node node1, string state1, DataFlow::Node node2, string state2 DataFlow::Node node1, string state1, DataFlow::Node node2, string state2
) { ) {
// create additional flow steps for `XXEFlowStateTranformer`s // create additional flow steps for `XxeFlowStateTransformer`s
state2 = node2.asConvertedExpr().(XXEFlowStateTranformer).transform(state1) and state2 = node2.asConvertedExpr().(XxeFlowStateTransformer).transform(state1) and
DataFlow::simpleLocalFlowStep(node1, node2) DataFlow::simpleLocalFlowStep(node1, node2)
} }
override predicate isBarrier(DataFlow::Node node, string flowstate) { override predicate isBarrier(DataFlow::Node node, string flowstate) {
// when the flowstate is transformed at a call node, block the original // when the flowstate is transformed at a call node, block the original
// flowstate value. // flowstate value.
node.asConvertedExpr().(XXEFlowStateTranformer).transform(flowstate) != flowstate node.asConvertedExpr().(XxeFlowStateTransformer).transform(flowstate) != flowstate
} }
} }

View File

@@ -0,0 +1,376 @@
/**
* Models the Xerces XML library.
*/
import cpp
import XML
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
import semmle.code.cpp.ir.IR
/**
* Gets a valid flow state for `AbstractDOMParser` or `SAXParser` flow.
*
* These flow states take the form `Xerces-A-B`, where:
* - A is 1 if `setDisableDefaultEntityResolution` is `true`, 0 otherwise.
* - B is 1 if `setCreateEntityReferenceNodes` is `true`, 0 otherwise.
*/
predicate encodeXercesFlowState(
string flowstate, int disabledDefaultEntityResolution, int createEntityReferenceNodes
) {
flowstate = "Xerces-0-0" and
disabledDefaultEntityResolution = 0 and
createEntityReferenceNodes = 0
or
flowstate = "Xerces-0-1" and
disabledDefaultEntityResolution = 0 and
createEntityReferenceNodes = 1
or
flowstate = "Xerces-1-0" and
disabledDefaultEntityResolution = 1 and
createEntityReferenceNodes = 0
or
flowstate = "Xerces-1-1" and
disabledDefaultEntityResolution = 1 and
createEntityReferenceNodes = 1
}
/**
* A flow state representing the configuration of an `AbstractDOMParser` or
* `SAXParser` object.
*/
class XercesFlowState extends XxeFlowState {
XercesFlowState() { encodeXercesFlowState(this, _, _) }
}
/**
* The `AbstractDOMParser` class.
*/
class AbstractDomParserClass extends Class {
AbstractDomParserClass() { this.hasName("AbstractDOMParser") }
}
/**
* The `XercesDOMParser` class.
*/
class XercesDomParserClass extends Class {
XercesDomParserClass() { this.hasName("XercesDOMParser") }
}
/**
* The `XercesDOMParser` interface for the Xerces XML library.
*/
class XercesDomParserLibrary extends XmlLibrary {
XercesDomParserLibrary() { this = "XercesDomParserLibrary" }
override predicate configurationSource(DataFlow::Node node, string flowstate) {
// source is the write on `this` of a call to the `XercesDOMParser`
// constructor.
exists(CallInstruction call |
call.getStaticCallTarget() = any(XercesDomParserClass c).getAConstructor() and
node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
call.getThisArgument() and
encodeXercesFlowState(flowstate, 0, 1) // default configuration
)
}
override predicate configurationSink(DataFlow::Node node, string flowstate) {
// sink is the read of the qualifier of a call to `AbstractDOMParser.parse`.
exists(Call call |
call.getTarget().getClassAndName("parse") instanceof AbstractDomParserClass and
call.getQualifier() = node.asConvertedExpr()
) and
flowstate instanceof XercesFlowState and
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
}
}
/**
* The `DOMLSParser` class.
*/
class DomLSParserClass extends Class {
DomLSParserClass() { this.hasName("DOMLSParser") }
}
/**
* The `createLSParser` function that returns a newly created `DOMLSParser`
* object.
*/
class CreateLSParser extends Function {
CreateLSParser() {
this.hasName("createLSParser") and
this.getUnspecifiedType().(PointerType).getBaseType() instanceof DomLSParserClass // returns a `DOMLSParser *`.
}
}
/**
* The createLSParser interface for the Xerces XML library.
*/
class CreateLSParserLibrary extends XmlLibrary {
CreateLSParserLibrary() { this = "CreateLSParserLibrary" }
override predicate configurationSource(DataFlow::Node node, string flowstate) {
// source is the result of a call to `createLSParser`.
exists(Call call |
call.getTarget() instanceof CreateLSParser and
call = node.asExpr() and
encodeXercesFlowState(flowstate, 0, 1) // default configuration
)
}
override predicate configurationSink(DataFlow::Node node, string flowstate) {
// sink is the read of the qualifier of a call to `DOMLSParserClass.parse`.
exists(Call call |
call.getTarget().getClassAndName("parse") instanceof DomLSParserClass and
call.getQualifier() = node.asConvertedExpr()
) and
flowstate instanceof XercesFlowState and
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
}
}
/**
* The `SAXParser` class.
*/
class SaxParserClass extends Class {
SaxParserClass() { this.hasName("SAXParser") }
}
/**
* The `SAX2XMLReader` class.
*/
class Sax2XmlReader extends Class {
Sax2XmlReader() { this.hasName("SAX2XMLReader") }
}
/**
* The SAXParser interface for the Xerces XML library.
*/
class SaxParserLibrary extends XmlLibrary {
SaxParserLibrary() { this = "SaxParserLibrary" }
override predicate configurationSource(DataFlow::Node node, string flowstate) {
// source is the write on `this` of a call to the `SAXParser`
// constructor.
exists(CallInstruction call |
call.getStaticCallTarget() = any(SaxParserClass c).getAConstructor() and
node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
call.getThisArgument() and
encodeXercesFlowState(flowstate, 0, 1) // default configuration
)
}
override predicate configurationSink(DataFlow::Node node, string flowstate) {
// sink is the read of the qualifier of a call to `SAXParser.parse`.
exists(Call call |
call.getTarget().getClassAndName("parse") instanceof SaxParserClass and
call.getQualifier() = node.asConvertedExpr()
) and
flowstate instanceof XercesFlowState and
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
}
}
/**
* The `createXMLReader` function that returns a newly created `SAX2XMLReader`
* object.
*/
class CreateXmlReader extends Function {
CreateXmlReader() {
this.hasName("createXMLReader") and
this.getUnspecifiedType().(PointerType).getBaseType() instanceof Sax2XmlReader // returns a `SAX2XMLReader *`.
}
}
/**
* The SAX2XMLReader interface for the Xerces XML library.
*/
class Sax2XmlReaderLibrary extends XmlLibrary {
Sax2XmlReaderLibrary() { this = "Sax2XmlReaderLibrary" }
override predicate configurationSource(DataFlow::Node node, string flowstate) {
// source is the result of a call to `createXMLReader`.
exists(Call call |
call.getTarget() instanceof CreateXmlReader and
call = node.asExpr() and
encodeXercesFlowState(flowstate, 0, 1) // default configuration
)
}
override predicate configurationSink(DataFlow::Node node, string flowstate) {
// sink is the read of the qualifier of a call to `SAX2XMLReader.parse`.
exists(Call call |
call.getTarget().getClassAndName("parse") instanceof Sax2XmlReader and
call.getQualifier() = node.asConvertedExpr()
) and
flowstate instanceof XercesFlowState and
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
}
}
/**
* A flow state transformer for a call to
* `AbstractDOMParser.setDisableDefaultEntityResolution` or
* `SAXParser.setDisableDefaultEntityResolution`. Transforms the flow
* state through the qualifier according to the setting in the parameter.
*/
class DisableDefaultEntityResolutionTransformer extends XxeFlowStateTransformer {
Expr newValue;
DisableDefaultEntityResolutionTransformer() {
exists(Call call, Function f |
call.getTarget() = f and
(
f.getDeclaringType() instanceof AbstractDomParserClass or
f.getDeclaringType() instanceof SaxParserClass
) and
f.hasName("setDisableDefaultEntityResolution") and
this = call.getQualifier() and
newValue = call.getArgument(0)
)
}
final override XxeFlowState transform(XxeFlowState flowstate) {
exists(int createEntityReferenceNodes |
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
(
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
or
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
)
)
}
}
/**
* A flow state transformer for a call to
* `AbstractDOMParser.setCreateEntityReferenceNodes`. Transforms the flow
* state through the qualifier according to the setting in the parameter.
*/
class CreateEntityReferenceNodesTransformer extends XxeFlowStateTransformer {
Expr newValue;
CreateEntityReferenceNodesTransformer() {
exists(Call call, Function f |
call.getTarget() = f and
f.getClassAndName("setCreateEntityReferenceNodes") instanceof AbstractDomParserClass and
this = call.getQualifier() and
newValue = call.getArgument(0)
)
}
final override XxeFlowState transform(XxeFlowState flowstate) {
exists(int disabledDefaultEntityResolution |
encodeXercesFlowState(flowstate, disabledDefaultEntityResolution, _) and
(
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
encodeXercesFlowState(result, disabledDefaultEntityResolution, 1)
or
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
encodeXercesFlowState(result, disabledDefaultEntityResolution, 0)
)
)
}
}
/**
* The `XMLUni.fgXercesDisableDefaultEntityResolution` constant.
*/
class FeatureDisableDefaultEntityResolution extends Variable {
FeatureDisableDefaultEntityResolution() {
this.getName() = "fgXercesDisableDefaultEntityResolution" and
this.getDeclaringType().getName() = "XMLUni"
}
}
/**
* A flow state transformer for a call to `SAX2XMLReader.setFeature`
* specifying the feature `XMLUni::fgXercesDisableDefaultEntityResolution`.
* Transforms the flow state through the qualifier according to this setting.
*/
class SetFeatureTransformer extends XxeFlowStateTransformer {
Expr newValue;
SetFeatureTransformer() {
exists(Call call, Function f |
call.getTarget() = f and
f.getClassAndName("setFeature") instanceof Sax2XmlReader and
this = call.getQualifier() and
globalValueNumber(call.getArgument(0)).getAnExpr().(VariableAccess).getTarget() instanceof
FeatureDisableDefaultEntityResolution and
newValue = call.getArgument(1)
)
}
final override XxeFlowState transform(XxeFlowState flowstate) {
exists(int createEntityReferenceNodes |
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
(
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
or
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
)
)
}
}
/**
* The `DOMLSParser.getDomConfig` function.
*/
class GetDomConfig extends Function {
GetDomConfig() { this.getClassAndName("getDomConfig") instanceof DomLSParserClass }
}
/**
* The `DOMConfiguration.setParameter` function.
*/
class DomConfigurationSetParameter extends Function {
DomConfigurationSetParameter() {
this.getClassAndName("setParameter").getName() = "DOMConfiguration"
}
}
/**
* A flow state transformer for a call to `DOMConfiguration.setParameter`
* specifying the feature `XMLUni::fgXercesDisableDefaultEntityResolution`.
* This is a slightly more complex transformer because the qualifier is a
* `DOMConfiguration` pointer returned by `DOMLSParser.getDomConfig` - and it
* is *that* qualifier we want to transform the flow state of.
*/
class DomConfigurationSetParameterTransformer extends XxeFlowStateTransformer {
Expr newValue;
DomConfigurationSetParameterTransformer() {
exists(FunctionCall getDomConfigCall, FunctionCall setParameterCall |
// this is the qualifier of a call to `DOMLSParser.getDomConfig`.
getDomConfigCall.getTarget() instanceof GetDomConfig and
this = getDomConfigCall.getQualifier() and
// `setParameterCall` is a call to `setParameter` on the return value of
// the same call to `DOMLSParser.getDomConfig`.
setParameterCall.getTarget() instanceof DomConfigurationSetParameter and
globalValueNumber(setParameterCall.getQualifier()).getAnExpr() = getDomConfigCall and
// the parameter being set is
// `XMLUni::fgXercesDisableDefaultEntityResolution`.
globalValueNumber(setParameterCall.getArgument(0)).getAnExpr().(VariableAccess).getTarget()
instanceof FeatureDisableDefaultEntityResolution and
// the value being set is `newValue`.
newValue = setParameterCall.getArgument(1)
)
}
final override XxeFlowState transform(XxeFlowState flowstate) {
exists(int createEntityReferenceNodes |
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
(
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
or
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
)
)
}
}

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The `cpp/unused-local-variable` no longer ignores functions that include `if` and `switch` statements with C++17-style initializers.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The "XML external entity expansion" (`cpp/external-entity-expansion`) query precision has been increased to `high`.

View File

@@ -1,4 +1,5 @@
--- ## 0.1.1
category: newQuery
--- ### New Queries
* An new query `cpp/external-entity-expansion` has been added. The query detects XML objects that are vulnerable to external entity expansion (XXE) attacks. * An new query `cpp/external-entity-expansion` has been added. The query detects XML objects that are vulnerable to external entity expansion (XXE) attacks.

View File

@@ -0,0 +1,5 @@
## 0.1.2
### Minor Analysis Improvements
* The "XML external entity expansion" (`cpp/external-entity-expansion`) query has been extended to support a broader selection of XML libraries and interfaces.

View File

@@ -1,2 +1,2 @@
--- ---
lastReleaseVersion: 0.1.0 lastReleaseVersion: 0.1.2

View File

@@ -58,7 +58,7 @@ where
// unfortunately cannot use numeric value here because // O_CREAT is defined differently on different OSes: // unfortunately cannot use numeric value here because // O_CREAT is defined differently on different OSes:
// https://github.com/red/red/blob/92feb0c0d5f91e087ab35fface6906afbf99b603/runtime/definitions.reds#L477-L491 // https://github.com/red/red/blob/92feb0c0d5f91e087ab35fface6906afbf99b603/runtime/definitions.reds#L477-L491
// this may introduce false negatives // this may introduce false negatives
fctmp.getArgument(1).(BitwiseOrExpr).getAChild*().getValueText().matches("O_CREAT") or fctmp.getArgument(1).(BitwiseOrExpr).getAChild*().getValueText() = "O_CREAT" or
fctmp.getArgument(1).getValueText().matches("%O_CREAT%") fctmp.getArgument(1).getValueText().matches("%O_CREAT%")
) and ) and
fctmp.getNumberOfArguments() = 2 and fctmp.getNumberOfArguments() = 2 and

View File

@@ -0,0 +1,48 @@
...
try {
if (checkValue) throw exception();
bufMyData = new myData*[sizeInt];
}
catch (...)
{
for (size_t i = 0; i < sizeInt; i++)
{
delete[] bufMyData[i]->buffer; // BAD
delete bufMyData[i];
}
...
try {
if (checkValue) throw exception();
bufMyData = new myData*[sizeInt];
}
catch (...)
{
for (size_t i = 0; i < sizeInt; i++)
{
if(bufMyData[i])
{
delete[] bufMyData[i]->buffer; // GOOD
delete bufMyData[i];
}
}
...
catch (const exception &) {
delete valData;
throw;
}
catch (...)
{
delete valData; // BAD
...
catch (const exception &) {
delete valData;
valData = NULL;
throw;
}
catch (...)
{
delete valData; // GOOD
...

View File

@@ -0,0 +1,23 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>When releasing memory in a catch block, be sure that the memory was allocated and has not already been released.</p>
</overview>
<example>
<p>The following example shows erroneous and fixed ways to use exception handling.</p>
<sample src="DangerousUseOfExceptionBlocks.cpp" />
</example>
<references>
<li>
CERT C Coding Standard:
<a href="https://wiki.sei.cmu.edu/confluence/display/c/EXP34-C.+Do+not+dereference+null+pointers">EXP34-C. Do not dereference null pointers</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,199 @@
/**
* @name Dangerous use of exception blocks.
* @description When clearing the data in the catch block, you must be sure that the memory was allocated before the exception.
* @kind problem
* @id cpp/dangerous-use-of-exception-blocks
* @problem.severity warning
* @precision medium
* @tags correctness
* security
* external/cwe/cwe-476
* external/cwe/cwe-415
*/
import cpp
/** Holds if `vr` may be released in the `try` block associated with `cb`, or in a `catch` block prior to `cb`. */
pragma[inline]
predicate doubleCallDelete(BlockStmt b, CatchAnyBlock cb, Variable vr) {
// Search for exceptions after freeing memory.
exists(Expr e1 |
// `e1` is a delete of `vr`
(
e1 = vr.getAnAccess().getEnclosingStmt().(ExprStmt).getExpr().(DeleteArrayExpr) or
e1 = vr.getAnAccess().getEnclosingStmt().(ExprStmt).getExpr().(DeleteExpr)
) and
e1.getEnclosingFunction() = cb.getEnclosingFunction() and
// there is no assignment `vr = 0` in the `try` block after `e1`
not exists(AssignExpr ae |
ae.getLValue().(VariableAccess).getTarget() = vr and
ae.getRValue().getValue() = "0" and
e1.getASuccessor+() = ae and
ae.getEnclosingStmt().getParentStmt*() = b
) and
// `e2` is a `throw` (or a function call that may throw) that occurs in the `try` or `catch` block after `e1`
exists(Expr e2, ThrowExpr th |
(
e2 = th or
e2 = th.getEnclosingFunction().getACallToThisFunction()
) and
e2.getEnclosingStmt().getParentStmt*() = b and
e1.getASuccessor+() = e2
) and
e1.getEnclosingStmt().getParentStmt*() = b and
(
// Search for a situation where there is a release in the block of `try`.
b = cb.getTryStmt().getStmt()
or
// Search for a situation when there is a higher catch block that also frees memory.
exists(b.(CatchBlock).getParameter())
) and
// Exclude the presence of a check in catch block.
not exists(IfStmt ifst | ifst.getEnclosingStmt().getParentStmt*() = cb.getAStmt())
)
}
/** Holds if an exception can be thrown before the memory is allocated, and when the exception is handled, an attempt is made to access unallocated memory in the catch block. */
pragma[inline]
predicate pointerDereference(CatchAnyBlock cb, Variable vr, Variable vro) {
// Search exceptions before allocating memory.
exists(Expr e0, Expr e1 |
(
// `e0` is a `new` expression (or equivalent function call) assigned to `vro`
exists(AssignExpr ase |
ase = vro.getAnAccess().getEnclosingStmt().(ExprStmt).getExpr() and
(
e0 = ase.getRValue().(NewOrNewArrayExpr) or
e0 = ase.getRValue().(NewOrNewArrayExpr).getEnclosingFunction().getACallToThisFunction()
) and
vro = ase.getLValue().(VariableAccess).getTarget()
)
or
// `e0` is a `new` expression (or equivalent function call) assigned to the array element `vro`
exists(AssignExpr ase |
ase = vro.getAnAccess().(Qualifier).getEnclosingStmt().(ExprStmt).getExpr() and
(
e0 = ase.getRValue().(NewOrNewArrayExpr) or
e0 = ase.getRValue().(NewOrNewArrayExpr).getEnclosingFunction().getACallToThisFunction()
) and
not ase.getLValue() instanceof VariableAccess and
vro = ase.getLValue().getAPredecessor().(VariableAccess).getTarget()
)
) and
// `e1` is a `new` expression (or equivalent function call) assigned to `vr`
exists(AssignExpr ase |
ase = vr.getAnAccess().getEnclosingStmt().(ExprStmt).getExpr() and
(
e1 = ase.getRValue().(NewOrNewArrayExpr) or
e1 = ase.getRValue().(NewOrNewArrayExpr).getEnclosingFunction().getACallToThisFunction()
) and
vr = ase.getLValue().(VariableAccess).getTarget()
) and
e0.getASuccessor*() = e1 and
e0.getEnclosingStmt().getParentStmt*() = cb.getTryStmt().getStmt() and
e1.getEnclosingStmt().getParentStmt*() = cb.getTryStmt().getStmt() and
// `e2` is a `throw` (or a function call that may throw) that occurs in the `try` block before `e0`
exists(Expr e2, ThrowExpr th |
(
e2 = th or
e2 = th.getEnclosingFunction().getACallToThisFunction()
) and
e2.getEnclosingStmt().getParentStmt*() = cb.getTryStmt().getStmt() and
e2.getASuccessor+() = e0
)
) and
// We exclude checking the value of a variable or its parent in the catch block.
not exists(IfStmt ifst |
ifst.getEnclosingStmt().getParentStmt*() = cb.getAStmt() and
(
ifst.getCondition().getAChild*().(VariableAccess).getTarget() = vr or
ifst.getCondition().getAChild*().(VariableAccess).getTarget() = vro
)
)
}
/** Holds if `vro` may be released in the `catch`. */
pragma[inline]
predicate newThrowDelete(CatchAnyBlock cb, Variable vro) {
exists(Expr e0, AssignExpr ase, NewOrNewArrayExpr nae |
ase = vro.getAnAccess().getEnclosingStmt().(ExprStmt).getExpr() and
nae = ase.getRValue() and
not nae.getAChild*().toString() = "nothrow" and
(
e0 = nae or
e0 = nae.getEnclosingFunction().getACallToThisFunction()
) and
vro = ase.getLValue().(VariableAccess).getTarget() and
e0.getEnclosingStmt().getParentStmt*() = cb.getTryStmt().getStmt() and
not exists(AssignExpr ase1 |
vro = ase1.getLValue().(VariableAccess).getTarget() and
ase1.getRValue().getValue() = "0" and
ase1.getASuccessor*() = e0
)
) and
not exists(Initializer it |
vro.getInitializer() = it and
it.getExpr().getValue() = "0"
) and
not exists(ConstructorFieldInit ci | vro = ci.getTarget())
}
from CatchAnyBlock cb, string msg
where
exists(Variable vr, Variable vro, Expr exp |
exp.getEnclosingStmt().getParentStmt*() = cb and
exists(VariableAccess va |
(
(
va = exp.(DeleteArrayExpr).getExpr().getAPredecessor+().(Qualifier) or
va = exp.(DeleteArrayExpr).getExpr().getAPredecessor+()
) and
vr = exp.(DeleteArrayExpr).getExpr().(VariableAccess).getTarget()
or
(
va = exp.(DeleteExpr).getExpr().getAPredecessor+().(Qualifier) or
va = exp.(DeleteExpr).getExpr().getAPredecessor+()
) and
vr = exp.(DeleteExpr).getExpr().(VariableAccess).getTarget()
) and
va.getEnclosingStmt() = exp.getEnclosingStmt() and
vro = va.getTarget() and
vr != vro
) and
pointerDereference(cb, vr, vro) and
msg =
"it is possible to dereference a pointer when accessing a " + vr.getName() +
", since it is possible to throw an exception before the memory for the " + vro.getName() +
" is allocated"
)
or
exists(Expr exp, Variable vr |
(
exp.(DeleteExpr).getEnclosingStmt().getParentStmt*() = cb and
vr = exp.(DeleteExpr).getExpr().(VariableAccess).getTarget()
or
exp.(DeleteArrayExpr).getEnclosingStmt().getParentStmt*() = cb and
vr = exp.(DeleteArrayExpr).getExpr().(VariableAccess).getTarget()
) and
doubleCallDelete(_, cb, vr) and
msg =
"This allocation may have been released in the try block or a previous catch block." +
vr.getName()
)
or
exists(Variable vro, Expr exp |
exp.getEnclosingStmt().getParentStmt*() = cb and
exists(VariableAccess va |
(
va = exp.(DeleteArrayExpr).getExpr() or
va = exp.(DeleteExpr).getExpr()
) and
va.getEnclosingStmt() = exp.getEnclosingStmt() and
vro = va.getTarget()
) and
newThrowDelete(cb, vro) and
msg =
"If the allocation in the try block fails, then an unallocated pointer " + vro.getName() +
" will be freed in the catch block."
)
select cb, msg

View File

@@ -13,7 +13,7 @@ import cpp
from Function f from Function f
where where
f.getName().regexpMatch("atof|atoi|atol") and f.getName() = ["atof", "atoi", "atol"] and
f.getFile().getAbsolutePath().matches("%stdlib.h") f.getFile().getAbsolutePath().matches("%stdlib.h")
select f.getACallToThisFunction(), select f.getACallToThisFunction(),
"AV Rule 23: The library functions atof, atoi and atol from library <stdlib.h> shall not be used." "AV Rule 23: The library functions atof, atoi and atol from library <stdlib.h> shall not be used."

View File

@@ -13,7 +13,7 @@ import cpp
from Function f from Function f
where where
f.getName().regexpMatch("abort|exit|getenv|system") and f.getName() = ["abort", "exit", "getenv", "system"] and
f.getFile().getAbsolutePath().matches("%stdlib.h") f.getFile().getAbsolutePath().matches("%stdlib.h")
select f.getACallToThisFunction(), select f.getACallToThisFunction(),
"The library functions abort, exit, getenv and system from library <stdlib.h> should not be used." "The library functions abort, exit, getenv and system from library <stdlib.h> should not be used."

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-queries name: codeql/cpp-queries
version: 0.1.1-dev version: 0.1.3-dev
groups: groups:
- cpp - cpp
- queries - queries

View File

@@ -181,7 +181,7 @@ private string expectationCommentPattern() { result = "\\s*\\$((?:[^/]|/[^/])*)(
/** /**
* The possible columns in an expectation comment. The `TDefaultColumn` branch represents the first * The possible columns in an expectation comment. The `TDefaultColumn` branch represents the first
* column in a comment. This column is not precedeeded by a name. `TNamedColumn(name)` represents a * column in a comment. This column is not precedeeded by a name. `TNamedColumn(name)` represents a
* column containing expected results preceeded by the string `name:`. * column containing expected results preceded by the string `name:`.
*/ */
private newtype TColumn = private newtype TColumn =
TDefaultColumn() or TDefaultColumn() or

View File

@@ -0,0 +1 @@
| test.cpp:14:16:14:16 | p | unsafe_put_user write user-mode pointer $@ without check. | test.cpp:14:16:14:16 | p | p |

View File

@@ -0,0 +1 @@
experimental/Security/CWE/CWE-020/NoCheckBeforeUnsafePutUser.ql

View File

@@ -0,0 +1,82 @@
typedef unsigned long size_t;
void SYSC_SOMESYSTEMCALL(void *param);
bool user_access_begin_impl(const void *where, size_t sz);
void user_access_end_impl();
#define user_access_begin(where, sz) user_access_begin_impl(where, sz)
#define user_access_end() user_access_end_impl()
void unsafe_put_user_impl(int what, const void *where, size_t sz);
#define unsafe_put_user(what, where) unsafe_put_user_impl( (what), (where), sizeof(*(where)) )
void test1(int p)
{
SYSC_SOMESYSTEMCALL(&p);
unsafe_put_user(123, &p); // BAD
}
void test2(int p)
{
SYSC_SOMESYSTEMCALL(&p);
if (user_access_begin(&p, sizeof(p)))
{
unsafe_put_user(123, &p); // GOOD
user_access_end();
}
}
void test3()
{
int v;
SYSC_SOMESYSTEMCALL(&v);
unsafe_put_user(123, &v); // BAD [NOT DETECTED]
}
void test4()
{
int v;
SYSC_SOMESYSTEMCALL(&v);
if (user_access_begin(&v, sizeof(v)))
{
unsafe_put_user(123, &v); // GOOD
user_access_end();
}
}
struct data
{
int x;
};
void test5()
{
data myData;
SYSC_SOMESYSTEMCALL(&myData);
unsafe_put_user(123, &(myData.x)); // BAD [NOT DETECTED]
}
void test6()
{
data myData;
SYSC_SOMESYSTEMCALL(&myData);
if (user_access_begin(&myData, sizeof(myData)))
{
unsafe_put_user(123, &(myData.x)); // GOOD
user_access_end();
}
}

View File

@@ -0,0 +1,8 @@
| test.cpp:63:3:71:3 | { ... } | If the allocation in the try block fails, then an unallocated pointer bufMyData will be freed in the catch block. |
| test.cpp:63:3:71:3 | { ... } | If the allocation in the try block fails, then an unallocated pointer buffer will be freed in the catch block. |
| test.cpp:63:3:71:3 | { ... } | it is possible to dereference a pointer when accessing a buffer, since it is possible to throw an exception before the memory for the bufMyData is allocated |
| test.cpp:91:3:100:3 | { ... } | If the allocation in the try block fails, then an unallocated pointer buffer will be freed in the catch block. |
| test.cpp:120:3:128:3 | { ... } | If the allocation in the try block fails, then an unallocated pointer buffer will be freed in the catch block. |
| test.cpp:143:3:151:3 | { ... } | If the allocation in the try block fails, then an unallocated pointer buffer will be freed in the catch block. |
| test.cpp:181:3:183:3 | { ... } | This allocation may have been released in the try block or a previous catch block.valData |
| test.cpp:219:3:221:3 | { ... } | This allocation may have been released in the try block or a previous catch block.valData |

View File

@@ -0,0 +1 @@
experimental/Security/CWE/CWE-476/DangerousUseOfExceptionBlocks.ql

View File

@@ -0,0 +1,255 @@
#define NULL ((void*)0)
typedef unsigned long size_t;
namespace std {
enum class align_val_t : size_t {};
}
class exception {};
void cleanFunction();
void* operator new(size_t, float);
void* operator new[](size_t, float);
void* operator new(size_t, std::align_val_t, float);
void* operator new[](size_t, std::align_val_t, float);
void operator delete(void*, float);
void operator delete[](void*, float);
void operator delete(void*, std::align_val_t, float);
void operator delete[](void*, std::align_val_t, float);
struct myData
{
int sizeInt;
char* buffer;
};
struct myGlobalData
{
int sizeInt;
myData** bufMyData;
};
void allocData(myData ** bufMyData) {
for (size_t i = 0; i < 10; i++)
{
bufMyData[i] = new myData;
bufMyData[i]->sizeInt = 10;
bufMyData[i]->buffer = new char[10];
}
}
void throwFunction(int a) {
if (a == 5) throw "my exception!";
}
void throwFunction2(int a) {
if (a == 5) throw exception();
}
void funcWork1b() {
int a;
myData **bufMyData;
try {
cleanFunction();
throwFunction(a);
bufMyData = new myData*[10];
cleanFunction();
allocData(bufMyData);
cleanFunction();
}
catch (...)
{
for (size_t i = 0; i < 10; i++)
{
delete[] bufMyData[i]->buffer; // BAD
delete bufMyData[i];
}
delete [] bufMyData;
}
}
void funcWork1() {
int a;
int i;
myData **bufMyData;
bufMyData = new myData*[10];
for (i = 0; i < 10; i++)
bufMyData[i] = 0;
try {
cleanFunction();
throwFunction(a);
cleanFunction();
allocData(bufMyData);
cleanFunction();
}
catch (...)
{
for (size_t i = 0; i < 10; i++)
{
if (bufMyData[i])
delete[] bufMyData[i]->buffer; // BAD
delete bufMyData[i];
}
delete [] bufMyData;
}
}
void funcWork2() {
int a;
myData **bufMyData;
bufMyData = new myData*[10];
try {
do {
cleanFunction();
allocData(bufMyData);
cleanFunction();
throwFunction(a);
}
while(0);
}
catch (...)
{
for (size_t i = 0; i < 10; i++)
{
delete[] bufMyData[i]->buffer; // BAD
delete bufMyData[i];
}
delete [] bufMyData;
}
}
void funcWork3() {
int a;
myData **bufMyData;
bufMyData = new myData*[10];
try {
cleanFunction();
allocData(bufMyData);
cleanFunction();
throwFunction(a);
}
catch (...)
{
for (size_t i = 0; i < 10; i++)
{
delete[] bufMyData[i]->buffer; // BAD
delete bufMyData[i];
}
delete [] bufMyData;
}
}
void funcWork4() {
int a;
myGlobalData *valData = 0;
try {
valData = new myGlobalData;
cleanFunction();
delete valData;
valData = 0;
throwFunction(a);
}
catch (...)
{
delete valData; // GOOD
}
}
void funcWork4b() {
int a;
myGlobalData *valData = 0;
try {
valData = new myGlobalData;
cleanFunction();
delete valData;
throwFunction(a);
}
catch (...)
{
delete valData; // BAD
}
}
void funcWork5() {
int a;
myGlobalData *valData = 0;
try {
valData = new myGlobalData;
cleanFunction();
delete valData;
valData = 0;
throwFunction2(a);
}
catch (const exception &) {
delete valData;
valData = 0;
throw;
}
catch (...)
{
delete valData; // GOOD
}
}
void funcWork5b() {
int a;
myGlobalData *valData = 0;
try {
valData = new myGlobalData;
cleanFunction();
throwFunction2(a);
}
catch (const exception &) {
delete valData;
throw;
}
catch (...)
{
delete valData; // BAD
}
}
void funcWork6() {
int a;
int flagB = 0;
myGlobalData *valData = 0;
try {
valData = new myGlobalData;
cleanFunction();
throwFunction2(a);
}
catch (const exception &) {
delete valData;
flagB = 1;
throw;
}
catch (...)
{
if(flagB == 0)
delete valData; // GOOD
}
}
void runnerFunc()
{
funcWork1();
funcWork1b();
funcWork2();
funcWork3();
funcWork4();
funcWork4b();
funcWork5();
funcWork5b();
funcWork6();
}

View File

@@ -13559,6 +13559,422 @@ ir.cpp:
# 1754| Type = [SpecifiedType] const CopyConstructorTestVirtualClass # 1754| Type = [SpecifiedType] const CopyConstructorTestVirtualClass
# 1754| ValueCategory = lvalue # 1754| ValueCategory = lvalue
# 1755| getStmt(2): [ReturnStmt] return ... # 1755| getStmt(2): [ReturnStmt] return ...
# 1757| [TopLevelFunction] void if_initialization(int)
# 1757| <params>:
# 1757| getParameter(0): [Parameter] x
# 1757| Type = [IntType] int
# 1757| getEntryPoint(): [BlockStmt] { ... }
# 1758| getStmt(0): [IfStmt] if (...) ...
# 1758| getInitialization(): [DeclStmt] declaration
# 1758| getDeclarationEntry(0): [VariableDeclarationEntry] definition of y
# 1758| Type = [IntType] int
# 1758| getVariable().getInitializer(): [Initializer] initializer for y
# 1758| getExpr(): [VariableAccess] x
# 1758| Type = [IntType] int
# 1758| ValueCategory = prvalue(load)
# 1758| getCondition(): [AddExpr] ... + ...
# 1758| Type = [IntType] int
# 1758| ValueCategory = prvalue
# 1758| getLeftOperand(): [VariableAccess] x
# 1758| Type = [IntType] int
# 1758| ValueCategory = prvalue(load)
# 1758| getRightOperand(): [Literal] 1
# 1758| Type = [IntType] int
# 1758| Value = [Literal] 1
# 1758| ValueCategory = prvalue
# 1758| getThen(): [BlockStmt] { ... }
# 1759| getStmt(0): [ExprStmt] ExprStmt
# 1759| getExpr(): [AssignExpr] ... = ...
# 1759| Type = [IntType] int
# 1759| ValueCategory = lvalue
# 1759| getLValue(): [VariableAccess] x
# 1759| Type = [IntType] int
# 1759| ValueCategory = lvalue
# 1759| getRValue(): [AddExpr] ... + ...
# 1759| Type = [IntType] int
# 1759| ValueCategory = prvalue
# 1759| getLeftOperand(): [VariableAccess] x
# 1759| Type = [IntType] int
# 1759| ValueCategory = prvalue(load)
# 1759| getRightOperand(): [VariableAccess] y
# 1759| Type = [IntType] int
# 1759| ValueCategory = prvalue(load)
# 1758| getCondition().getFullyConverted(): [CStyleCast] (bool)...
# 1758| Conversion = [BoolConversion] conversion to bool
# 1758| Type = [BoolType] bool
# 1758| ValueCategory = prvalue
# 1762| getStmt(1): [DeclStmt] declaration
# 1762| getDeclarationEntry(0): [VariableDeclarationEntry] definition of w
# 1762| Type = [IntType] int
# 1763| getStmt(2): [IfStmt] if (...) ...
# 1763| getInitialization(): [ExprStmt] ExprStmt
# 1763| getExpr(): [AssignExpr] ... = ...
# 1763| Type = [IntType] int
# 1763| ValueCategory = lvalue
# 1763| getLValue(): [VariableAccess] w
# 1763| Type = [IntType] int
# 1763| ValueCategory = lvalue
# 1763| getRValue(): [VariableAccess] x
# 1763| Type = [IntType] int
# 1763| ValueCategory = prvalue(load)
# 1763| getCondition(): [AddExpr] ... + ...
# 1763| Type = [IntType] int
# 1763| ValueCategory = prvalue
# 1763| getLeftOperand(): [VariableAccess] x
# 1763| Type = [IntType] int
# 1763| ValueCategory = prvalue(load)
# 1763| getRightOperand(): [Literal] 1
# 1763| Type = [IntType] int
# 1763| Value = [Literal] 1
# 1763| ValueCategory = prvalue
# 1763| getThen(): [BlockStmt] { ... }
# 1764| getStmt(0): [ExprStmt] ExprStmt
# 1764| getExpr(): [AssignExpr] ... = ...
# 1764| Type = [IntType] int
# 1764| ValueCategory = lvalue
# 1764| getLValue(): [VariableAccess] x
# 1764| Type = [IntType] int
# 1764| ValueCategory = lvalue
# 1764| getRValue(): [AddExpr] ... + ...
# 1764| Type = [IntType] int
# 1764| ValueCategory = prvalue
# 1764| getLeftOperand(): [VariableAccess] x
# 1764| Type = [IntType] int
# 1764| ValueCategory = prvalue(load)
# 1764| getRightOperand(): [VariableAccess] w
# 1764| Type = [IntType] int
# 1764| ValueCategory = prvalue(load)
# 1763| getCondition().getFullyConverted(): [CStyleCast] (bool)...
# 1763| Conversion = [BoolConversion] conversion to bool
# 1763| Type = [BoolType] bool
# 1763| ValueCategory = prvalue
# 1767| getStmt(3): [IfStmt] if (...) ...
# 1767| getInitialization(): [ExprStmt] ExprStmt
# 1767| getExpr(): [AssignExpr] ... = ...
# 1767| Type = [IntType] int
# 1767| ValueCategory = lvalue
# 1767| getLValue(): [VariableAccess] w
# 1767| Type = [IntType] int
# 1767| ValueCategory = lvalue
# 1767| getRValue(): [VariableAccess] x
# 1767| Type = [IntType] int
# 1767| ValueCategory = prvalue(load)
# 1767| getCondition(): [ConditionDeclExpr] (condition decl)
# 1767| Type = [BoolType] bool
# 1767| ValueCategory = prvalue
# 1767| getVariableAccess(): [VariableAccess] w2
# 1767| Type = [IntType] int
# 1767| ValueCategory = prvalue(load)
# 1767| getVariableAccess().getFullyConverted(): [CStyleCast] (bool)...
# 1767| Conversion = [BoolConversion] conversion to bool
# 1767| Type = [BoolType] bool
# 1767| ValueCategory = prvalue
# 1767| getThen(): [BlockStmt] { ... }
# 1768| getStmt(0): [ExprStmt] ExprStmt
# 1768| getExpr(): [AssignExpr] ... = ...
# 1768| Type = [IntType] int
# 1768| ValueCategory = lvalue
# 1768| getLValue(): [VariableAccess] x
# 1768| Type = [IntType] int
# 1768| ValueCategory = lvalue
# 1768| getRValue(): [AddExpr] ... + ...
# 1768| Type = [IntType] int
# 1768| ValueCategory = prvalue
# 1768| getLeftOperand(): [VariableAccess] x
# 1768| Type = [IntType] int
# 1768| ValueCategory = prvalue(load)
# 1768| getRightOperand(): [VariableAccess] w
# 1768| Type = [IntType] int
# 1768| ValueCategory = prvalue(load)
# 1771| getStmt(4): [IfStmt] if (...) ...
# 1771| getInitialization(): [DeclStmt] declaration
# 1771| getDeclarationEntry(0): [VariableDeclarationEntry] definition of v
# 1771| Type = [IntType] int
# 1771| getVariable().getInitializer(): [Initializer] initializer for v
# 1771| getExpr(): [VariableAccess] x
# 1771| Type = [IntType] int
# 1771| ValueCategory = prvalue(load)
# 1771| getCondition(): [ConditionDeclExpr] (condition decl)
# 1771| Type = [BoolType] bool
# 1771| ValueCategory = prvalue
# 1771| getVariableAccess(): [VariableAccess] v2
# 1771| Type = [IntType] int
# 1771| ValueCategory = prvalue(load)
# 1771| getVariableAccess().getFullyConverted(): [CStyleCast] (bool)...
# 1771| Conversion = [BoolConversion] conversion to bool
# 1771| Type = [BoolType] bool
# 1771| ValueCategory = prvalue
# 1771| getThen(): [BlockStmt] { ... }
# 1772| getStmt(0): [ExprStmt] ExprStmt
# 1772| getExpr(): [AssignExpr] ... = ...
# 1772| Type = [IntType] int
# 1772| ValueCategory = lvalue
# 1772| getLValue(): [VariableAccess] x
# 1772| Type = [IntType] int
# 1772| ValueCategory = lvalue
# 1772| getRValue(): [AddExpr] ... + ...
# 1772| Type = [IntType] int
# 1772| ValueCategory = prvalue
# 1772| getLeftOperand(): [VariableAccess] x
# 1772| Type = [IntType] int
# 1772| ValueCategory = prvalue(load)
# 1772| getRightOperand(): [VariableAccess] v
# 1772| Type = [IntType] int
# 1772| ValueCategory = prvalue(load)
# 1775| getStmt(5): [DeclStmt] declaration
# 1775| getDeclarationEntry(0): [VariableDeclarationEntry] definition of z
# 1775| Type = [IntType] int
# 1775| getVariable().getInitializer(): [Initializer] initializer for z
# 1775| getExpr(): [VariableAccess] x
# 1775| Type = [IntType] int
# 1775| ValueCategory = prvalue(load)
# 1776| getStmt(6): [IfStmt] if (...) ...
# 1776| getCondition(): [VariableAccess] z
# 1776| Type = [IntType] int
# 1776| ValueCategory = prvalue(load)
# 1776| getThen(): [BlockStmt] { ... }
# 1777| getStmt(0): [ExprStmt] ExprStmt
# 1777| getExpr(): [AssignExpr] ... = ...
# 1777| Type = [IntType] int
# 1777| ValueCategory = lvalue
# 1777| getLValue(): [VariableAccess] x
# 1777| Type = [IntType] int
# 1777| ValueCategory = lvalue
# 1777| getRValue(): [AddExpr] ... + ...
# 1777| Type = [IntType] int
# 1777| ValueCategory = prvalue
# 1777| getLeftOperand(): [VariableAccess] x
# 1777| Type = [IntType] int
# 1777| ValueCategory = prvalue(load)
# 1777| getRightOperand(): [VariableAccess] z
# 1777| Type = [IntType] int
# 1777| ValueCategory = prvalue(load)
# 1776| getCondition().getFullyConverted(): [CStyleCast] (bool)...
# 1776| Conversion = [BoolConversion] conversion to bool
# 1776| Type = [BoolType] bool
# 1776| ValueCategory = prvalue
# 1780| getStmt(7): [IfStmt] if (...) ...
# 1780| getCondition(): [ConditionDeclExpr] (condition decl)
# 1780| Type = [BoolType] bool
# 1780| ValueCategory = prvalue
# 1780| getVariableAccess(): [VariableAccess] z2
# 1780| Type = [IntType] int
# 1780| ValueCategory = prvalue(load)
# 1780| getVariableAccess().getFullyConverted(): [CStyleCast] (bool)...
# 1780| Conversion = [BoolConversion] conversion to bool
# 1780| Type = [BoolType] bool
# 1780| ValueCategory = prvalue
# 1780| getThen(): [BlockStmt] { ... }
# 1781| getStmt(0): [ExprStmt] ExprStmt
# 1781| getExpr(): [AssignAddExpr] ... += ...
# 1781| Type = [IntType] int
# 1781| ValueCategory = lvalue
# 1781| getLValue(): [VariableAccess] x
# 1781| Type = [IntType] int
# 1781| ValueCategory = lvalue
# 1781| getRValue(): [VariableAccess] z2
# 1781| Type = [IntType] int
# 1781| ValueCategory = prvalue(load)
# 1783| getStmt(8): [ReturnStmt] return ...
# 1785| [TopLevelFunction] void switch_initialization(int)
# 1785| <params>:
# 1785| getParameter(0): [Parameter] x
# 1785| Type = [IntType] int
# 1785| getEntryPoint(): [BlockStmt] { ... }
# 1786| getStmt(0): [SwitchStmt] switch (...) ...
# 1786| getInitialization(): [DeclStmt] declaration
# 1786| getDeclarationEntry(0): [VariableDeclarationEntry] definition of y
# 1786| Type = [IntType] int
# 1786| getVariable().getInitializer(): [Initializer] initializer for y
# 1786| getExpr(): [VariableAccess] x
# 1786| Type = [IntType] int
# 1786| ValueCategory = prvalue(load)
# 1786| getExpr(): [AddExpr] ... + ...
# 1786| Type = [IntType] int
# 1786| ValueCategory = prvalue
# 1786| getLeftOperand(): [VariableAccess] x
# 1786| Type = [IntType] int
# 1786| ValueCategory = prvalue(load)
# 1786| getRightOperand(): [Literal] 1
# 1786| Type = [IntType] int
# 1786| Value = [Literal] 1
# 1786| ValueCategory = prvalue
# 1786| getStmt(): [BlockStmt] { ... }
# 1787| getStmt(0): [SwitchCase] default:
# 1788| getStmt(1): [ExprStmt] ExprStmt
# 1788| getExpr(): [AssignExpr] ... = ...
# 1788| Type = [IntType] int
# 1788| ValueCategory = lvalue
# 1788| getLValue(): [VariableAccess] x
# 1788| Type = [IntType] int
# 1788| ValueCategory = lvalue
# 1788| getRValue(): [AddExpr] ... + ...
# 1788| Type = [IntType] int
# 1788| ValueCategory = prvalue
# 1788| getLeftOperand(): [VariableAccess] x
# 1788| Type = [IntType] int
# 1788| ValueCategory = prvalue(load)
# 1788| getRightOperand(): [VariableAccess] y
# 1788| Type = [IntType] int
# 1788| ValueCategory = prvalue(load)
# 1791| getStmt(1): [DeclStmt] declaration
# 1791| getDeclarationEntry(0): [VariableDeclarationEntry] definition of w
# 1791| Type = [IntType] int
# 1792| getStmt(2): [SwitchStmt] switch (...) ...
# 1792| getInitialization(): [ExprStmt] ExprStmt
# 1792| getExpr(): [AssignExpr] ... = ...
# 1792| Type = [IntType] int
# 1792| ValueCategory = lvalue
# 1792| getLValue(): [VariableAccess] w
# 1792| Type = [IntType] int
# 1792| ValueCategory = lvalue
# 1792| getRValue(): [VariableAccess] x
# 1792| Type = [IntType] int
# 1792| ValueCategory = prvalue(load)
# 1792| getExpr(): [AddExpr] ... + ...
# 1792| Type = [IntType] int
# 1792| ValueCategory = prvalue
# 1792| getLeftOperand(): [VariableAccess] x
# 1792| Type = [IntType] int
# 1792| ValueCategory = prvalue(load)
# 1792| getRightOperand(): [Literal] 1
# 1792| Type = [IntType] int
# 1792| Value = [Literal] 1
# 1792| ValueCategory = prvalue
# 1792| getStmt(): [BlockStmt] { ... }
# 1793| getStmt(0): [SwitchCase] default:
# 1794| getStmt(1): [ExprStmt] ExprStmt
# 1794| getExpr(): [AssignExpr] ... = ...
# 1794| Type = [IntType] int
# 1794| ValueCategory = lvalue
# 1794| getLValue(): [VariableAccess] x
# 1794| Type = [IntType] int
# 1794| ValueCategory = lvalue
# 1794| getRValue(): [AddExpr] ... + ...
# 1794| Type = [IntType] int
# 1794| ValueCategory = prvalue
# 1794| getLeftOperand(): [VariableAccess] x
# 1794| Type = [IntType] int
# 1794| ValueCategory = prvalue(load)
# 1794| getRightOperand(): [VariableAccess] w
# 1794| Type = [IntType] int
# 1794| ValueCategory = prvalue(load)
# 1797| getStmt(3): [SwitchStmt] switch (...) ...
# 1797| getInitialization(): [ExprStmt] ExprStmt
# 1797| getExpr(): [AssignExpr] ... = ...
# 1797| Type = [IntType] int
# 1797| ValueCategory = lvalue
# 1797| getLValue(): [VariableAccess] w
# 1797| Type = [IntType] int
# 1797| ValueCategory = lvalue
# 1797| getRValue(): [VariableAccess] x
# 1797| Type = [IntType] int
# 1797| ValueCategory = prvalue(load)
# 1797| getExpr(): [ConditionDeclExpr] (condition decl)
# 1797| Type = [IntType] int
# 1797| ValueCategory = prvalue
# 1797| getVariableAccess(): [VariableAccess] w2
# 1797| Type = [IntType] int
# 1797| ValueCategory = prvalue(load)
# 1797| getStmt(): [BlockStmt] { ... }
# 1798| getStmt(0): [SwitchCase] default:
# 1799| getStmt(1): [ExprStmt] ExprStmt
# 1799| getExpr(): [AssignExpr] ... = ...
# 1799| Type = [IntType] int
# 1799| ValueCategory = lvalue
# 1799| getLValue(): [VariableAccess] x
# 1799| Type = [IntType] int
# 1799| ValueCategory = lvalue
# 1799| getRValue(): [AddExpr] ... + ...
# 1799| Type = [IntType] int
# 1799| ValueCategory = prvalue
# 1799| getLeftOperand(): [VariableAccess] x
# 1799| Type = [IntType] int
# 1799| ValueCategory = prvalue(load)
# 1799| getRightOperand(): [VariableAccess] w
# 1799| Type = [IntType] int
# 1799| ValueCategory = prvalue(load)
# 1802| getStmt(4): [SwitchStmt] switch (...) ...
# 1802| getInitialization(): [DeclStmt] declaration
# 1802| getDeclarationEntry(0): [VariableDeclarationEntry] definition of v
# 1802| Type = [IntType] int
# 1802| getVariable().getInitializer(): [Initializer] initializer for v
# 1802| getExpr(): [VariableAccess] x
# 1802| Type = [IntType] int
# 1802| ValueCategory = prvalue(load)
# 1802| getExpr(): [ConditionDeclExpr] (condition decl)
# 1802| Type = [IntType] int
# 1802| ValueCategory = prvalue
# 1802| getVariableAccess(): [VariableAccess] v2
# 1802| Type = [IntType] int
# 1802| ValueCategory = prvalue(load)
# 1802| getStmt(): [BlockStmt] { ... }
# 1803| getStmt(0): [SwitchCase] default:
# 1804| getStmt(1): [ExprStmt] ExprStmt
# 1804| getExpr(): [AssignExpr] ... = ...
# 1804| Type = [IntType] int
# 1804| ValueCategory = lvalue
# 1804| getLValue(): [VariableAccess] x
# 1804| Type = [IntType] int
# 1804| ValueCategory = lvalue
# 1804| getRValue(): [AddExpr] ... + ...
# 1804| Type = [IntType] int
# 1804| ValueCategory = prvalue
# 1804| getLeftOperand(): [VariableAccess] x
# 1804| Type = [IntType] int
# 1804| ValueCategory = prvalue(load)
# 1804| getRightOperand(): [VariableAccess] v
# 1804| Type = [IntType] int
# 1804| ValueCategory = prvalue(load)
# 1807| getStmt(5): [DeclStmt] declaration
# 1807| getDeclarationEntry(0): [VariableDeclarationEntry] definition of z
# 1807| Type = [IntType] int
# 1807| getVariable().getInitializer(): [Initializer] initializer for z
# 1807| getExpr(): [VariableAccess] x
# 1807| Type = [IntType] int
# 1807| ValueCategory = prvalue(load)
# 1808| getStmt(6): [SwitchStmt] switch (...) ...
# 1808| getExpr(): [VariableAccess] z
# 1808| Type = [IntType] int
# 1808| ValueCategory = prvalue(load)
# 1808| getStmt(): [BlockStmt] { ... }
# 1809| getStmt(0): [SwitchCase] default:
# 1810| getStmt(1): [ExprStmt] ExprStmt
# 1810| getExpr(): [AssignExpr] ... = ...
# 1810| Type = [IntType] int
# 1810| ValueCategory = lvalue
# 1810| getLValue(): [VariableAccess] x
# 1810| Type = [IntType] int
# 1810| ValueCategory = lvalue
# 1810| getRValue(): [AddExpr] ... + ...
# 1810| Type = [IntType] int
# 1810| ValueCategory = prvalue
# 1810| getLeftOperand(): [VariableAccess] x
# 1810| Type = [IntType] int
# 1810| ValueCategory = prvalue(load)
# 1810| getRightOperand(): [VariableAccess] z
# 1810| Type = [IntType] int
# 1810| ValueCategory = prvalue(load)
# 1813| getStmt(7): [SwitchStmt] switch (...) ...
# 1813| getExpr(): [ConditionDeclExpr] (condition decl)
# 1813| Type = [IntType] int
# 1813| ValueCategory = prvalue
# 1813| getVariableAccess(): [VariableAccess] z2
# 1813| Type = [IntType] int
# 1813| ValueCategory = prvalue(load)
# 1813| getStmt(): [BlockStmt] { ... }
# 1814| getStmt(0): [SwitchCase] default:
# 1815| getStmt(1): [ExprStmt] ExprStmt
# 1815| getExpr(): [AssignAddExpr] ... += ...
# 1815| Type = [IntType] int
# 1815| ValueCategory = lvalue
# 1815| getLValue(): [VariableAccess] x
# 1815| Type = [IntType] int
# 1815| ValueCategory = lvalue
# 1815| getRValue(): [VariableAccess] z2
# 1815| Type = [IntType] int
# 1815| ValueCategory = prvalue(load)
# 1817| getStmt(8): [ReturnStmt] return ...
perf-regression.cpp: perf-regression.cpp:
# 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&) # 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&)
# 4| <params>: # 4| <params>:

View File

@@ -6,6 +6,8 @@ missingOperandType
duplicateChiOperand duplicateChiOperand
sideEffectWithoutPrimary sideEffectWithoutPrimary
instructionWithoutSuccessor instructionWithoutSuccessor
| ir.cpp:1757:28:1757:28 | InitializeParameter: x | Instruction 'InitializeParameter: x' has no successors in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
| ir.cpp:1785:32:1785:32 | InitializeParameter: x | Instruction 'InitializeParameter: x' has no successors in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
ambiguousSuccessors ambiguousSuccessors
unexplainedLoop unexplainedLoop
unnecessaryPhiInstruction unnecessaryPhiInstruction

View File

@@ -6,6 +6,8 @@ missingOperandType
duplicateChiOperand duplicateChiOperand
sideEffectWithoutPrimary sideEffectWithoutPrimary
instructionWithoutSuccessor instructionWithoutSuccessor
| ir.cpp:1757:28:1757:28 | InitializeParameter: x | Instruction 'InitializeParameter: x' has no successors in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
| ir.cpp:1785:32:1785:32 | InitializeParameter: x | Instruction 'InitializeParameter: x' has no successors in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
ambiguousSuccessors ambiguousSuccessors
unexplainedLoop unexplainedLoop
unnecessaryPhiInstruction unnecessaryPhiInstruction

View File

@@ -1754,6 +1754,68 @@ int implicit_copy_constructor_test(
CopyConstructorTestVirtualClass cy = y; CopyConstructorTestVirtualClass cy = y;
} }
void if_initialization(int x) {
if (int y = x; x + 1) {
x = x + y;
}
int w;
if (w = x; x + 1) {
x = x + w;
}
if (w = x; int w2 = w) {
x = x + w;
}
if (int v = x; int v2 = v) {
x = x + v;
}
int z = x;
if (z) {
x = x + z;
}
if (int z2 = z) {
x += z2;
}
}
void switch_initialization(int x) {
switch (int y = x; x + 1) {
default:
x = x + y;
}
int w;
switch (w = x; x + 1) {
default:
x = x + w;
}
switch (w = x; int w2 = w) {
default:
x = x + w;
}
switch (int v = x; int v2 = v) {
default:
x = x + v;
}
int z = x;
switch (z) {
default:
x = x + z;
}
switch (int z2 = z) {
default:
x += z2;
}
}
int global_1; int global_1;
int global_2 = 1; int global_2 = 1;

View File

@@ -4841,9 +4841,6 @@
| ir.cpp:1043:24:1043:24 | SideEffect | ~m1043_20 | | ir.cpp:1043:24:1043:24 | SideEffect | ~m1043_20 |
| ir.cpp:1043:31:1043:31 | Address | &:r1043_9 | | ir.cpp:1043:31:1043:31 | Address | &:r1043_9 |
| ir.cpp:1043:36:1043:55 | Address | &:r1043_11 | | ir.cpp:1043:36:1043:55 | Address | &:r1043_11 |
| ir.cpp:1043:43:1043:43 | Address | &:r1043_16 |
| ir.cpp:1043:43:1043:43 | Arg(this) | this:r1043_16 |
| ir.cpp:1043:43:1043:43 | SideEffect | ~m1043_20 |
| ir.cpp:1043:43:1043:54 | Address | &:r1043_22 | | ir.cpp:1043:43:1043:54 | Address | &:r1043_22 |
| ir.cpp:1043:43:1043:54 | Address | &:r1043_24 | | ir.cpp:1043:43:1043:54 | Address | &:r1043_24 |
| ir.cpp:1043:43:1043:54 | Address | &:r1043_25 | | ir.cpp:1043:43:1043:54 | Address | &:r1043_25 |
@@ -4864,8 +4861,11 @@
| ir.cpp:1043:45:1043:49 | SideEffect | ~m1043_4 | | ir.cpp:1043:45:1043:49 | SideEffect | ~m1043_4 |
| ir.cpp:1043:45:1043:49 | Unary | r1043_13 | | ir.cpp:1043:45:1043:49 | Unary | r1043_13 |
| ir.cpp:1043:45:1043:49 | Unary | r1043_15 | | ir.cpp:1043:45:1043:49 | Unary | r1043_15 |
| ir.cpp:1043:53:1043:53 | Load | ~m1043_20 | | ir.cpp:1043:52:1043:52 | Address | &:r1043_16 |
| ir.cpp:1043:53:1043:53 | Right | r1043_26 | | ir.cpp:1043:52:1043:52 | Arg(this) | this:r1043_16 |
| ir.cpp:1043:52:1043:52 | SideEffect | ~m1043_20 |
| ir.cpp:1043:54:1043:54 | Load | ~m1043_20 |
| ir.cpp:1043:54:1043:54 | Right | r1043_26 |
| ir.cpp:1043:58:1043:58 | ChiPartial | partial:m1043_9 | | ir.cpp:1043:58:1043:58 | ChiPartial | partial:m1043_9 |
| ir.cpp:1043:58:1043:58 | ChiTotal | total:m1043_3 | | ir.cpp:1043:58:1043:58 | ChiTotal | total:m1043_3 |
| ir.cpp:1043:58:1043:58 | StoreValue | r1043_8 | | ir.cpp:1043:58:1043:58 | StoreValue | r1043_8 |
@@ -4980,9 +4980,6 @@
| ir.cpp:1047:34:1047:34 | SideEffect | ~m1047_20 | | ir.cpp:1047:34:1047:34 | SideEffect | ~m1047_20 |
| ir.cpp:1047:41:1047:41 | Address | &:r1047_9 | | ir.cpp:1047:41:1047:41 | Address | &:r1047_9 |
| ir.cpp:1047:46:1047:65 | Address | &:r1047_11 | | ir.cpp:1047:46:1047:65 | Address | &:r1047_11 |
| ir.cpp:1047:53:1047:53 | Address | &:r1047_16 |
| ir.cpp:1047:53:1047:53 | Arg(this) | this:r1047_16 |
| ir.cpp:1047:53:1047:53 | SideEffect | ~m1047_20 |
| ir.cpp:1047:53:1047:64 | Address | &:r1047_23 | | ir.cpp:1047:53:1047:64 | Address | &:r1047_23 |
| ir.cpp:1047:53:1047:64 | Load | ~m1047_20 | | ir.cpp:1047:53:1047:64 | Load | ~m1047_20 |
| ir.cpp:1047:53:1047:64 | StoreValue | r1047_24 | | ir.cpp:1047:53:1047:64 | StoreValue | r1047_24 |
@@ -4997,6 +4994,9 @@
| ir.cpp:1047:55:1047:59 | SideEffect | ~m1047_4 | | ir.cpp:1047:55:1047:59 | SideEffect | ~m1047_4 |
| ir.cpp:1047:55:1047:59 | Unary | r1047_13 | | ir.cpp:1047:55:1047:59 | Unary | r1047_13 |
| ir.cpp:1047:55:1047:59 | Unary | r1047_15 | | ir.cpp:1047:55:1047:59 | Unary | r1047_15 |
| ir.cpp:1047:62:1047:62 | Address | &:r1047_16 |
| ir.cpp:1047:62:1047:62 | Arg(this) | this:r1047_16 |
| ir.cpp:1047:62:1047:62 | SideEffect | ~m1047_20 |
| ir.cpp:1047:63:1047:63 | Right | r1047_22 | | ir.cpp:1047:63:1047:63 | Right | r1047_22 |
| ir.cpp:1047:68:1047:68 | StoreValue | r1047_8 | | ir.cpp:1047:68:1047:68 | StoreValue | r1047_8 |
| ir.cpp:1047:68:1047:68 | Unary | r1047_7 | | ir.cpp:1047:68:1047:68 | Unary | r1047_7 |
@@ -5105,9 +5105,6 @@
| ir.cpp:1051:39:1051:39 | SideEffect | ~m1051_20 | | ir.cpp:1051:39:1051:39 | SideEffect | ~m1051_20 |
| ir.cpp:1051:46:1051:46 | Address | &:r1051_9 | | ir.cpp:1051:46:1051:46 | Address | &:r1051_9 |
| ir.cpp:1051:51:1051:70 | Address | &:r1051_11 | | ir.cpp:1051:51:1051:70 | Address | &:r1051_11 |
| ir.cpp:1051:58:1051:58 | Address | &:r1051_16 |
| ir.cpp:1051:58:1051:58 | Arg(this) | this:r1051_16 |
| ir.cpp:1051:58:1051:58 | SideEffect | ~m1051_20 |
| ir.cpp:1051:58:1051:69 | Address | &:r1051_22 | | ir.cpp:1051:58:1051:69 | Address | &:r1051_22 |
| ir.cpp:1051:58:1051:69 | Address | &:r1051_24 | | ir.cpp:1051:58:1051:69 | Address | &:r1051_24 |
| ir.cpp:1051:58:1051:69 | Address | &:r1051_26 | | ir.cpp:1051:58:1051:69 | Address | &:r1051_26 |
@@ -5128,6 +5125,9 @@
| ir.cpp:1051:60:1051:64 | SideEffect | ~m1051_4 | | ir.cpp:1051:60:1051:64 | SideEffect | ~m1051_4 |
| ir.cpp:1051:60:1051:64 | Unary | r1051_13 | | ir.cpp:1051:60:1051:64 | Unary | r1051_13 |
| ir.cpp:1051:60:1051:64 | Unary | r1051_15 | | ir.cpp:1051:60:1051:64 | Unary | r1051_15 |
| ir.cpp:1051:67:1051:67 | Address | &:r1051_16 |
| ir.cpp:1051:67:1051:67 | Arg(this) | this:r1051_16 |
| ir.cpp:1051:67:1051:67 | SideEffect | ~m1051_20 |
| ir.cpp:1051:73:1051:73 | ChiPartial | partial:m1051_9 | | ir.cpp:1051:73:1051:73 | ChiPartial | partial:m1051_9 |
| ir.cpp:1051:73:1051:73 | ChiTotal | total:m1051_3 | | ir.cpp:1051:73:1051:73 | ChiTotal | total:m1051_3 |
| ir.cpp:1051:73:1051:73 | StoreValue | r1051_8 | | ir.cpp:1051:73:1051:73 | StoreValue | r1051_8 |
@@ -5192,9 +5192,6 @@
| ir.cpp:1054:49:1054:49 | SideEffect | ~m1054_20 | | ir.cpp:1054:49:1054:49 | SideEffect | ~m1054_20 |
| ir.cpp:1054:56:1054:56 | Address | &:r1054_9 | | ir.cpp:1054:56:1054:56 | Address | &:r1054_9 |
| ir.cpp:1054:61:1054:88 | Address | &:r1054_11 | | ir.cpp:1054:61:1054:88 | Address | &:r1054_11 |
| ir.cpp:1054:68:1054:68 | Address | &:r1054_16 |
| ir.cpp:1054:68:1054:68 | Arg(this) | this:r1054_16 |
| ir.cpp:1054:68:1054:68 | SideEffect | ~m1054_20 |
| ir.cpp:1054:68:1054:87 | Address | &:r1054_37 | | ir.cpp:1054:68:1054:87 | Address | &:r1054_37 |
| ir.cpp:1054:68:1054:87 | Load | ~m1054_20 | | ir.cpp:1054:68:1054:87 | Load | ~m1054_20 |
| ir.cpp:1054:68:1054:87 | StoreValue | r1054_38 | | ir.cpp:1054:68:1054:87 | StoreValue | r1054_38 |
@@ -5209,6 +5206,9 @@
| ir.cpp:1054:70:1054:74 | SideEffect | ~m1054_4 | | ir.cpp:1054:70:1054:74 | SideEffect | ~m1054_4 |
| ir.cpp:1054:70:1054:74 | Unary | r1054_13 | | ir.cpp:1054:70:1054:74 | Unary | r1054_13 |
| ir.cpp:1054:70:1054:74 | Unary | r1054_15 | | ir.cpp:1054:70:1054:74 | Unary | r1054_15 |
| ir.cpp:1054:77:1054:77 | Address | &:r1054_16 |
| ir.cpp:1054:77:1054:77 | Arg(this) | this:r1054_16 |
| ir.cpp:1054:77:1054:77 | SideEffect | ~m1054_20 |
| ir.cpp:1054:78:1054:82 | Address | &:r1054_22 | | ir.cpp:1054:78:1054:82 | Address | &:r1054_22 |
| ir.cpp:1054:78:1054:82 | Address | &:r1054_24 | | ir.cpp:1054:78:1054:82 | Address | &:r1054_24 |
| ir.cpp:1054:78:1054:82 | Left | r1054_25 | | ir.cpp:1054:78:1054:82 | Left | r1054_25 |
@@ -8223,45 +8223,51 @@
| ir.cpp:1754:42:1754:42 | SideEffect | ~m1752_4 | | ir.cpp:1754:42:1754:42 | SideEffect | ~m1752_4 |
| ir.cpp:1754:42:1754:42 | Unary | r1754_5 | | ir.cpp:1754:42:1754:42 | Unary | r1754_5 |
| ir.cpp:1754:42:1754:42 | Unary | r1754_6 | | ir.cpp:1754:42:1754:42 | Unary | r1754_6 |
| ir.cpp:1759:5:1759:12 | Address | &:r1759_3 | | ir.cpp:1757:6:1757:22 | ChiPartial | partial:m1757_3 |
| ir.cpp:1759:5:1759:12 | SideEffect | ~m1759_6 | | ir.cpp:1757:6:1757:22 | ChiTotal | total:m1757_2 |
| ir.cpp:1759:16:1759:16 | ChiPartial | partial:m1759_5 | | ir.cpp:1757:28:1757:28 | Address | &:r1757_5 |
| ir.cpp:1759:16:1759:16 | ChiTotal | total:m1759_2 | | ir.cpp:1785:6:1785:26 | ChiPartial | partial:m1785_3 |
| ir.cpp:1759:16:1759:16 | StoreValue | r1759_4 | | ir.cpp:1785:6:1785:26 | ChiTotal | total:m1785_2 |
| ir.cpp:1763:18:1763:25 | Address | &:r1763_3 | | ir.cpp:1785:32:1785:32 | Address | &:r1785_5 |
| ir.cpp:1763:18:1763:25 | Arg(this) | this:r1763_3 | | ir.cpp:1821:5:1821:12 | Address | &:r1821_3 |
| ir.cpp:1763:18:1763:25 | SideEffect | ~m1763_10 | | ir.cpp:1821:5:1821:12 | SideEffect | ~m1821_6 |
| ir.cpp:1763:27:1763:27 | Arg(0) | 0:r1763_5 | | ir.cpp:1821:16:1821:16 | ChiPartial | partial:m1821_5 |
| ir.cpp:1763:27:1763:28 | CallTarget | func:r1763_4 | | ir.cpp:1821:16:1821:16 | ChiTotal | total:m1821_2 |
| ir.cpp:1763:27:1763:28 | ChiPartial | partial:m1763_7 | | ir.cpp:1821:16:1821:16 | StoreValue | r1821_4 |
| ir.cpp:1763:27:1763:28 | ChiPartial | partial:m1763_9 | | ir.cpp:1825:18:1825:25 | Address | &:r1825_3 |
| ir.cpp:1763:27:1763:28 | ChiTotal | total:m1763_2 | | ir.cpp:1825:18:1825:25 | Arg(this) | this:r1825_3 |
| ir.cpp:1763:27:1763:28 | ChiTotal | total:m1763_8 | | ir.cpp:1825:18:1825:25 | SideEffect | ~m1825_10 |
| ir.cpp:1763:27:1763:28 | SideEffect | ~m1763_2 | | ir.cpp:1825:27:1825:27 | Arg(0) | 0:r1825_5 |
| ir.cpp:1765:18:1765:25 | Address | &:r1765_3 | | ir.cpp:1825:27:1825:28 | CallTarget | func:r1825_4 |
| ir.cpp:1765:18:1765:25 | Arg(this) | this:r1765_3 | | ir.cpp:1825:27:1825:28 | ChiPartial | partial:m1825_7 |
| ir.cpp:1765:18:1765:25 | SideEffect | ~m1765_10 | | ir.cpp:1825:27:1825:28 | ChiPartial | partial:m1825_9 |
| ir.cpp:1765:28:1765:47 | CallTarget | func:r1765_4 | | ir.cpp:1825:27:1825:28 | ChiTotal | total:m1825_2 |
| ir.cpp:1765:28:1765:47 | ChiPartial | partial:m1765_7 | | ir.cpp:1825:27:1825:28 | ChiTotal | total:m1825_8 |
| ir.cpp:1765:28:1765:47 | ChiPartial | partial:m1765_9 | | ir.cpp:1825:27:1825:28 | SideEffect | ~m1825_2 |
| ir.cpp:1765:28:1765:47 | ChiTotal | total:m1765_2 | | ir.cpp:1827:18:1827:25 | Address | &:r1827_3 |
| ir.cpp:1765:28:1765:47 | ChiTotal | total:m1765_8 | | ir.cpp:1827:18:1827:25 | Arg(this) | this:r1827_3 |
| ir.cpp:1765:28:1765:47 | SideEffect | ~m1765_2 | | ir.cpp:1827:18:1827:25 | SideEffect | ~m1827_10 |
| ir.cpp:1765:46:1765:46 | Arg(0) | 0:r1765_5 | | ir.cpp:1827:28:1827:47 | CallTarget | func:r1827_4 |
| ir.cpp:1767:7:1767:19 | Address | &:r1767_3 | | ir.cpp:1827:28:1827:47 | ChiPartial | partial:m1827_7 |
| ir.cpp:1767:7:1767:19 | SideEffect | ~m1767_8 | | ir.cpp:1827:28:1827:47 | ChiPartial | partial:m1827_9 |
| ir.cpp:1767:23:1767:37 | ChiPartial | partial:m1767_7 | | ir.cpp:1827:28:1827:47 | ChiTotal | total:m1827_2 |
| ir.cpp:1767:23:1767:37 | ChiTotal | total:m1767_2 | | ir.cpp:1827:28:1827:47 | ChiTotal | total:m1827_8 |
| ir.cpp:1767:23:1767:37 | StoreValue | r1767_6 | | ir.cpp:1827:28:1827:47 | SideEffect | ~m1827_2 |
| ir.cpp:1767:23:1767:37 | Unary | r1767_4 | | ir.cpp:1827:46:1827:46 | Arg(0) | 0:r1827_5 |
| ir.cpp:1767:23:1767:37 | Unary | r1767_5 | | ir.cpp:1829:7:1829:19 | Address | &:r1829_3 |
| ir.cpp:1769:5:1769:12 | Address | &:r1769_3 | | ir.cpp:1829:7:1829:19 | SideEffect | ~m1829_8 |
| ir.cpp:1769:5:1769:12 | SideEffect | ~m1769_7 | | ir.cpp:1829:23:1829:37 | ChiPartial | partial:m1829_7 |
| ir.cpp:1769:16:1769:23 | Address | &:r1769_4 | | ir.cpp:1829:23:1829:37 | ChiTotal | total:m1829_2 |
| ir.cpp:1769:16:1769:23 | ChiPartial | partial:m1769_6 | | ir.cpp:1829:23:1829:37 | StoreValue | r1829_6 |
| ir.cpp:1769:16:1769:23 | ChiTotal | total:m1769_2 | | ir.cpp:1829:23:1829:37 | Unary | r1829_4 |
| ir.cpp:1769:16:1769:23 | Load | ~m1769_2 | | ir.cpp:1829:23:1829:37 | Unary | r1829_5 |
| ir.cpp:1769:16:1769:23 | StoreValue | r1769_5 | | ir.cpp:1831:5:1831:12 | Address | &:r1831_3 |
| ir.cpp:1831:5:1831:12 | SideEffect | ~m1831_7 |
| ir.cpp:1831:16:1831:23 | Address | &:r1831_4 |
| ir.cpp:1831:16:1831:23 | ChiPartial | partial:m1831_6 |
| ir.cpp:1831:16:1831:23 | ChiTotal | total:m1831_2 |
| ir.cpp:1831:16:1831:23 | Load | ~m1831_2 |
| ir.cpp:1831:16:1831:23 | StoreValue | r1831_5 |
| perf-regression.cpp:6:3:6:5 | Address | &:r6_5 | | perf-regression.cpp:6:3:6:5 | Address | &:r6_5 |
| perf-regression.cpp:6:3:6:5 | Address | &:r6_5 | | perf-regression.cpp:6:3:6:5 | Address | &:r6_5 |
| perf-regression.cpp:6:3:6:5 | Address | &:r6_7 | | perf-regression.cpp:6:3:6:5 | Address | &:r6_7 |

View File

@@ -1,4 +1,8 @@
missingOperand missingOperand
| ir.cpp:1758:9:1758:24 | CopyValue: (condition decl) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
| ir.cpp:1763:14:1763:20 | CopyValue: (condition decl) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
| ir.cpp:1786:13:1786:28 | CopyValue: (condition decl) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
| ir.cpp:1792:18:1792:24 | CopyValue: (condition decl) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
unexpectedOperand unexpectedOperand
duplicateOperand duplicateOperand
missingPhiOperand missingPhiOperand
@@ -7,6 +11,14 @@ duplicateChiOperand
sideEffectWithoutPrimary sideEffectWithoutPrimary
instructionWithoutSuccessor instructionWithoutSuccessor
| ../../../include/memory.h:68:25:68:33 | CopyValue: (reference to) | Instruction 'CopyValue: (reference to)' has no successors in function '$@'. | ../../../include/memory.h:67:5:67:5 | void std::unique_ptr<int, std::default_delete<int>>::~unique_ptr() | void std::unique_ptr<int, std::default_delete<int>>::~unique_ptr() | | ../../../include/memory.h:68:25:68:33 | CopyValue: (reference to) | Instruction 'CopyValue: (reference to)' has no successors in function '$@'. | ../../../include/memory.h:67:5:67:5 | void std::unique_ptr<int, std::default_delete<int>>::~unique_ptr() | void std::unique_ptr<int, std::default_delete<int>>::~unique_ptr() |
| ir.cpp:1757:28:1757:28 | InitializeParameter: x | Instruction 'InitializeParameter: x' has no successors in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
| ir.cpp:1758:20:1758:24 | CompareNE: (bool)... | Instruction 'CompareNE: (bool)...' has no successors in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
| ir.cpp:1762:9:1762:9 | Uninitialized: definition of w | Instruction 'Uninitialized: definition of w' has no successors in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
| ir.cpp:1763:16:1763:20 | CompareNE: (bool)... | Instruction 'CompareNE: (bool)...' has no successors in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
| ir.cpp:1785:32:1785:32 | InitializeParameter: x | Instruction 'InitializeParameter: x' has no successors in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
| ir.cpp:1786:24:1786:28 | Add: ... + ... | Instruction 'Add: ... + ...' has no successors in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
| ir.cpp:1791:9:1791:9 | Uninitialized: definition of w | Instruction 'Uninitialized: definition of w' has no successors in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
| ir.cpp:1792:20:1792:24 | Add: ... + ... | Instruction 'Add: ... + ...' has no successors in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
ambiguousSuccessors ambiguousSuccessors
unexplainedLoop unexplainedLoop
unnecessaryPhiInstruction unnecessaryPhiInstruction
@@ -28,6 +40,12 @@ nonUniqueEnclosingIRFunction
fieldAddressOnNonPointer fieldAddressOnNonPointer
thisArgumentIsNonPointer thisArgumentIsNonPointer
nonUniqueIRVariable nonUniqueIRVariable
| ir.cpp:1759:17:1759:17 | VariableAddress: y | Variable address instruction 'VariableAddress: y' has no associated variable, in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
| ir.cpp:1771:29:1771:29 | VariableAddress: v | Variable address instruction 'VariableAddress: v' has no associated variable, in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
| ir.cpp:1772:17:1772:17 | VariableAddress: v | Variable address instruction 'VariableAddress: v' has no associated variable, in function '$@'. | ir.cpp:1757:6:1757:22 | void if_initialization(int) | void if_initialization(int) |
| ir.cpp:1788:17:1788:17 | VariableAddress: y | Variable address instruction 'VariableAddress: y' has no associated variable, in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
| ir.cpp:1802:33:1802:33 | VariableAddress: v | Variable address instruction 'VariableAddress: v' has no associated variable, in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
| ir.cpp:1804:17:1804:17 | VariableAddress: v | Variable address instruction 'VariableAddress: v' has no associated variable, in function '$@'. | ir.cpp:1785:6:1785:26 | void switch_initialization(int) | void switch_initialization(int) |
missingCanonicalLanguageType missingCanonicalLanguageType
multipleCanonicalLanguageTypes multipleCanonicalLanguageTypes
missingIRType missingIRType

View File

@@ -9431,69 +9431,359 @@ ir.cpp:
# 1750| v1750_6(void) = AliasedUse : ~m? # 1750| v1750_6(void) = AliasedUse : ~m?
# 1750| v1750_7(void) = ExitFunction : # 1750| v1750_7(void) = ExitFunction :
# 1759| int global_2 # 1757| void if_initialization(int)
# 1759| Block 0 # 1758| (no string representation)
# 1759| v1759_1(void) = EnterFunction : # 1758| CopyValue: (condition decl)
# 1759| mu1759_2(unknown) = AliasedDefinition : # 1758| ConditionalBranch: (condition decl)
# 1759| r1759_3(glval<int>) = VariableAddress[global_2] : #-----| False -> Block 4
# 1759| r1759_4(int) = Constant[1] : #-----| True -> Block 3
# 1759| mu1759_5(int) = Store[global_2] : &:r1759_3, r1759_4
# 1759| v1759_6(void) = ReturnVoid :
# 1759| v1759_7(void) = AliasedUse : ~m?
# 1759| v1759_8(void) = ExitFunction :
# 1763| constructor_only global_4 # 1763| (no string representation)
# 1763| Block 0 # 1763| CopyValue: (condition decl)
# 1763| v1763_1(void) = EnterFunction : # 1763| ConditionalBranch: (condition decl)
# 1763| mu1763_2(unknown) = AliasedDefinition : #-----| False -> Block 6
# 1763| r1763_3(glval<constructor_only>) = VariableAddress[global_4] : #-----| True -> Block 5
# 1763| r1763_4(glval<unknown>) = FunctionAddress[constructor_only] :
# 1763| r1763_5(int) = Constant[1] :
# 1763| v1763_6(void) = Call[constructor_only] : func:r1763_4, this:r1763_3, 0:r1763_5
# 1763| mu1763_7(unknown) = ^CallSideEffect : ~m?
# 1763| mu1763_8(constructor_only) = ^IndirectMayWriteSideEffect[-1] : &:r1763_3
# 1763| v1763_9(void) = ReturnVoid :
# 1763| v1763_10(void) = AliasedUse : ~m?
# 1763| v1763_11(void) = ExitFunction :
# 1765| constructor_only global_5 # 1757| Block 0
# 1765| Block 0 # 1757| v1757_1(void) = EnterFunction :
# 1765| v1765_1(void) = EnterFunction : # 1757| mu1757_2(unknown) = AliasedDefinition :
# 1765| mu1765_2(unknown) = AliasedDefinition : # 1757| mu1757_3(unknown) = InitializeNonLocal :
# 1765| r1765_3(glval<constructor_only>) = VariableAddress[global_5] : # 1757| r1757_4(glval<int>) = VariableAddress[x] :
# 1765| r1765_4(glval<unknown>) = FunctionAddress[constructor_only] : # 1757| mu1757_5(int) = InitializeParameter[x] : &:r1757_4
# 1765| r1765_5(int) = Constant[2] :
# 1765| v1765_6(void) = Call[constructor_only] : func:r1765_4, this:r1765_3, 0:r1765_5
# 1765| mu1765_7(unknown) = ^CallSideEffect : ~m?
# 1765| mu1765_8(constructor_only) = ^IndirectMayWriteSideEffect[-1] : &:r1765_3
# 1765| v1765_9(void) = ReturnVoid :
# 1765| v1765_10(void) = AliasedUse : ~m?
# 1765| v1765_11(void) = ExitFunction :
# 1767| char* global_string # 1758| Block 1
# 1767| Block 0 # 1758| r1758_1(glval<int>) = VariableAddress[x] :
# 1767| v1767_1(void) = EnterFunction : # 1758| r1758_2(int) = Load[x] : &:r1758_1, ~m?
# 1767| mu1767_2(unknown) = AliasedDefinition : # 1758| r1758_3(int) = Constant[1] :
# 1767| r1767_3(glval<char *>) = VariableAddress[global_string] : # 1758| r1758_4(int) = Add : r1758_2, r1758_3
# 1767| r1767_4(glval<char[14]>) = StringConstant["global string"] : # 1758| r1758_5(int) = Constant[0] :
# 1767| r1767_5(char *) = Convert : r1767_4 # 1758| r1758_6(bool) = CompareNE : r1758_4, r1758_5
# 1767| r1767_6(char *) = Convert : r1767_5
# 1767| mu1767_7(char *) = Store[global_string] : &:r1767_3, r1767_6
# 1767| v1767_8(void) = ReturnVoid :
# 1767| v1767_9(void) = AliasedUse : ~m?
# 1767| v1767_10(void) = ExitFunction :
# 1769| int global_6 # 1763| Block 1
# 1769| Block 0 # 1763| r1763_1(glval<int>) = VariableAddress[x] :
# 1769| v1769_1(void) = EnterFunction : # 1763| r1763_2(int) = Load[x] : &:r1763_1, ~m?
# 1769| mu1769_2(unknown) = AliasedDefinition : # 1763| r1763_3(int) = Constant[1] :
# 1769| r1769_3(glval<int>) = VariableAddress[global_6] : # 1763| r1763_4(int) = Add : r1763_2, r1763_3
# 1769| r1769_4(glval<int>) = VariableAddress[global_2] : # 1763| r1763_5(int) = Constant[0] :
# 1769| r1769_5(int) = Load[global_2] : &:r1769_4, ~m? # 1763| r1763_6(bool) = CompareNE : r1763_4, r1763_5
# 1769| mu1769_6(int) = Store[global_6] : &:r1769_3, r1769_5
# 1769| v1769_7(void) = ReturnVoid : # 1759| Block 3
# 1769| v1769_8(void) = AliasedUse : ~m? # 1759| r1759_1(glval<int>) = VariableAddress[x] :
# 1769| v1769_9(void) = ExitFunction : # 1759| r1759_2(int) = Load[x] : &:r1759_1, ~m?
# 1759| r1759_3(glval<int>) = VariableAddress :
# 1759| r1759_4(int) = Load[?] : &:r1759_3, ~m?
# 1759| r1759_5(int) = Add : r1759_2, r1759_4
# 1759| r1759_6(glval<int>) = VariableAddress[x] :
# 1759| mu1759_7(int) = Store[x] : &:r1759_6, r1759_5
#-----| Goto -> Block 4
# 1762| Block 4
# 1762| r1762_1(glval<int>) = VariableAddress[w] :
# 1762| mu1762_2(int) = Uninitialized[w] : &:r1762_1
# 1764| Block 5
# 1764| r1764_1(glval<int>) = VariableAddress[x] :
# 1764| r1764_2(int) = Load[x] : &:r1764_1, ~m?
# 1764| r1764_3(glval<int>) = VariableAddress[w] :
# 1764| r1764_4(int) = Load[w] : &:r1764_3, ~m?
# 1764| r1764_5(int) = Add : r1764_2, r1764_4
# 1764| r1764_6(glval<int>) = VariableAddress[x] :
# 1764| mu1764_7(int) = Store[x] : &:r1764_6, r1764_5
#-----| Goto -> Block 6
# 1767| Block 6
# 1767| r1767_1(glval<int>) = VariableAddress[w2] :
# 1767| r1767_2(glval<int>) = VariableAddress[w] :
# 1767| r1767_3(int) = Load[w] : &:r1767_2, ~m?
# 1767| mu1767_4(int) = Store[w2] : &:r1767_1, r1767_3
# 1767| r1767_5(glval<int>) = VariableAddress[w2] :
# 1767| r1767_6(int) = Load[w2] : &:r1767_5, ~m?
# 1767| r1767_7(int) = Constant[0] :
# 1767| r1767_8(bool) = CompareNE : r1767_6, r1767_7
# 1767| r1767_9(bool) = CopyValue : r1767_8
# 1767| v1767_10(void) = ConditionalBranch : r1767_9
#-----| False -> Block 8
#-----| True -> Block 7
# 1768| Block 7
# 1768| r1768_1(glval<int>) = VariableAddress[x] :
# 1768| r1768_2(int) = Load[x] : &:r1768_1, ~m?
# 1768| r1768_3(glval<int>) = VariableAddress[w] :
# 1768| r1768_4(int) = Load[w] : &:r1768_3, ~m?
# 1768| r1768_5(int) = Add : r1768_2, r1768_4
# 1768| r1768_6(glval<int>) = VariableAddress[x] :
# 1768| mu1768_7(int) = Store[x] : &:r1768_6, r1768_5
#-----| Goto -> Block 8
# 1771| Block 8
# 1771| r1771_1(glval<int>) = VariableAddress[v2] :
# 1771| r1771_2(glval<int>) = VariableAddress :
# 1771| r1771_3(int) = Load[?] : &:r1771_2, ~m?
# 1771| mu1771_4(int) = Store[v2] : &:r1771_1, r1771_3
# 1771| r1771_5(glval<int>) = VariableAddress[v2] :
# 1771| r1771_6(int) = Load[v2] : &:r1771_5, ~m?
# 1771| r1771_7(int) = Constant[0] :
# 1771| r1771_8(bool) = CompareNE : r1771_6, r1771_7
# 1771| r1771_9(bool) = CopyValue : r1771_8
# 1771| v1771_10(void) = ConditionalBranch : r1771_9
#-----| False -> Block 10
#-----| True -> Block 9
# 1772| Block 9
# 1772| r1772_1(glval<int>) = VariableAddress[x] :
# 1772| r1772_2(int) = Load[x] : &:r1772_1, ~m?
# 1772| r1772_3(glval<int>) = VariableAddress :
# 1772| r1772_4(int) = Load[?] : &:r1772_3, ~m?
# 1772| r1772_5(int) = Add : r1772_2, r1772_4
# 1772| r1772_6(glval<int>) = VariableAddress[x] :
# 1772| mu1772_7(int) = Store[x] : &:r1772_6, r1772_5
#-----| Goto -> Block 10
# 1775| Block 10
# 1775| r1775_1(glval<int>) = VariableAddress[z] :
# 1775| r1775_2(glval<int>) = VariableAddress[x] :
# 1775| r1775_3(int) = Load[x] : &:r1775_2, ~m?
# 1775| mu1775_4(int) = Store[z] : &:r1775_1, r1775_3
# 1776| r1776_1(glval<int>) = VariableAddress[z] :
# 1776| r1776_2(int) = Load[z] : &:r1776_1, ~m?
# 1776| r1776_3(int) = Constant[0] :
# 1776| r1776_4(bool) = CompareNE : r1776_2, r1776_3
# 1776| v1776_5(void) = ConditionalBranch : r1776_4
#-----| False -> Block 12
#-----| True -> Block 11
# 1777| Block 11
# 1777| r1777_1(glval<int>) = VariableAddress[x] :
# 1777| r1777_2(int) = Load[x] : &:r1777_1, ~m?
# 1777| r1777_3(glval<int>) = VariableAddress[z] :
# 1777| r1777_4(int) = Load[z] : &:r1777_3, ~m?
# 1777| r1777_5(int) = Add : r1777_2, r1777_4
# 1777| r1777_6(glval<int>) = VariableAddress[x] :
# 1777| mu1777_7(int) = Store[x] : &:r1777_6, r1777_5
#-----| Goto -> Block 12
# 1780| Block 12
# 1780| r1780_1(glval<int>) = VariableAddress[z2] :
# 1780| r1780_2(glval<int>) = VariableAddress[z] :
# 1780| r1780_3(int) = Load[z] : &:r1780_2, ~m?
# 1780| mu1780_4(int) = Store[z2] : &:r1780_1, r1780_3
# 1780| r1780_5(glval<int>) = VariableAddress[z2] :
# 1780| r1780_6(int) = Load[z2] : &:r1780_5, ~m?
# 1780| r1780_7(int) = Constant[0] :
# 1780| r1780_8(bool) = CompareNE : r1780_6, r1780_7
# 1780| r1780_9(bool) = CopyValue : r1780_8
# 1780| v1780_10(void) = ConditionalBranch : r1780_9
#-----| False -> Block 14
#-----| True -> Block 13
# 1781| Block 13
# 1781| r1781_1(glval<int>) = VariableAddress[z2] :
# 1781| r1781_2(int) = Load[z2] : &:r1781_1, ~m?
# 1781| r1781_3(glval<int>) = VariableAddress[x] :
# 1781| r1781_4(int) = Load[x] : &:r1781_3, ~m?
# 1781| r1781_5(int) = Add : r1781_4, r1781_2
# 1781| mu1781_6(int) = Store[x] : &:r1781_3, r1781_5
#-----| Goto -> Block 14
# 1783| Block 14
# 1783| v1783_1(void) = NoOp :
# 1757| v1757_6(void) = ReturnVoid :
# 1757| v1757_7(void) = AliasedUse : ~m?
# 1757| v1757_8(void) = ExitFunction :
# 1785| void switch_initialization(int)
# 1786| (no string representation)
# 1786| CopyValue: (condition decl)
# 1786| Switch: switch (...) ...
#-----| Default -> Block 3
# 1792| (no string representation)
# 1792| CopyValue: (condition decl)
# 1792| Switch: switch (...) ...
#-----| Default -> Block 4
# 1785| Block 0
# 1785| v1785_1(void) = EnterFunction :
# 1785| mu1785_2(unknown) = AliasedDefinition :
# 1785| mu1785_3(unknown) = InitializeNonLocal :
# 1785| r1785_4(glval<int>) = VariableAddress[x] :
# 1785| mu1785_5(int) = InitializeParameter[x] : &:r1785_4
# 1786| Block 1
# 1786| r1786_1(glval<int>) = VariableAddress[x] :
# 1786| r1786_2(int) = Load[x] : &:r1786_1, ~m?
# 1786| r1786_3(int) = Constant[1] :
# 1786| r1786_4(int) = Add : r1786_2, r1786_3
# 1792| Block 1
# 1792| r1792_1(glval<int>) = VariableAddress[x] :
# 1792| r1792_2(int) = Load[x] : &:r1792_1, ~m?
# 1792| r1792_3(int) = Constant[1] :
# 1792| r1792_4(int) = Add : r1792_2, r1792_3
# 1787| Block 3
# 1787| v1787_1(void) = NoOp :
# 1788| r1788_1(glval<int>) = VariableAddress[x] :
# 1788| r1788_2(int) = Load[x] : &:r1788_1, ~m?
# 1788| r1788_3(glval<int>) = VariableAddress :
# 1788| r1788_4(int) = Load[?] : &:r1788_3, ~m?
# 1788| r1788_5(int) = Add : r1788_2, r1788_4
# 1788| r1788_6(glval<int>) = VariableAddress[x] :
# 1788| mu1788_7(int) = Store[x] : &:r1788_6, r1788_5
# 1791| r1791_1(glval<int>) = VariableAddress[w] :
# 1791| mu1791_2(int) = Uninitialized[w] : &:r1791_1
# 1793| Block 4
# 1793| v1793_1(void) = NoOp :
# 1794| r1794_1(glval<int>) = VariableAddress[x] :
# 1794| r1794_2(int) = Load[x] : &:r1794_1, ~m?
# 1794| r1794_3(glval<int>) = VariableAddress[w] :
# 1794| r1794_4(int) = Load[w] : &:r1794_3, ~m?
# 1794| r1794_5(int) = Add : r1794_2, r1794_4
# 1794| r1794_6(glval<int>) = VariableAddress[x] :
# 1794| mu1794_7(int) = Store[x] : &:r1794_6, r1794_5
# 1797| r1797_1(glval<int>) = VariableAddress[w2] :
# 1797| r1797_2(glval<int>) = VariableAddress[w] :
# 1797| r1797_3(int) = Load[w] : &:r1797_2, ~m?
# 1797| mu1797_4(int) = Store[w2] : &:r1797_1, r1797_3
# 1797| r1797_5(glval<int>) = VariableAddress[w2] :
# 1797| r1797_6(int) = Load[w2] : &:r1797_5, ~m?
# 1797| r1797_7(int) = CopyValue : r1797_6
# 1797| v1797_8(void) = Switch : r1797_7
#-----| Default -> Block 5
# 1798| Block 5
# 1798| v1798_1(void) = NoOp :
# 1799| r1799_1(glval<int>) = VariableAddress[x] :
# 1799| r1799_2(int) = Load[x] : &:r1799_1, ~m?
# 1799| r1799_3(glval<int>) = VariableAddress[w] :
# 1799| r1799_4(int) = Load[w] : &:r1799_3, ~m?
# 1799| r1799_5(int) = Add : r1799_2, r1799_4
# 1799| r1799_6(glval<int>) = VariableAddress[x] :
# 1799| mu1799_7(int) = Store[x] : &:r1799_6, r1799_5
# 1802| r1802_1(glval<int>) = VariableAddress[v2] :
# 1802| r1802_2(glval<int>) = VariableAddress :
# 1802| r1802_3(int) = Load[?] : &:r1802_2, ~m?
# 1802| mu1802_4(int) = Store[v2] : &:r1802_1, r1802_3
# 1802| r1802_5(glval<int>) = VariableAddress[v2] :
# 1802| r1802_6(int) = Load[v2] : &:r1802_5, ~m?
# 1802| r1802_7(int) = CopyValue : r1802_6
# 1802| v1802_8(void) = Switch : r1802_7
#-----| Default -> Block 6
# 1803| Block 6
# 1803| v1803_1(void) = NoOp :
# 1804| r1804_1(glval<int>) = VariableAddress[x] :
# 1804| r1804_2(int) = Load[x] : &:r1804_1, ~m?
# 1804| r1804_3(glval<int>) = VariableAddress :
# 1804| r1804_4(int) = Load[?] : &:r1804_3, ~m?
# 1804| r1804_5(int) = Add : r1804_2, r1804_4
# 1804| r1804_6(glval<int>) = VariableAddress[x] :
# 1804| mu1804_7(int) = Store[x] : &:r1804_6, r1804_5
# 1807| r1807_1(glval<int>) = VariableAddress[z] :
# 1807| r1807_2(glval<int>) = VariableAddress[x] :
# 1807| r1807_3(int) = Load[x] : &:r1807_2, ~m?
# 1807| mu1807_4(int) = Store[z] : &:r1807_1, r1807_3
# 1808| r1808_1(glval<int>) = VariableAddress[z] :
# 1808| r1808_2(int) = Load[z] : &:r1808_1, ~m?
# 1808| v1808_3(void) = Switch : r1808_2
#-----| Default -> Block 7
# 1809| Block 7
# 1809| v1809_1(void) = NoOp :
# 1810| r1810_1(glval<int>) = VariableAddress[x] :
# 1810| r1810_2(int) = Load[x] : &:r1810_1, ~m?
# 1810| r1810_3(glval<int>) = VariableAddress[z] :
# 1810| r1810_4(int) = Load[z] : &:r1810_3, ~m?
# 1810| r1810_5(int) = Add : r1810_2, r1810_4
# 1810| r1810_6(glval<int>) = VariableAddress[x] :
# 1810| mu1810_7(int) = Store[x] : &:r1810_6, r1810_5
# 1813| r1813_1(glval<int>) = VariableAddress[z2] :
# 1813| r1813_2(glval<int>) = VariableAddress[z] :
# 1813| r1813_3(int) = Load[z] : &:r1813_2, ~m?
# 1813| mu1813_4(int) = Store[z2] : &:r1813_1, r1813_3
# 1813| r1813_5(glval<int>) = VariableAddress[z2] :
# 1813| r1813_6(int) = Load[z2] : &:r1813_5, ~m?
# 1813| r1813_7(int) = CopyValue : r1813_6
# 1813| v1813_8(void) = Switch : r1813_7
#-----| Default -> Block 8
# 1814| Block 8
# 1814| v1814_1(void) = NoOp :
# 1815| r1815_1(glval<int>) = VariableAddress[z2] :
# 1815| r1815_2(int) = Load[z2] : &:r1815_1, ~m?
# 1815| r1815_3(glval<int>) = VariableAddress[x] :
# 1815| r1815_4(int) = Load[x] : &:r1815_3, ~m?
# 1815| r1815_5(int) = Add : r1815_4, r1815_2
# 1815| mu1815_6(int) = Store[x] : &:r1815_3, r1815_5
# 1817| v1817_1(void) = NoOp :
# 1785| v1785_6(void) = ReturnVoid :
# 1785| v1785_7(void) = AliasedUse : ~m?
# 1785| v1785_8(void) = ExitFunction :
# 1821| int global_2
# 1821| Block 0
# 1821| v1821_1(void) = EnterFunction :
# 1821| mu1821_2(unknown) = AliasedDefinition :
# 1821| r1821_3(glval<int>) = VariableAddress[global_2] :
# 1821| r1821_4(int) = Constant[1] :
# 1821| mu1821_5(int) = Store[global_2] : &:r1821_3, r1821_4
# 1821| v1821_6(void) = ReturnVoid :
# 1821| v1821_7(void) = AliasedUse : ~m?
# 1821| v1821_8(void) = ExitFunction :
# 1825| constructor_only global_4
# 1825| Block 0
# 1825| v1825_1(void) = EnterFunction :
# 1825| mu1825_2(unknown) = AliasedDefinition :
# 1825| r1825_3(glval<constructor_only>) = VariableAddress[global_4] :
# 1825| r1825_4(glval<unknown>) = FunctionAddress[constructor_only] :
# 1825| r1825_5(int) = Constant[1] :
# 1825| v1825_6(void) = Call[constructor_only] : func:r1825_4, this:r1825_3, 0:r1825_5
# 1825| mu1825_7(unknown) = ^CallSideEffect : ~m?
# 1825| mu1825_8(constructor_only) = ^IndirectMayWriteSideEffect[-1] : &:r1825_3
# 1825| v1825_9(void) = ReturnVoid :
# 1825| v1825_10(void) = AliasedUse : ~m?
# 1825| v1825_11(void) = ExitFunction :
# 1827| constructor_only global_5
# 1827| Block 0
# 1827| v1827_1(void) = EnterFunction :
# 1827| mu1827_2(unknown) = AliasedDefinition :
# 1827| r1827_3(glval<constructor_only>) = VariableAddress[global_5] :
# 1827| r1827_4(glval<unknown>) = FunctionAddress[constructor_only] :
# 1827| r1827_5(int) = Constant[2] :
# 1827| v1827_6(void) = Call[constructor_only] : func:r1827_4, this:r1827_3, 0:r1827_5
# 1827| mu1827_7(unknown) = ^CallSideEffect : ~m?
# 1827| mu1827_8(constructor_only) = ^IndirectMayWriteSideEffect[-1] : &:r1827_3
# 1827| v1827_9(void) = ReturnVoid :
# 1827| v1827_10(void) = AliasedUse : ~m?
# 1827| v1827_11(void) = ExitFunction :
# 1829| char* global_string
# 1829| Block 0
# 1829| v1829_1(void) = EnterFunction :
# 1829| mu1829_2(unknown) = AliasedDefinition :
# 1829| r1829_3(glval<char *>) = VariableAddress[global_string] :
# 1829| r1829_4(glval<char[14]>) = StringConstant["global string"] :
# 1829| r1829_5(char *) = Convert : r1829_4
# 1829| r1829_6(char *) = Convert : r1829_5
# 1829| mu1829_7(char *) = Store[global_string] : &:r1829_3, r1829_6
# 1829| v1829_8(void) = ReturnVoid :
# 1829| v1829_9(void) = AliasedUse : ~m?
# 1829| v1829_10(void) = ExitFunction :
# 1831| int global_6
# 1831| Block 0
# 1831| v1831_1(void) = EnterFunction :
# 1831| mu1831_2(unknown) = AliasedDefinition :
# 1831| r1831_3(glval<int>) = VariableAddress[global_6] :
# 1831| r1831_4(glval<int>) = VariableAddress[global_2] :
# 1831| r1831_5(int) = Load[global_2] : &:r1831_4, ~m?
# 1831| mu1831_6(int) = Store[global_6] : &:r1831_3, r1831_5
# 1831| v1831_7(void) = ReturnVoid :
# 1831| v1831_8(void) = AliasedUse : ~m?
# 1831| v1831_9(void) = ExitFunction :
perf-regression.cpp: perf-regression.cpp:
# 6| void Big::Big() # 6| void Big::Big()

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