mirror of
https://github.com/github/codeql.git
synced 2026-05-25 00:27:09 +02:00
Compare commits
70 Commits
revert-465
...
v1.26.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a165d2e559 | ||
|
|
38085b27c3 | ||
|
|
7f79b28a11 | ||
|
|
2c385f7121 | ||
|
|
915d640274 | ||
|
|
eabfeca514 | ||
|
|
ebdb3e25aa | ||
|
|
45a4d5beb9 | ||
|
|
d0a2582673 | ||
|
|
d19d23cc5e | ||
|
|
f3642fcec2 | ||
|
|
1486aef5e1 | ||
|
|
0316c673f8 | ||
|
|
ac2d621558 | ||
|
|
18fc33c78c | ||
|
|
f659e6c9df | ||
|
|
e346b479d2 | ||
|
|
5d717a53fa | ||
|
|
b62d01a74f | ||
|
|
8c4c6501ee | ||
|
|
2484941330 | ||
|
|
771425e860 | ||
|
|
38ab87e5b1 | ||
|
|
91c96ada4f | ||
|
|
19d334c577 | ||
|
|
d08b25c65c | ||
|
|
2140a03c2e | ||
|
|
65a048b65c | ||
|
|
f7fe7c03b8 | ||
|
|
5002968e86 | ||
|
|
9897a81a5c | ||
|
|
4d8983830e | ||
|
|
8b2c34e267 | ||
|
|
509d153b61 | ||
|
|
fd8c696b67 | ||
|
|
4cea019cee | ||
|
|
e50e2f8c4f | ||
|
|
a10fe44afa | ||
|
|
d38e297ef9 | ||
|
|
b5626cbb70 | ||
|
|
9702326232 | ||
|
|
dcf52f3ee3 | ||
|
|
f5ae00865f | ||
|
|
f2b177413a | ||
|
|
13c72d243a | ||
|
|
bc7264cd5d | ||
|
|
4a9b61274a | ||
|
|
e5d2edd911 | ||
|
|
8414c22f67 | ||
|
|
78fc15174f | ||
|
|
d6e9f4d6f2 | ||
|
|
4b07f395d0 | ||
|
|
d70240c786 | ||
|
|
cb962a9ce0 | ||
|
|
0fe0d067e9 | ||
|
|
27f52851ca | ||
|
|
2383960e0d | ||
|
|
e9b2d771c2 | ||
|
|
bb1c0a184a | ||
|
|
1a60f961e6 | ||
|
|
fc848e553e | ||
|
|
fe5979d92a | ||
|
|
d25a0ef7e6 | ||
|
|
c775a27a22 | ||
|
|
2d93b3a45a | ||
|
|
8504724dbb | ||
|
|
47483a8e84 | ||
|
|
6667b58b2c | ||
|
|
710e675b17 | ||
|
|
10de931b92 |
57
.github/workflows/generate-query-help-docs.yml
vendored
Normal file
57
.github/workflows/generate-query-help-docs.yml
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
name: Generate CodeQL query help documentation using Sphinx
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 'rc/**'
|
||||
- 'lgtm.com'
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/generate-query-help-docs.yml'
|
||||
- 'docs/codeql/query-help/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Clone github/codeql
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: codeql
|
||||
- name: Clone github/codeql-go
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: 'github/codeql-go'
|
||||
path: codeql-go
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.8
|
||||
- name: Download CodeQL CLI
|
||||
uses: dsaltares/fetch-gh-release-asset@aa37ae5c44d3c9820bc12fe675e8670ecd93bd1c
|
||||
with:
|
||||
repo: "github/codeql-cli-binaries"
|
||||
version: "latest"
|
||||
file: "codeql-linux64.zip"
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Unzip CodeQL CLI
|
||||
run: unzip -d codeql-cli codeql-linux64.zip
|
||||
- name: Set up query help docs folder
|
||||
run: |
|
||||
cp -r codeql/docs/codeql/** .
|
||||
- name: Query help to markdown
|
||||
run: |
|
||||
PATH="$PATH:codeql-cli/codeql" python codeql/docs/codeql/query-help-markdown.py
|
||||
- name: Run Sphinx for query help
|
||||
uses: ammaraskar/sphinx-action@8b4f60114d7fd1faeba1a712269168508d4750d2 # v0.4
|
||||
with:
|
||||
docs-folder: "query-help/"
|
||||
pre-build-command: "python -m pip install --upgrade recommonmark"
|
||||
build-command: "sphinx-build -b dirhtml . _build"
|
||||
- name: Upload HTML artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: query-help-html
|
||||
path: query-help/_build
|
||||
|
||||
@@ -4,19 +4,34 @@ The following changes in version 1.26 affect Python analysis in all applications
|
||||
|
||||
## General improvements
|
||||
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|----------------------------|------------------------|------------------------------------------------------------------|
|
||||
|
||||
|
||||
|`py/unsafe-deserialization` | Different results. | The underlying data flow library has been changed. See below for more details. |
|
||||
|`py/path-injection` | Different results. | The underlying data flow library has been changed. See below for more details. |
|
||||
|`py/command-line-injection` | Different results. | The underlying data flow library has been changed. See below for more details. |
|
||||
|`py/reflective-xss` | Different results. | The underlying data flow library has been changed. See below for more details. |
|
||||
|`py/sql-injection` | Different results. | The underlying data flow library has been changed. See below for more details. |
|
||||
|`py/code-injection` | Different results. | The underlying data flow library has been changed. See below for more details. |
|
||||
## Changes to libraries
|
||||
|
||||
* Some of the security queries now use the shared data flow library for data flow and taint tracking. This has resulted in an overall more robust and accurate analysis. The libraries mentioned below have been modelled in this new framework. Other libraries (e.g. the web framework `CherryPy`) have not been modelled yet, and this may lead to a temporary loss of results for these frameworks.
|
||||
* Improved modelling of the following serialization libraries:
|
||||
- `PyYAML`
|
||||
- `dill`
|
||||
- `pickle`
|
||||
- `marshal`
|
||||
* Improved modelling of the following web frameworks:
|
||||
- `Django` (Note that modelling of class-based response handlers is currently incomplete.)
|
||||
- `Flask`
|
||||
* Support for Werkzeug `MultiDict`.
|
||||
* Support for the [Python Database API Specification v2.0 (PEP-249)](https://www.python.org/dev/peps/pep-0249/), including the following libraries:
|
||||
- `MySQLdb`
|
||||
- `mysql-connector-python`
|
||||
- `django.db`
|
||||
* Improved modelling of the following command execution libraries:
|
||||
- `Fabric`
|
||||
- `Invoke`
|
||||
* Improved modelling of security-related standard library modules, such as `os`, `popen2`, `platform`, and `base64`.
|
||||
* The original versions of the updated queries have been preserved [here](https://github.com/github/codeql/tree/main/python/ql/src/experimental/Security-old-dataflow).
|
||||
* Added taint tracking support for string formatting through f-strings.
|
||||
|
||||
@@ -358,14 +358,6 @@
|
||||
"cpp/ql/test/TestUtilities/InlineExpectationsTest.qll",
|
||||
"python/ql/test/TestUtilities/InlineExpectationsTest.qll"
|
||||
],
|
||||
"C++ ExternalAPIs": [
|
||||
"cpp/ql/src/Security/CWE/CWE-020/ExternalAPIs.qll",
|
||||
"cpp/ql/src/Security/CWE/CWE-020/ir/ExternalAPIs.qll"
|
||||
],
|
||||
"C++ SafeExternalAPIFunction": [
|
||||
"cpp/ql/src/Security/CWE/CWE-020/SafeExternalAPIFunction.qll",
|
||||
"cpp/ql/src/Security/CWE/CWE-020/ir/SafeExternalAPIFunction.qll"
|
||||
],
|
||||
"XML": [
|
||||
"cpp/ql/src/semmle/code/cpp/XML.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/XML.qll",
|
||||
@@ -417,12 +409,5 @@
|
||||
"java/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp",
|
||||
"javascript/ql/src/Comments/CommentedOutCodeReferences.qhelp",
|
||||
"python/ql/src/Lexical/CommentedOutCodeReferences.qhelp"
|
||||
],
|
||||
"IDE Contextual Queries": [
|
||||
"cpp/ql/src/IDEContextual.qll",
|
||||
"csharp/ql/src/IDEContextual.qll",
|
||||
"java/ql/src/IDEContextual.qll",
|
||||
"javascript/ql/src/IDEContextual.qll",
|
||||
"python/ql/src/analysis/IDEContextual.qll"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* Two issues causing the 'Unused local variable' query (`cpp/unused-local-variable`) to produce false positive results have been fixed.
|
||||
@@ -1,3 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* Various classes in `semmle.code.cpp.models.implementations` have been made private. Users should not depend on library implementation details.
|
||||
* The `OperatorNewAllocationFunction`, `OperatorDeleteDeallocationFunction`, `Iterator` and `Snprintf` classes now have interfaces in `semmle.code.cpp.models.interfaces`.
|
||||
@@ -1,2 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* A new query (`cpp/unsafe-use-of-this`) has been added. The query finds pure virtual function calls whose qualifier is an object under construction.
|
||||
@@ -1,2 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* The queries `cpp/local-variable-hides-global-variable` and `cpp/missing-header-guard` now have severity `recommendation` instead of `warning`.
|
||||
@@ -9,7 +9,6 @@
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql: /Correctness/Dangerous Conversions
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.ql: /Correctness/Dangerous Conversions
|
||||
+ semmlecode-cpp-queries/Security/CWE/CWE-253/HResultBooleanConversion.ql: /Correctness/Dangerous Conversions
|
||||
+ semmlecode-cpp-queries/Likely Bugs/OO/UnsafeUseOfThis.ql: /Correctness/Dangerous Conversions
|
||||
# Consistent Use
|
||||
+ semmlecode-cpp-queries/Critical/ReturnValueIgnored.ql: /Correctness/Consistent Use
|
||||
+ semmlecode-cpp-queries/Likely Bugs/InconsistentCheckReturnNull.ql: /Correctness/Consistent Use
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @name Local variable hides global variable
|
||||
* @description A local variable or parameter that hides a global variable of the same name. This may be confusing. Consider renaming one of the variables.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @problem.severity warning
|
||||
* @precision very-high
|
||||
* @id cpp/local-variable-hides-global-variable
|
||||
* @tags maintainability
|
||||
|
||||
@@ -57,12 +57,5 @@ where
|
||||
not declarationHasSideEffects(v) and
|
||||
not exists(AsmStmt s | f = s.getEnclosingFunction()) and
|
||||
not v.getAnAttribute().getName() = "unused" and
|
||||
not any(ErrorExpr e).getEnclosingFunction() = f and // unextracted expr may use `v`
|
||||
not exists(
|
||||
Literal l // this case can be removed when the `myFunction2( [obj](){} );` test case doesn't depend on this exclusion
|
||||
|
|
||||
l.getEnclosingFunction() = f and
|
||||
not exists(l.getValue())
|
||||
) and
|
||||
not any(ConditionDeclExpr cde).getEnclosingFunction() = f // this case can be removed when the `if (a = b; a)` test case doesn't depend on this exclusion
|
||||
not any(ErrorExpr e).getEnclosingFunction() = f // unextracted expr likely used `v`
|
||||
select v, "Variable " + v.getName() + " is not used"
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.SSA
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.models.implementations.Allocation
|
||||
import semmle.code.cpp.models.implementations.Deallocation
|
||||
|
||||
/**
|
||||
* Holds if `alloc` is a use of `malloc` or `new`. `kind` is
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/**
|
||||
* Provides shared predicates related to contextual queries in the code viewer.
|
||||
*/
|
||||
|
||||
import semmle.files.FileSystem
|
||||
|
||||
/**
|
||||
* Returns the `File` matching the given source file name as encoded by the VS
|
||||
* Code extension.
|
||||
*/
|
||||
cached
|
||||
File getFileBySourceArchiveName(string name) {
|
||||
// The name provided for a file in the source archive by the VS Code extension
|
||||
// has some differences from the absolute path in the database:
|
||||
// 1. colons are replaced by underscores
|
||||
// 2. there's a leading slash, even for Windows paths: "C:/foo/bar" ->
|
||||
// "/C_/foo/bar"
|
||||
// 3. double slashes in UNC prefixes are replaced with a single slash
|
||||
// We can handle 2 and 3 together by unconditionally adding a leading slash
|
||||
// before replacing double slashes.
|
||||
name = ("/" + result.getAbsolutePath().replaceAll(":", "_")).replaceAll("//", "/")
|
||||
}
|
||||
@@ -50,12 +50,10 @@ class SafeTimeGatheringFunction extends Function {
|
||||
class TimeConversionFunction extends Function {
|
||||
TimeConversionFunction() {
|
||||
this.getQualifiedName() =
|
||||
[
|
||||
"FileTimeToSystemTime", "SystemTimeToFileTime", "SystemTimeToTzSpecificLocalTime",
|
||||
"SystemTimeToTzSpecificLocalTimeEx", "TzSpecificLocalTimeToSystemTime",
|
||||
"TzSpecificLocalTimeToSystemTimeEx", "RtlLocalTimeToSystemTime",
|
||||
"RtlTimeToSecondsSince1970", "_mkgmtime"
|
||||
]
|
||||
["FileTimeToSystemTime", "SystemTimeToFileTime", "SystemTimeToTzSpecificLocalTime",
|
||||
"SystemTimeToTzSpecificLocalTimeEx", "TzSpecificLocalTimeToSystemTime",
|
||||
"TzSpecificLocalTimeToSystemTimeEx", "RtlLocalTimeToSystemTime",
|
||||
"RtlTimeToSecondsSince1970", "_mkgmtime"]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
class Base {
|
||||
private:
|
||||
// pure virtual member function used for initialization of derived classes.
|
||||
virtual void construct() = 0;
|
||||
public:
|
||||
Base() {
|
||||
// wrong: the virtual table of `Derived` has not been initialized yet. So this
|
||||
// call will resolve to `Base::construct`, which cannot be called as it is a pure
|
||||
// virtual function.
|
||||
construct();
|
||||
}
|
||||
};
|
||||
|
||||
class Derived : public Base {
|
||||
int field;
|
||||
|
||||
void construct() override {
|
||||
field = 1;
|
||||
}
|
||||
};
|
||||
@@ -1,30 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This rule finds calls to pure virtual member functions in constructors and destructors. When executing the body of a constructor of class <code>T</code>, the virtual table of <code>T</code> refers to the virtual table of one of <code>T</code>'s base classes. This can produce unexpected behavior, including program abort that can lead to denial of service attacks. The same problem exists during destruction of an object.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Do not rely on virtual dispatch in constructors and destructors. Instead, each class should be responsible for acquiring and releasing its resources. If a base class needs to refer to a derived class during initialization, use the Dynamic Binding During Initialization idiom.</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="UnsafeUseOfThis.cpp" />
|
||||
|
||||
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>ISO C++ FAQ: <a href="https://isocpp.org/wiki/faq/strange-inheritance#calling-virtuals-from-ctors">When my base class's constructor calls a virtual function on its this object, why doesn't my derived class's override of that virtual function get invoked?</a>
|
||||
</li>
|
||||
<li>SEI CERT C++ Coding Standard <a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/OOP50-CPP.+Do+not+invoke+virtual+functions+from+constructors+or+destructors">OOP50-CPP. Do not invoke virtual functions from constructors or destructors</a>
|
||||
</li>
|
||||
<li>ISO C++ FAQ: <a href="https://isocpp.org/wiki/faq/strange-inheritance#calling-virtuals-from-ctor-idiom">Okay, but is there a way to simulate that behavior as if dynamic binding worked on the this object within my base class's constructor?</a>
|
||||
</li>
|
||||
|
||||
|
||||
</references></qhelp>
|
||||
@@ -1,212 +0,0 @@
|
||||
/**
|
||||
* @name Unsafe use of this in constructor
|
||||
* @description A call to a pure virtual function using a 'this'
|
||||
* pointer of an object that is under construction
|
||||
* may lead to undefined behavior.
|
||||
* @kind path-problem
|
||||
* @id cpp/unsafe-use-of-this
|
||||
* @problem.severity error
|
||||
* @precision very-high
|
||||
* @tags correctness
|
||||
* language-features
|
||||
* security
|
||||
*/
|
||||
|
||||
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
|
||||
private import semmle.code.cpp.ir.IR
|
||||
|
||||
bindingset[n, result]
|
||||
int unbind(int n) { result >= n and result <= n }
|
||||
|
||||
/** Holds if `p` is the `n`'th parameter of the non-virtual function `f`. */
|
||||
predicate parameterOf(Parameter p, Function f, int n) {
|
||||
not f.isVirtual() and f.getParameter(n) = p
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is the `n`'th argument to a call to the non-virtual function `f`, and
|
||||
* `init` is the corresponding initiazation instruction that receives the value of `instr` in `f`.
|
||||
*/
|
||||
predicate flowIntoParameter(
|
||||
CallInstruction call, Instruction instr, Function f, int n, InitializeParameterInstruction init
|
||||
) {
|
||||
not f.isVirtual() and
|
||||
call.getPositionalArgument(n) = instr and
|
||||
f = call.getStaticCallTarget() and
|
||||
getEnclosingNonVirtualFunctionInitializeParameter(init, f) and
|
||||
init.getParameter().getIndex() = unbind(n)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is an argument to a call to the function `f`, and `init` is the
|
||||
* corresponding initialization instruction that receives the value of `instr` in `f`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate getPositionalArgumentInitParam(
|
||||
CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f
|
||||
) {
|
||||
exists(int n |
|
||||
parameterOf(_, f, n) and
|
||||
flowIntoParameter(call, instr, f, unbind(n), init)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is the qualifier to a call to the non-virtual function `f`, and
|
||||
* `init` is the corresponding initiazation instruction that receives the value of
|
||||
* `instr` in `f`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate getThisArgumentInitParam(
|
||||
CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f
|
||||
) {
|
||||
not f.isVirtual() and
|
||||
call.getStaticCallTarget() = f and
|
||||
getEnclosingNonVirtualFunctionInitializeParameter(init, f) and
|
||||
call.getThisArgument() = instr and
|
||||
init.getIRVariable() instanceof IRThisVariable
|
||||
}
|
||||
|
||||
/** Holds if `instr` is a `this` pointer used by the call instruction `call`. */
|
||||
predicate isSink(Instruction instr, CallInstruction call) {
|
||||
exists(PureVirtualFunction func |
|
||||
call.getStaticCallTarget() = func and
|
||||
call.getThisArgument() = instr and
|
||||
// Weed out implicit calls to destructors of a base class
|
||||
not func instanceof Destructor
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `init` initializes the `this` pointer in class `c`. */
|
||||
predicate isSource(InitializeParameterInstruction init, string msg, Class c) {
|
||||
(
|
||||
exists(Constructor func |
|
||||
not func instanceof CopyConstructor and
|
||||
not func instanceof MoveConstructor and
|
||||
func = init.getEnclosingFunction() and
|
||||
msg = "construction"
|
||||
)
|
||||
or
|
||||
init.getEnclosingFunction() instanceof Destructor and msg = "destruction"
|
||||
) and
|
||||
init.getIRVariable() instanceof IRThisVariable and
|
||||
init.getEnclosingFunction().getDeclaringType() = c
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` flows to a sink (which is a use of the value of `instr` as a `this` pointer).
|
||||
*/
|
||||
predicate flowsToSink(Instruction instr, Instruction sink) {
|
||||
flowsFromSource(instr) and
|
||||
(
|
||||
isSink(instr, _) and instr = sink
|
||||
or
|
||||
exists(Instruction mid |
|
||||
successor(instr, mid) and
|
||||
flowsToSink(mid, sink)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `instr` flows from a source. */
|
||||
predicate flowsFromSource(Instruction instr) {
|
||||
isSource(instr, _, _)
|
||||
or
|
||||
exists(Instruction mid |
|
||||
successor(mid, instr) and
|
||||
flowsFromSource(mid)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `f` is the enclosing non-virtual function of `init`. */
|
||||
predicate getEnclosingNonVirtualFunctionInitializeParameter(
|
||||
InitializeParameterInstruction init, Function f
|
||||
) {
|
||||
not f.isVirtual() and
|
||||
init.getEnclosingFunction() = f
|
||||
}
|
||||
|
||||
/** Holds if `f` is the enclosing non-virtual function of `init`. */
|
||||
predicate getEnclosingNonVirtualFunctionInitializeIndirection(
|
||||
InitializeIndirectionInstruction init, Function f
|
||||
) {
|
||||
not f.isVirtual() and
|
||||
init.getEnclosingFunction() = f
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is an argument (or argument indirection) to a call, and
|
||||
* `succ` is the corresponding initialization instruction in the call target.
|
||||
*/
|
||||
predicate flowThroughCallable(Instruction instr, Instruction succ) {
|
||||
// Flow from an argument to a parameter
|
||||
exists(CallInstruction call, InitializeParameterInstruction init | init = succ |
|
||||
getPositionalArgumentInitParam(call, instr, init, call.getStaticCallTarget())
|
||||
or
|
||||
getThisArgumentInitParam(call, instr, init, call.getStaticCallTarget())
|
||||
)
|
||||
or
|
||||
// Flow from argument indirection to parameter indirection
|
||||
exists(
|
||||
CallInstruction call, ReadSideEffectInstruction read, InitializeIndirectionInstruction init
|
||||
|
|
||||
init = succ and
|
||||
read.getPrimaryInstruction() = call and
|
||||
getEnclosingNonVirtualFunctionInitializeIndirection(init, call.getStaticCallTarget())
|
||||
|
|
||||
exists(int n |
|
||||
read.getSideEffectOperand().getAnyDef() = instr and
|
||||
read.getIndex() = n and
|
||||
init.getParameter().getIndex() = unbind(n)
|
||||
)
|
||||
or
|
||||
call.getThisArgument() = instr and
|
||||
init.getIRVariable() instanceof IRThisVariable
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `instr` flows to `succ`. */
|
||||
predicate successor(Instruction instr, Instruction succ) {
|
||||
succ.(CopyInstruction).getSourceValue() = instr or
|
||||
succ.(CheckedConvertOrNullInstruction).getUnary() = instr or
|
||||
succ.(ChiInstruction).getTotal() = instr or
|
||||
succ.(ConvertInstruction).getUnary() = instr or
|
||||
succ.(InheritanceConversionInstruction).getUnary() = instr or
|
||||
flowThroughCallable(instr, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if:
|
||||
* - `source` is an initialization of a `this` pointer of type `sourceClass`, and
|
||||
* - `sink` is a use of the `this` pointer, and
|
||||
* - `call` invokes a pure virtual function using `sink` as the `this` pointer, and
|
||||
* - `msg` is a string describing whether `source` is from a constructor or destructor.
|
||||
*/
|
||||
predicate flows(
|
||||
Instruction source, string msg, Class sourceClass, Instruction sink, CallInstruction call
|
||||
) {
|
||||
isSource(source, msg, sourceClass) and
|
||||
flowsToSink(source, sink) and
|
||||
isSink(sink, call)
|
||||
}
|
||||
|
||||
query predicate edges(Instruction a, Instruction b) { successor(a, b) and flowsToSink(b, _) }
|
||||
|
||||
query predicate nodes(Instruction n, string key, string val) {
|
||||
flowsToSink(n, _) and
|
||||
key = "semmle.label" and
|
||||
val = n.toString()
|
||||
}
|
||||
|
||||
from Instruction source, Instruction sink, CallInstruction call, string msg, Class sourceClass
|
||||
where
|
||||
flows(source, msg, sourceClass, sink, call) and
|
||||
// Only raise an alert if there is no override of the pure virtual function in any base class.
|
||||
not exists(Class c | c = sourceClass.getABaseClass*() |
|
||||
c.getAMemberFunction().getAnOverriddenFunction() = call.getStaticCallTarget()
|
||||
)
|
||||
select call.getUnconvertedResultExpression(), source, sink,
|
||||
"Call to pure virtual function during " + msg
|
||||
@@ -1,48 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using unsanitized untrusted data in an external API can cause a variety of security issues. This query reports
|
||||
all external APIs that are used with untrusted data, along with how frequently the API is used, and how many
|
||||
unique sources of untrusted data flow to this API. This query is designed primarily to help identify which APIs
|
||||
may be relevant for security analysis of this application.</p>
|
||||
|
||||
<p>An external API is defined as a call to a function that is not defined in the source code, and is not
|
||||
modeled as a taint step in the default taint library. External APIs may be from the C++ standard library,
|
||||
third party dependencies or from internal dependencies. The query will report the function name, along with
|
||||
either <code>[param x]</code>, where <code>x</code> indicates the position of the parameter receiving the
|
||||
untrusted data or <code>[qualifier]</code> indicating the untrusted data is used as the qualifier to the
|
||||
function call.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>For each result:</p>
|
||||
|
||||
<ul>
|
||||
<li>If the result highlights a known sink, no action is required.</li>
|
||||
<li>If the result highlights an unknown sink for a problem, then add modeling for the sink to the relevant query.</li>
|
||||
<li>If the result represents a call to an external API which transfers taint, add the appropriate modeling, and
|
||||
re-run the query to determine what new results have appeared due to this additional modeling.</li>
|
||||
</ul>
|
||||
|
||||
<p>Otherwise, the result is likely uninteresting. Custom versions of this query can extend the <code>SafeExternalAPIFunction</code>
|
||||
class to exclude known safe external APIs from future analysis.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>If the query were to return the API <code>fputs [param 1]</code>
|
||||
then we should first consider whether this a security relevant sink. In this case, this is writing to a <code>FILE*</code>, so we should
|
||||
consider whether this is an XSS sink. If it is, we should confirm that it is handled by the XSS query.</p>
|
||||
|
||||
<p>If the query were to return the API <code>strcat [param 1]</code>, then this should be
|
||||
reviewed as a possible taint step, because tainted data would flow from the 1st argument to the 0th argument of the call.</p>
|
||||
|
||||
<p>Note that both examples are correctly handled by the standard taint tracking library and XSS query.</p>
|
||||
</example>
|
||||
<references>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,17 +0,0 @@
|
||||
/**
|
||||
* @name Frequency counts for external APIs that are used with untrusted data
|
||||
* @description This reports the external APIs that are used with untrusted data, along with how
|
||||
* frequently the API is called, and how many unique sources of untrusted data flow
|
||||
* to it.
|
||||
* @id cpp/count-untrusted-data-external-api
|
||||
* @kind table
|
||||
* @tags security external/cwe/cwe-20
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import ExternalAPIs
|
||||
|
||||
from ExternalAPIUsedWithUntrustedData externalAPI
|
||||
select externalAPI, count(externalAPI.getUntrustedDataNode()) as numberOfUses,
|
||||
externalAPI.getNumberOfUntrustedSources() as numberOfUntrustedSources order by
|
||||
numberOfUntrustedSources desc
|
||||
@@ -1,13 +0,0 @@
|
||||
#include <cstdio>
|
||||
|
||||
void do_get(FILE* request, FILE* response) {
|
||||
char page[1024];
|
||||
fgets(page, 1024, request);
|
||||
|
||||
char buffer[1024];
|
||||
strcat(buffer, "The page \"");
|
||||
strcat(buffer, page);
|
||||
strcat(buffer, "\" was not found.");
|
||||
|
||||
fputs(buffer, response);
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
#include <cstdio>
|
||||
|
||||
void do_get(FILE* request, FILE* response) {
|
||||
char user_id[1024];
|
||||
fgets(user_id, 1024, request);
|
||||
|
||||
char buffer[1024];
|
||||
strcat(buffer, "SELECT * FROM user WHERE user_id='");
|
||||
strcat(buffer, user_id);
|
||||
strcat(buffer, "'");
|
||||
|
||||
// ...
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/**
|
||||
* Definitions for reasoning about untrusted data used in APIs defined outside the
|
||||
* database.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.models.interfaces.Taint
|
||||
import ExternalAPIsSpecific
|
||||
|
||||
/** A node representing untrusted data being passed to an external API. */
|
||||
class UntrustedExternalAPIDataNode extends ExternalAPIDataNode {
|
||||
UntrustedExternalAPIDataNode() { any(UntrustedDataToExternalAPIConfig c).hasFlow(_, this) }
|
||||
|
||||
/** Gets a source of untrusted data which is passed to this external API data node. */
|
||||
DataFlow::Node getAnUntrustedSource() {
|
||||
any(UntrustedDataToExternalAPIConfig c).hasFlow(result, this)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TExternalAPI =
|
||||
TExternalAPIParameter(Function f, int index) {
|
||||
exists(UntrustedExternalAPIDataNode n |
|
||||
f = n.getExternalFunction() and
|
||||
index = n.getIndex()
|
||||
)
|
||||
}
|
||||
|
||||
/** An external API which is used with untrusted data. */
|
||||
class ExternalAPIUsedWithUntrustedData extends TExternalAPI {
|
||||
/** Gets a possibly untrusted use of this external API. */
|
||||
UntrustedExternalAPIDataNode getUntrustedDataNode() {
|
||||
this = TExternalAPIParameter(result.getExternalFunction(), result.getIndex())
|
||||
}
|
||||
|
||||
/** Gets the number of untrusted sources used with this external API. */
|
||||
int getNumberOfUntrustedSources() {
|
||||
result = strictcount(getUntrustedDataNode().getAnUntrustedSource())
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
exists(Function f, int index, string indexString |
|
||||
if index = -1 then indexString = "qualifier" else indexString = "param " + index
|
||||
|
|
||||
this = TExternalAPIParameter(f, index) and
|
||||
result = f.toString() + " [" + indexString + "]"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/**
|
||||
* Provides AST-specific definitions for use in the `ExternalAPI` library.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import SafeExternalAPIFunction
|
||||
|
||||
/** A node representing untrusted data being passed to an external API. */
|
||||
class ExternalAPIDataNode extends DataFlow::Node {
|
||||
Call call;
|
||||
int i;
|
||||
|
||||
ExternalAPIDataNode() {
|
||||
// Argument to call to a function
|
||||
(
|
||||
this.asExpr() = call.getArgument(i)
|
||||
or
|
||||
i = -1 and this.asExpr() = call.getQualifier()
|
||||
) and
|
||||
exists(Function f |
|
||||
f = call.getTarget() and
|
||||
// Defined outside the source archive
|
||||
not f.hasDefinition() and
|
||||
// Not already modeled as a dataflow or taint step
|
||||
not f instanceof DataFlowFunction and
|
||||
not f instanceof TaintFunction and
|
||||
// Not a call to a known safe external API
|
||||
not f instanceof SafeExternalAPIFunction
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the called API `Function`. */
|
||||
Function getExternalFunction() { result = call.getTarget() }
|
||||
|
||||
/** Gets the index which is passed untrusted data (where -1 indicates the qualifier). */
|
||||
int getIndex() { result = i }
|
||||
|
||||
/** Gets the description of the function being called. */
|
||||
string getFunctionDescription() { result = getExternalFunction().toString() }
|
||||
}
|
||||
|
||||
/** A configuration for tracking flow from `RemoteFlowSource`s to `ExternalAPIDataNode`s. */
|
||||
class UntrustedDataToExternalAPIConfig extends TaintTracking::Configuration {
|
||||
UntrustedDataToExternalAPIConfig() { this = "UntrustedDataToExternalAPIConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(RemoteFlowFunction remoteFlow |
|
||||
remoteFlow = source.asExpr().(Call).getTarget() and
|
||||
remoteFlow.hasRemoteFlowSource(_, _)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof ExternalAPIDataNode }
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using unsanitized untrusted data in an external API can cause a variety of security issues. This query reports
|
||||
all external APIs that are used with untrusted data, along with how frequently the API is used, and how many
|
||||
unique sources of untrusted data flow to this API. This query is designed primarily to help identify which APIs
|
||||
may be relevant for security analysis of this application.</p>
|
||||
|
||||
<p>An external API is defined as a call to a function that is not defined in the source code, and is not
|
||||
modeled as a taint step in the default taint library. External APIs may be from the C++ standard library,
|
||||
third party dependencies or from internal dependencies. The query will report the function name, along with
|
||||
either <code>[param x]</code>, where <code>x</code> indicates the position of the parameter receiving the
|
||||
untrusted data or <code>[qualifier]</code> indicating the untrusted data is used as the qualifier to the
|
||||
function call.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>For each result:</p>
|
||||
|
||||
<ul>
|
||||
<li>If the result highlights a known sink, no action is required.</li>
|
||||
<li>If the result highlights an unknown sink for a problem, then add modeling for the sink to the relevant query.</li>
|
||||
<li>If the result represents a call to an external API which transfers taint, add the appropriate modeling, and
|
||||
re-run the query to determine what new results have appeared due to this additional modeling.</li>
|
||||
</ul>
|
||||
|
||||
<p>Otherwise, the result is likely uninteresting. Custom versions of this query can extend the <code>SafeExternalAPIFunction</code>
|
||||
class to exclude known safe external APIs from future analysis.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>If the query were to return the API <code>fputs [param 1]</code>
|
||||
then we should first consider whether this a security relevant sink. In this case, this is writing to a <code>FILE*</code>, so we should
|
||||
consider whether this is an XSS sink. If it is, we should confirm that it is handled by the XSS query.</p>
|
||||
|
||||
<p>If the query were to return the API <code>strcat [param 1]</code>, then this should be
|
||||
reviewed as a possible taint step, because tainted data would flow from the 1st argument to the 0th argument of the call.</p>
|
||||
|
||||
<p>Note that both examples are correctly handled by the standard taint tracking library and XSS query.</p>
|
||||
</example>
|
||||
<references>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,17 +0,0 @@
|
||||
/**
|
||||
* @name Frequency counts for external APIs that are used with untrusted data
|
||||
* @description This reports the external APIs that are used with untrusted data, along with how
|
||||
* frequently the API is called, and how many unique sources of untrusted data flow
|
||||
* to it.
|
||||
* @id cpp/count-untrusted-data-external-api-ir
|
||||
* @kind table
|
||||
* @tags security external/cwe/cwe-20
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import ir.ExternalAPIs
|
||||
|
||||
from ExternalAPIUsedWithUntrustedData externalAPI
|
||||
select externalAPI, count(externalAPI.getUntrustedDataNode()) as numberOfUses,
|
||||
externalAPI.getNumberOfUntrustedSources() as numberOfUntrustedSources order by
|
||||
numberOfUntrustedSources desc
|
||||
@@ -1,59 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using unsanitized untrusted data in an external API can cause a variety of security issues. This query reports
|
||||
external APIs that use untrusted data. The results are not filtered, so you can audit all examples.
|
||||
The query provides data for security reviews of the application and you can also use it to identify external APIs
|
||||
that should be modeled as either taint steps, or sinks for specific problems.</p>
|
||||
|
||||
<p>An external API is defined as a call to a function that is not defined in the source code, and is not modeled
|
||||
as a taint step in the default taint library. External APIs may be from the
|
||||
C++ standard library, third-party dependencies or from internal dependencies. The query reports uses of
|
||||
untrusted data in either the qualifier or as one of the arguments of external APIs.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>For each result:</p>
|
||||
|
||||
<ul>
|
||||
<li>If the result highlights a known sink, confirm that the result is reported by the relevant query, or
|
||||
that the result is a false positive because this data is sanitized.</li>
|
||||
<li>If the result highlights an unknown sink for a problem, then add modeling for the sink to the relevant query,
|
||||
and confirm that the result is either found, or is safe due to appropriate sanitization.</li>
|
||||
<li>If the result represents a call to an external API that transfers taint, add the appropriate modeling, and
|
||||
re-run the query to determine what new results have appeared due to this additional modeling.</li>
|
||||
</ul>
|
||||
|
||||
<p>Otherwise, the result is likely uninteresting. Custom versions of this query can extend the <code>SafeExternalAPIFunction</code>
|
||||
class to exclude known safe external APIs from future analysis.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>In this first example, input is read from <code>fgets</code> and then ultimately used in a call to the
|
||||
<code>fputs</code> external API:</p>
|
||||
|
||||
<sample src="ExternalAPISinkExample.cpp" />
|
||||
|
||||
<p>This is an XSS sink. The XSS query should therefore be reviewed to confirm that this sink is appropriately modeled,
|
||||
and if it is, to confirm that the query reports this particular result, or that the result is a false positive due to
|
||||
some existing sanitization.</p>
|
||||
|
||||
<p>In this second example, again a request parameter is read from <code>fgets</code>.</p>
|
||||
|
||||
<sample src="ExternalAPITaintStepExample.cpp" />
|
||||
|
||||
<p>If the query reported the call to <code>strcat</code> on line 9, this would suggest that this external API is
|
||||
not currently modeled as a taint step in the taint tracking library. The next step would be to model this as a taint step, then
|
||||
re-run the query to determine what additional results might be found. In this example, it seems likely that <code>buffer</code>
|
||||
will be executed as an SQL query, potentially leading to an SQL injection vulnerability.</p>
|
||||
|
||||
<p>Note that both examples are correctly handled by the standard taint tracking library and XSS query.</p>
|
||||
</example>
|
||||
<references>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* @name Untrusted data passed to external API
|
||||
* @description Data provided remotely is used in this external API without sanitization, which could be a security risk.
|
||||
* @id cpp/untrusted-data-to-external-api-ir
|
||||
* @kind path-problem
|
||||
* @precision low
|
||||
* @problem.severity error
|
||||
* @tags security external/cwe/cwe-20
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import ir.ExternalAPIs
|
||||
import semmle.code.cpp.security.FlowSources
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from UntrustedDataToExternalAPIConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where config.hasFlowPath(source, sink)
|
||||
select sink, source, sink,
|
||||
"Call to " + sink.getNode().(ExternalAPIDataNode).getExternalFunction().toString() +
|
||||
" with untrusted data from $@.", source, source.getNode().(RemoteFlowSource).getSourceType()
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* Provides a class for modeling external functions that are "safe" from a security perspective.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* A `Function` that is considered a "safe" external API from a security perspective.
|
||||
*/
|
||||
abstract class SafeExternalAPIFunction extends Function { }
|
||||
|
||||
/** The default set of "safe" external APIs. */
|
||||
private class DefaultSafeExternalAPIFunction extends SafeExternalAPIFunction {
|
||||
DefaultSafeExternalAPIFunction() {
|
||||
// If a function does not write to any of its arguments, we consider it safe to
|
||||
// pass untrusted data to it. This means that string functions such as `strcmp`
|
||||
// and `strlen`, as well as memory functions such as `memcmp`, are considered safe.
|
||||
exists(SideEffectFunction model | model = this |
|
||||
model.hasOnlySpecificWriteSideEffects() and
|
||||
not model.hasSpecificWriteSideEffect(_, _, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using unsanitized untrusted data in an external API can cause a variety of security issues. This query reports
|
||||
external APIs that use untrusted data. The results are not filtered, so you can audit all examples.
|
||||
The query provides data for security reviews of the application and you can also use it to identify external APIs
|
||||
that should be modeled as either taint steps, or sinks for specific problems.</p>
|
||||
|
||||
<p>An external API is defined as a call to a function that is not defined in the source code, and is not modeled
|
||||
as a taint step in the default taint library. External APIs may be from the
|
||||
C++ standard library, third-party dependencies or from internal dependencies. The query reports uses of
|
||||
untrusted data in either the qualifier or as one of the arguments of external APIs.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>For each result:</p>
|
||||
|
||||
<ul>
|
||||
<li>If the result highlights a known sink, confirm that the result is reported by the relevant query, or
|
||||
that the result is a false positive because this data is sanitized.</li>
|
||||
<li>If the result highlights an unknown sink for a problem, then add modeling for the sink to the relevant query,
|
||||
and confirm that the result is either found, or is safe due to appropriate sanitization.</li>
|
||||
<li>If the result represents a call to an external API that transfers taint, add the appropriate modeling, and
|
||||
re-run the query to determine what new results have appeared due to this additional modeling.</li>
|
||||
</ul>
|
||||
|
||||
<p>Otherwise, the result is likely uninteresting. Custom versions of this query can extend the <code>SafeExternalAPIFunction</code>
|
||||
class to exclude known safe external APIs from future analysis.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>In this first example, input is read from <code>fgets</code> and then ultimately used in a call to the
|
||||
<code>fputs</code> external API:</p>
|
||||
|
||||
<sample src="ExternalAPISinkExample.cpp" />
|
||||
|
||||
<p>This is an XSS sink. The XSS query should therefore be reviewed to confirm that this sink is appropriately modeled,
|
||||
and if it is, to confirm that the query reports this particular result, or that the result is a false positive due to
|
||||
some existing sanitization.</p>
|
||||
|
||||
<p>In this second example, again a request parameter is read from <code>fgets</code>.</p>
|
||||
|
||||
<sample src="ExternalAPITaintStepExample.cpp" />
|
||||
|
||||
<p>If the query reported the call to <code>strcat</code> on line 9, this would suggest that this external API is
|
||||
not currently modeled as a taint step in the taint tracking library. The next step would be to model this as a taint step, then
|
||||
re-run the query to determine what additional results might be found. In this example, it seems likely that <code>buffer</code>
|
||||
will be executed as an SQL query, potentially leading to an SQL injection vulnerability.</p>
|
||||
|
||||
<p>Note that both examples are correctly handled by the standard taint tracking library and XSS query.</p>
|
||||
</example>
|
||||
<references>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* @name Untrusted data passed to external API
|
||||
* @description Data provided remotely is used in this external API without sanitization, which could be a security risk.
|
||||
* @id cpp/untrusted-data-to-external-api
|
||||
* @kind path-problem
|
||||
* @precision low
|
||||
* @problem.severity error
|
||||
* @tags security external/cwe/cwe-20
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
import ExternalAPIs
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from UntrustedDataToExternalAPIConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where config.hasFlowPath(source, sink)
|
||||
select sink, source, sink,
|
||||
"Call to " + sink.getNode().(ExternalAPIDataNode).getExternalFunction().toString() +
|
||||
" with untrusted data from $@.", source, source.toString()
|
||||
@@ -1,50 +0,0 @@
|
||||
/**
|
||||
* Definitions for reasoning about untrusted data used in APIs defined outside the
|
||||
* database.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.models.interfaces.Taint
|
||||
import ExternalAPIsSpecific
|
||||
|
||||
/** A node representing untrusted data being passed to an external API. */
|
||||
class UntrustedExternalAPIDataNode extends ExternalAPIDataNode {
|
||||
UntrustedExternalAPIDataNode() { any(UntrustedDataToExternalAPIConfig c).hasFlow(_, this) }
|
||||
|
||||
/** Gets a source of untrusted data which is passed to this external API data node. */
|
||||
DataFlow::Node getAnUntrustedSource() {
|
||||
any(UntrustedDataToExternalAPIConfig c).hasFlow(result, this)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TExternalAPI =
|
||||
TExternalAPIParameter(Function f, int index) {
|
||||
exists(UntrustedExternalAPIDataNode n |
|
||||
f = n.getExternalFunction() and
|
||||
index = n.getIndex()
|
||||
)
|
||||
}
|
||||
|
||||
/** An external API which is used with untrusted data. */
|
||||
class ExternalAPIUsedWithUntrustedData extends TExternalAPI {
|
||||
/** Gets a possibly untrusted use of this external API. */
|
||||
UntrustedExternalAPIDataNode getUntrustedDataNode() {
|
||||
this = TExternalAPIParameter(result.getExternalFunction(), result.getIndex())
|
||||
}
|
||||
|
||||
/** Gets the number of untrusted sources used with this external API. */
|
||||
int getNumberOfUntrustedSources() {
|
||||
result = strictcount(getUntrustedDataNode().getAnUntrustedSource())
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
exists(Function f, int index, string indexString |
|
||||
if index = -1 then indexString = "qualifier" else indexString = "param " + index
|
||||
|
|
||||
this = TExternalAPIParameter(f, index) and
|
||||
result = f.toString() + " [" + indexString + "]"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/**
|
||||
* Provides IR-specific definitions for use in the `ExternalAPI` library.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
private import semmle.code.cpp.security.FlowSources
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import SafeExternalAPIFunction
|
||||
|
||||
/** A node representing untrusted data being passed to an external API. */
|
||||
class ExternalAPIDataNode extends DataFlow::Node {
|
||||
Call call;
|
||||
int i;
|
||||
|
||||
ExternalAPIDataNode() {
|
||||
// Argument to call to a function
|
||||
(
|
||||
this.asExpr() = call.getArgument(i)
|
||||
or
|
||||
i = -1 and this.asExpr() = call.getQualifier()
|
||||
) and
|
||||
exists(Function f |
|
||||
f = call.getTarget() and
|
||||
// Defined outside the source archive
|
||||
not f.hasDefinition() and
|
||||
// Not already modeled as a dataflow or taint step
|
||||
not f instanceof DataFlowFunction and
|
||||
not f instanceof TaintFunction and
|
||||
// Not a call to a known safe external API
|
||||
not f instanceof SafeExternalAPIFunction
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the called API `Function`. */
|
||||
Function getExternalFunction() { result = call.getTarget() }
|
||||
|
||||
/** Gets the index which is passed untrusted data (where -1 indicates the qualifier). */
|
||||
int getIndex() { result = i }
|
||||
|
||||
/** Gets the description of the function being called. */
|
||||
string getFunctionDescription() { result = getExternalFunction().toString() }
|
||||
}
|
||||
|
||||
/** A configuration for tracking flow from `RemoteFlowSource`s to `ExternalAPIDataNode`s. */
|
||||
class UntrustedDataToExternalAPIConfig extends TaintTracking::Configuration {
|
||||
UntrustedDataToExternalAPIConfig() { this = "UntrustedDataToExternalAPIConfigIR" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof ExternalAPIDataNode }
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* Provides a class for modeling external functions that are "safe" from a security perspective.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* A `Function` that is considered a "safe" external API from a security perspective.
|
||||
*/
|
||||
abstract class SafeExternalAPIFunction extends Function { }
|
||||
|
||||
/** The default set of "safe" external APIs. */
|
||||
private class DefaultSafeExternalAPIFunction extends SafeExternalAPIFunction {
|
||||
DefaultSafeExternalAPIFunction() {
|
||||
// If a function does not write to any of its arguments, we consider it safe to
|
||||
// pass untrusted data to it. This means that string functions such as `strcmp`
|
||||
// and `strlen`, as well as memory functions such as `memcmp`, are considered safe.
|
||||
exists(SideEffectFunction model | model = this |
|
||||
model.hasOnlySpecificWriteSideEffects() and
|
||||
not model.hasSpecificWriteSideEffect(_, _, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import IDEContextual
|
||||
|
||||
/**
|
||||
* Any element that might be the source or target of a jump-to-definition
|
||||
@@ -208,3 +207,11 @@ Top definitionOf(Top e, string kind) {
|
||||
// later on.
|
||||
strictcount(result.getLocation()) < 10
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an appropriately encoded version of a filename `name`
|
||||
* passed by the VS Code extension in order to coincide with the
|
||||
* output of `.getFile()` on locatable entities.
|
||||
*/
|
||||
cached
|
||||
File getEncodedFile(string name) { result.getAbsolutePath().replaceAll(":", "_") = name }
|
||||
|
||||
@@ -7,6 +7,7 @@ import semmle.code.cpp.dataflow.TaintTracking
|
||||
import experimental.semmle.code.cpp.security.PrivateData
|
||||
import semmle.code.cpp.security.FileWrite
|
||||
import semmle.code.cpp.security.BufferWrite
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
|
||||
module PrivateCleartextWrite {
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* the file from being included twice). This prevents errors and
|
||||
* inefficiencies caused by repeated inclusion.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @id cpp/missing-header-guard
|
||||
* @tags efficiency
|
||||
|
||||
@@ -12,5 +12,5 @@ import definitions
|
||||
external string selectedSourceFile();
|
||||
|
||||
from Top e, Top def, string kind
|
||||
where def = definitionOf(e, kind) and e.getFile() = getFileBySourceArchiveName(selectedSourceFile())
|
||||
where def = definitionOf(e, kind) and e.getFile() = getEncodedFile(selectedSourceFile())
|
||||
select e, def, kind
|
||||
|
||||
@@ -12,6 +12,5 @@ import definitions
|
||||
external string selectedSourceFile();
|
||||
|
||||
from Top e, Top def, string kind
|
||||
where
|
||||
def = definitionOf(e, kind) and def.getFile() = getFileBySourceArchiveName(selectedSourceFile())
|
||||
where def = definitionOf(e, kind) and def.getFile() = getEncodedFile(selectedSourceFile())
|
||||
select e, def, kind
|
||||
|
||||
@@ -22,6 +22,6 @@ class Cfg extends PrintASTConfiguration {
|
||||
* Print All functions from the selected file.
|
||||
*/
|
||||
override predicate shouldPrintFunction(Function func) {
|
||||
func.getFile() = getFileBySourceArchiveName(selectedSourceFile())
|
||||
func.getFile() = getEncodedFile(selectedSourceFile())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -977,12 +977,7 @@ class ClassTemplateInstantiation extends Class {
|
||||
* specialization - see `FullClassTemplateSpecialization` and
|
||||
* `PartialClassTemplateSpecialization`).
|
||||
*/
|
||||
class ClassTemplateSpecialization extends Class {
|
||||
ClassTemplateSpecialization() {
|
||||
isFullClassTemplateSpecialization(this) or
|
||||
isPartialClassTemplateSpecialization(this)
|
||||
}
|
||||
|
||||
abstract class ClassTemplateSpecialization extends Class {
|
||||
/**
|
||||
* Gets the primary template for the specialization, for example on
|
||||
* `S<T,int>`, the result is `S<T,U>`.
|
||||
@@ -1002,16 +997,6 @@ class ClassTemplateSpecialization extends Class {
|
||||
override string getAPrimaryQlClass() { result = "ClassTemplateSpecialization" }
|
||||
}
|
||||
|
||||
private predicate isFullClassTemplateSpecialization(Class c) {
|
||||
// This class has template arguments, but none of them involves a template parameter.
|
||||
exists(c.getATemplateArgument()) and
|
||||
not exists(Type ta | ta = c.getATemplateArgument() and ta.involvesTemplateParameter()) and
|
||||
// This class does not have any instantiations.
|
||||
not exists(c.(TemplateClass).getAnInstantiation()) and
|
||||
// This class is not an instantiation of a class template.
|
||||
not c instanceof ClassTemplateInstantiation
|
||||
}
|
||||
|
||||
/**
|
||||
* A full specialization of a class template. For example `MyTemplateClass<int>`
|
||||
* in the following code is a `FullClassTemplateSpecialization`:
|
||||
@@ -1028,31 +1013,19 @@ private predicate isFullClassTemplateSpecialization(Class c) {
|
||||
* ```
|
||||
*/
|
||||
class FullClassTemplateSpecialization extends ClassTemplateSpecialization {
|
||||
FullClassTemplateSpecialization() { isFullClassTemplateSpecialization(this) }
|
||||
FullClassTemplateSpecialization() {
|
||||
// This class has template arguments, but none of them involves a template parameter.
|
||||
exists(getATemplateArgument()) and
|
||||
not exists(Type ta | ta = getATemplateArgument() and ta.involvesTemplateParameter()) and
|
||||
// This class does not have any instantiations.
|
||||
not exists(this.(TemplateClass).getAnInstantiation()) and
|
||||
// This class is not an instantiation of a class template.
|
||||
not this instanceof ClassTemplateInstantiation
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "FullClassTemplateSpecialization" }
|
||||
}
|
||||
|
||||
private predicate isPartialClassTemplateSpecialization(Class c) {
|
||||
/*
|
||||
* (a) At least one of this class's template arguments involves a
|
||||
* template parameter in some respect, for example T, T*, etc.
|
||||
*
|
||||
* (b) It is not the case that the n template arguments of this class
|
||||
* are a set of n distinct template parameters.
|
||||
*
|
||||
* template <typename T,U> class X {}; // class template
|
||||
* template <typename T> class X<T,T> {}; // partial class template specialization
|
||||
* template <typename T> class X<T,int> {}; // partial class template specialization
|
||||
* template <typename T> class Y {}; // class template
|
||||
* template <typename T> class Y<T*> {}; // partial class template specialization
|
||||
*/
|
||||
|
||||
exists(Type ta | ta = c.getATemplateArgument() and ta.involvesTemplateParameter()) and
|
||||
count(TemplateParameter tp | tp = c.getATemplateArgument()) !=
|
||||
count(int i | exists(c.getTemplateArgument(i)))
|
||||
}
|
||||
|
||||
/**
|
||||
* A partial specialization of a class template. For example `MyTemplateClass<int, T>`
|
||||
* in the following code is a `PartialClassTemplateSpecialization`:
|
||||
@@ -1069,7 +1042,25 @@ private predicate isPartialClassTemplateSpecialization(Class c) {
|
||||
* ```
|
||||
*/
|
||||
class PartialClassTemplateSpecialization extends ClassTemplateSpecialization {
|
||||
PartialClassTemplateSpecialization() { isPartialClassTemplateSpecialization(this) }
|
||||
PartialClassTemplateSpecialization() {
|
||||
/*
|
||||
* (a) At least one of this class's template arguments involves a
|
||||
* template parameter in some respect, for example T, T*, etc.
|
||||
*
|
||||
* (b) It is not the case that the n template arguments of this class
|
||||
* are a set of n distinct template parameters.
|
||||
*
|
||||
* template <typename T,U> class X {}; // class template
|
||||
* template <typename T> class X<T,T> {}; // partial class template specialization
|
||||
* template <typename T> class X<T,int> {}; // partial class template specialization
|
||||
* template <typename T> class Y {}; // class template
|
||||
* template <typename T> class Y<T*> {}; // partial class template specialization
|
||||
*/
|
||||
|
||||
exists(Type ta | ta = getATemplateArgument() and ta.involvesTemplateParameter()) and
|
||||
count(TemplateParameter tp | tp = getATemplateArgument()) !=
|
||||
count(int i | exists(getTemplateArgument(i)))
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "PartialClassTemplateSpecialization" }
|
||||
}
|
||||
|
||||
@@ -65,10 +65,11 @@ class ElementBase extends @element {
|
||||
* which they belong; for example, `AddExpr` is a primary class, but
|
||||
* `BinaryOperation` is not.
|
||||
*
|
||||
* This predicate can have multiple results if multiple primary classes match.
|
||||
* For some elements, this predicate may not have a result.
|
||||
* This predicate always has a result. If no primary class can be
|
||||
* determined, the result is `"???"`. If multiple primary classes match,
|
||||
* this predicate can have multiple results.
|
||||
*/
|
||||
string getAPrimaryQlClass() { none() }
|
||||
string getAPrimaryQlClass() { result = "???" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -205,21 +205,12 @@ class Constructor extends MemberFunction {
|
||||
/**
|
||||
* A function that defines an implicit conversion.
|
||||
*/
|
||||
class ImplicitConversionFunction extends MemberFunction {
|
||||
ImplicitConversionFunction() {
|
||||
// ConversionOperator
|
||||
functions(underlyingElement(this), _, 4)
|
||||
or
|
||||
// ConversionConstructor (deprecated)
|
||||
strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
|
||||
not hasSpecifier("explicit")
|
||||
}
|
||||
|
||||
abstract class ImplicitConversionFunction extends MemberFunction {
|
||||
/** Gets the type this `ImplicitConversionFunction` takes as input. */
|
||||
Type getSourceType() { none() } // overridden in subclasses
|
||||
abstract Type getSourceType();
|
||||
|
||||
/** Gets the type this `ImplicitConversionFunction` converts to. */
|
||||
Type getDestType() { none() } // overridden in subclasses
|
||||
abstract Type getDestType();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -60,7 +60,7 @@ private string getTemplateArgumentString(Declaration d, int i) {
|
||||
/**
|
||||
* A `Declaration` extended to add methods for generating strings useful only for dumps and debugging.
|
||||
*/
|
||||
private class DumpDeclaration extends Declaration {
|
||||
abstract private class DumpDeclaration extends Declaration {
|
||||
DumpDeclaration() { shouldPrintDeclaration(this) }
|
||||
|
||||
/**
|
||||
@@ -304,7 +304,7 @@ private class SpecifiedDumpType extends DerivedDumpType, SpecifiedType {
|
||||
basePrefix = getBaseType().(DumpType).getDeclaratorPrefix() and
|
||||
if getBaseType().getUnspecifiedType() instanceof RoutineType
|
||||
then result = basePrefix
|
||||
else result = basePrefix + " " + getSpecifierString()
|
||||
else result = basePrefix + " " + getSpecifierString().trim()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -312,7 +312,7 @@ private class SpecifiedDumpType extends DerivedDumpType, SpecifiedType {
|
||||
exists(string baseSuffix |
|
||||
baseSuffix = getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() and
|
||||
if getBaseType().getUnspecifiedType() instanceof RoutineType
|
||||
then result = baseSuffix + " " + getSpecifierString()
|
||||
then result = baseSuffix + " " + getSpecifierString().trim()
|
||||
else result = baseSuffix
|
||||
)
|
||||
}
|
||||
|
||||
@@ -91,8 +91,7 @@ private newtype TPrintASTNode =
|
||||
TDeclarationEntryNode(DeclStmt stmt, DeclarationEntry entry) {
|
||||
// We create a unique node for each pair of (stmt, entry), to avoid having one node with
|
||||
// multiple parents due to extractor bug CPP-413.
|
||||
stmt.getADeclarationEntry() = entry and
|
||||
shouldPrintFunction(stmt.getEnclosingFunction())
|
||||
stmt.getADeclarationEntry() = entry
|
||||
} or
|
||||
TParametersNode(Function func) { shouldPrintFunction(func) } or
|
||||
TConstructorInitializersNode(Constructor ctor) {
|
||||
@@ -235,27 +234,11 @@ class PrintASTNode extends TPrintASTNode {
|
||||
private Function getEnclosingFunction() { result = getParent*().(FunctionNode).getFunction() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that restricts the elements that we compute `qlClass` for.
|
||||
*/
|
||||
private class PrintableElement extends Element {
|
||||
PrintableElement() {
|
||||
exists(TASTNode(this))
|
||||
or
|
||||
exists(TDeclarationEntryNode(_, this))
|
||||
or
|
||||
this instanceof Type
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
string getAPrimaryQlClass0() { result = getAPrimaryQlClass() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the canonical QL class(es) for entity `el`
|
||||
*/
|
||||
private string qlClass(PrintableElement el) {
|
||||
result = "[" + concat(el.getAPrimaryQlClass0(), ",") + "] "
|
||||
private string qlClass(ElementBase el) {
|
||||
result = "[" + concat(el.getAPrimaryQlClass(), ",") + "] "
|
||||
// Alternative implementation -- do not delete. It is useful for QL class discovery.
|
||||
//result = "["+ concat(el.getAQlClass(), ",") + "] "
|
||||
}
|
||||
|
||||
@@ -577,9 +577,7 @@ class BoolType extends IntegralType {
|
||||
* unsigned char e, f;
|
||||
* ```
|
||||
*/
|
||||
class CharType extends IntegralType {
|
||||
CharType() { builtintypes(underlyingElement(this), _, [5, 6, 7], _, _, _) }
|
||||
}
|
||||
abstract class CharType extends IntegralType { }
|
||||
|
||||
/**
|
||||
* The C/C++ `char` type (which is distinct from `signed char` and
|
||||
@@ -1306,16 +1304,14 @@ class SpecifiedType extends DerivedType {
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets all the specifiers of this type as a string in a fixed order (the order
|
||||
* only depends on the specifiers, not on the source program). This is intended
|
||||
* for debugging queries only and is an expensive operation.
|
||||
*/
|
||||
string getSpecifierString() { result = concat(this.getASpecifier().getName(), " ") }
|
||||
string getSpecifierString() { internalSpecString(this, result, 1) }
|
||||
|
||||
override string explain() {
|
||||
result = this.getSpecifierString() + " {" + this.getBaseType().explain() + "}"
|
||||
result = this.getSpecifierString() + "{" + this.getBaseType().explain() + "}"
|
||||
}
|
||||
|
||||
override predicate isDeeplyConst() {
|
||||
@@ -1714,6 +1710,28 @@ class AutoType extends TemplateParameter {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Internal implementation predicates
|
||||
//
|
||||
private predicate allSpecifiers(int i, string s) { s = rank[i](string t | specifiers(_, t) | t) }
|
||||
|
||||
private predicate internalSpecString(Type t, string res, int i) {
|
||||
(
|
||||
if allSpecifiers(i, t.getASpecifier().getName())
|
||||
then
|
||||
exists(string spec, string rest |
|
||||
allSpecifiers(i, spec) and
|
||||
res = spec + " " + rest and
|
||||
internalSpecString(t, rest, i + 1)
|
||||
)
|
||||
else (
|
||||
allSpecifiers(i, _) and internalSpecString(t, res, i + 1)
|
||||
)
|
||||
)
|
||||
or
|
||||
i = count(Specifier s) + 1 and res = ""
|
||||
}
|
||||
|
||||
private predicate suppressUnusedThis(Type t) { any() }
|
||||
|
||||
/** A source code location referring to a type */
|
||||
|
||||
@@ -4,11 +4,8 @@
|
||||
|
||||
import semmle.files.FileSystem
|
||||
|
||||
private class TXMLLocatable =
|
||||
@xmldtd or @xmlelement or @xmlattribute or @xmlnamespace or @xmlcomment or @xmlcharacters;
|
||||
|
||||
/** An XML element that has a location. */
|
||||
class XMLLocatable extends @xmllocatable, TXMLLocatable {
|
||||
abstract class XMLLocatable extends @xmllocatable {
|
||||
/** Gets the source location for this element. */
|
||||
Location getLocation() { xmllocations(this, result) }
|
||||
|
||||
@@ -36,7 +33,7 @@ class XMLLocatable extends @xmllocatable, TXMLLocatable {
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() } // overridden in subclasses
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,7 +51,7 @@ class XMLParent extends @xmlparent {
|
||||
* Gets a printable representation of this XML parent.
|
||||
* (Intended to be overridden in subclasses.)
|
||||
*/
|
||||
string getName() { none() } // overridden in subclasses
|
||||
abstract string getName();
|
||||
|
||||
/** Gets the file to which this XML parent belongs. */
|
||||
XMLFile getFile() { result = this or xmlElements(this, _, _, _, result) }
|
||||
|
||||
@@ -6,6 +6,7 @@ import semmle.code.cpp.Type
|
||||
import semmle.code.cpp.commons.CommonType
|
||||
import semmle.code.cpp.commons.StringAnalysis
|
||||
import semmle.code.cpp.models.interfaces.FormattingFunction
|
||||
import semmle.code.cpp.models.implementations.Printf
|
||||
|
||||
class PrintfFormatAttribute extends FormatAttribute {
|
||||
PrintfFormatAttribute() { getArchetype() = ["printf", "__printf__"] }
|
||||
|
||||
@@ -9,14 +9,12 @@ import cpp
|
||||
class StrcatFunction extends Function {
|
||||
StrcatFunction() {
|
||||
getName() =
|
||||
[
|
||||
"strcat", // strcat(dst, src)
|
||||
"strncat", // strncat(dst, src, max_amount)
|
||||
"wcscat", // wcscat(dst, src)
|
||||
"_mbscat", // _mbscat(dst, src)
|
||||
"wcsncat", // wcsncat(dst, src, max_amount)
|
||||
"_mbsncat", // _mbsncat(dst, src, max_amount)
|
||||
"_mbsncat_l" // _mbsncat_l(dst, src, max_amount, locale)
|
||||
]
|
||||
["strcat", // strcat(dst, src)
|
||||
"strncat", // strncat(dst, src, max_amount)
|
||||
"wcscat", // wcscat(dst, src)
|
||||
"_mbscat", // _mbscat(dst, src)
|
||||
"wcsncat", // wcsncat(dst, src, max_amount)
|
||||
"_mbsncat", // _mbsncat(dst, src, max_amount)
|
||||
"_mbsncat_l"] // _mbsncat_l(dst, src, max_amount, locale)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,8 +83,6 @@ private predicate pointerToPointerStep(Expr pointerIn, Expr pointerOut) {
|
||||
or
|
||||
pointerIn.getConversion() = pointerOut.(ParenthesisExpr)
|
||||
or
|
||||
pointerIn.getConversion() = pointerOut.(TemporaryObjectExpr)
|
||||
or
|
||||
pointerIn = pointerOut.(ConditionalExpr).getThen().getFullyConverted()
|
||||
or
|
||||
pointerIn = pointerOut.(ConditionalExpr).getElse().getFullyConverted()
|
||||
@@ -212,9 +210,14 @@ private predicate addressMayEscapeAt(Expr e) {
|
||||
|
||||
private predicate addressMayEscapeMutablyAt(Expr e) {
|
||||
addressMayEscapeAt(e) and
|
||||
exists(Type t | t = e.getType().stripTopLevelSpecifiers() |
|
||||
t instanceof PointerType and
|
||||
not t.(PointerType).getBaseType().isConst()
|
||||
exists(Type t | t = e.getType().getUnderlyingType() |
|
||||
exists(PointerType pt |
|
||||
pt = t
|
||||
or
|
||||
pt = t.(SpecifiedType).getBaseType()
|
||||
|
|
||||
not pt.getBaseType().isConst()
|
||||
)
|
||||
or
|
||||
t instanceof ReferenceType and
|
||||
not t.(ReferenceType).getBaseType().isConst()
|
||||
@@ -222,15 +225,6 @@ private predicate addressMayEscapeMutablyAt(Expr e) {
|
||||
// If the address has been cast to an integral type, conservatively assume that it may eventually be cast back to a
|
||||
// pointer to non-const type.
|
||||
t instanceof IntegralType
|
||||
or
|
||||
// If we go through a temporary object step, we can take a reference to a temporary const pointer
|
||||
// object, where the pointer doesn't point to a const value
|
||||
exists(TemporaryObjectExpr temp, PointerType pt |
|
||||
temp.getConversion() = e.(ReferenceToExpr) and
|
||||
pt = temp.getType().stripTopLevelSpecifiers()
|
||||
|
|
||||
not pt.getBaseType().isConst()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -258,7 +252,7 @@ private predicate addressFromVariableAccess(VariableAccess va, Expr e) {
|
||||
// `e` could be a pointer that is converted to a reference as the final step,
|
||||
// meaning that we pass a value that is two dereferences away from referring
|
||||
// to `va`. This happens, for example, with `void std::vector::push_back(T&&
|
||||
// value);` when called as `v.push_back(&x)`, for a variable `x`. It
|
||||
// value);` when called as `v.push_back(&x)`, for a static variable `x`. It
|
||||
// can also happen when taking a reference to a const pointer to a
|
||||
// (potentially non-const) value.
|
||||
exists(Expr pointerValue |
|
||||
|
||||
@@ -81,8 +81,6 @@ private predicate pointerToPointerStep(Expr pointerIn, Expr pointerOut) {
|
||||
or
|
||||
pointerIn.getConversion() = pointerOut.(ParenthesisExpr)
|
||||
or
|
||||
pointerIn.getConversion() = pointerOut.(TemporaryObjectExpr)
|
||||
or
|
||||
pointerIn = pointerOut.(ConditionalExpr).getThen().getFullyConverted()
|
||||
or
|
||||
pointerIn = pointerOut.(ConditionalExpr).getElse().getFullyConverted()
|
||||
|
||||
@@ -112,8 +112,8 @@ abstract class Configuration extends string {
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
* measured in approximate number of interprocedural steps.
|
||||
* Gets the exploration limit for `hasPartialFlow` measured in approximate
|
||||
* number of interprocedural steps.
|
||||
*/
|
||||
int explorationLimit() { none() }
|
||||
|
||||
@@ -123,7 +123,7 @@ abstract class Configuration extends string {
|
||||
* is restricted to be less than or equal to `explorationLimit()`. This
|
||||
* predicate completely disregards sink definitions.
|
||||
*
|
||||
* This predicate is intended for data-flow exploration and debugging and may
|
||||
* This predicate is intended for dataflow exploration and debugging and may
|
||||
* perform poorly if the number of sources is too big and/or the exploration
|
||||
* limit is set too high without using barriers.
|
||||
*
|
||||
@@ -136,29 +136,6 @@ abstract class Configuration extends string {
|
||||
partialFlow(source, node, this) and
|
||||
dist = node.getSourceDistance()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a partial data flow path from `node` to `sink`. The
|
||||
* approximate distance between `node` and the closest sink is `dist` and
|
||||
* is restricted to be less than or equal to `explorationLimit()`. This
|
||||
* predicate completely disregards source definitions.
|
||||
*
|
||||
* This predicate is intended for data-flow exploration and debugging and may
|
||||
* perform poorly if the number of sinks is too big and/or the exploration
|
||||
* limit is set too high without using barriers.
|
||||
*
|
||||
* This predicate is disabled (has no results) by default. Override
|
||||
* `explorationLimit()` with a suitable number to enable this predicate.
|
||||
*
|
||||
* To use this in a `path-problem` query, import the module `PartialPathGraph`.
|
||||
*
|
||||
* Note that reverse flow has slightly lower precision than the corresponding
|
||||
* forward flow, as reverse flow disregards type pruning among other features.
|
||||
*/
|
||||
final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) {
|
||||
revPartialFlow(node, sink, this) and
|
||||
dist = node.getSinkDistance()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2966,26 +2943,12 @@ private module FlowExploration {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
|
||||
exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
|
||||
or
|
||||
exists(DataFlowCallable mid |
|
||||
interestingCallableSink(mid, config) and callableStep(c, mid, config)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TCallableExt =
|
||||
TCallable(DataFlowCallable c, Configuration config) {
|
||||
interestingCallableSrc(c, config) or
|
||||
interestingCallableSink(c, config)
|
||||
} or
|
||||
TCallableSrc() or
|
||||
TCallableSink()
|
||||
TCallable(DataFlowCallable c, Configuration config) { interestingCallableSrc(c, config) } or
|
||||
TCallableSrc()
|
||||
|
||||
private predicate callableExtSrc(TCallableSrc src) { any() }
|
||||
|
||||
private predicate callableExtSink(TCallableSink sink) { any() }
|
||||
|
||||
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
|
||||
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
|
||||
callableStep(c1, c2, config) and
|
||||
@@ -2998,32 +2961,15 @@ private module FlowExploration {
|
||||
config.isSource(n) and
|
||||
ce2 = TCallable(n.getEnclosingCallable(), config)
|
||||
)
|
||||
or
|
||||
exists(Node n, Configuration config |
|
||||
ce2 = TCallableSink() and
|
||||
config.isSink(n) and
|
||||
ce1 = TCallable(n.getEnclosingCallable(), config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) {
|
||||
callableExtStepFwd(ce2, ce1)
|
||||
}
|
||||
|
||||
private int distSrcExt(TCallableExt c) =
|
||||
shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result)
|
||||
|
||||
private int distSinkExt(TCallableExt c) =
|
||||
shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result)
|
||||
|
||||
private int distSrc(DataFlowCallable c, Configuration config) {
|
||||
result = distSrcExt(TCallable(c, config)) - 1
|
||||
}
|
||||
|
||||
private int distSink(DataFlowCallable c, Configuration config) {
|
||||
result = distSinkExt(TCallable(c, config)) - 1
|
||||
}
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
|
||||
@@ -3051,12 +2997,18 @@ private module FlowExploration {
|
||||
or
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
}
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = TFrontNil(t))
|
||||
}
|
||||
}
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
@@ -3067,39 +3019,9 @@ private module FlowExploration {
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TRevPartialAccessPath =
|
||||
TRevPartialNil() or
|
||||
TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s, but only the first
|
||||
* element of the list and its length are tracked.
|
||||
*/
|
||||
private class RevPartialAccessPath extends TRevPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TRevPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TRevPartialNil() and result = 0
|
||||
or
|
||||
this = TRevPartialCons(_, result)
|
||||
}
|
||||
}
|
||||
|
||||
private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil {
|
||||
override string toString() { result = "" }
|
||||
}
|
||||
|
||||
private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons {
|
||||
override string toString() {
|
||||
exists(Content c, int len | this = TRevPartialCons(c, len) |
|
||||
if len = 1
|
||||
then result = "[" + c.toString() + "]"
|
||||
else result = "[" + c.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
override AccessPathFront getFront() {
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3111,16 +3033,8 @@ private module FlowExploration {
|
||||
TSummaryCtx2None() or
|
||||
TSummaryCtx2Some(PartialAccessPath ap)
|
||||
|
||||
private newtype TRevSummaryCtx1 =
|
||||
TRevSummaryCtx1None() or
|
||||
TRevSummaryCtx1Some(ReturnPosition pos)
|
||||
|
||||
private newtype TRevSummaryCtx2 =
|
||||
TRevSummaryCtx2None() or
|
||||
TRevSummaryCtx2Some(RevPartialAccessPath ap)
|
||||
|
||||
private newtype TPartialPathNode =
|
||||
TPartialPathNodeFwd(
|
||||
TPartialPathNodeMk(
|
||||
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
@@ -3134,23 +3048,6 @@ private module FlowExploration {
|
||||
or
|
||||
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
|
||||
distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
} or
|
||||
TPartialPathNodeRev(
|
||||
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
config.isSink(node) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = TRevPartialNil() and
|
||||
not fullBarrier(node, config) and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -3158,7 +3055,7 @@ private module FlowExploration {
|
||||
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
exists(PartialPathNode mid |
|
||||
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
if node instanceof CastingNode
|
||||
@@ -3210,32 +3107,15 @@ private module FlowExploration {
|
||||
result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the approximate distance to the nearest sink measured in number
|
||||
* of interprocedural steps.
|
||||
*/
|
||||
int getSinkDistance() {
|
||||
result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s |
|
||||
s = this.(PartialPathNodeFwd).getAp().toString() or
|
||||
s = this.(PartialPathNodeRev).getAp().toString()
|
||||
|
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
private string ppCtx() {
|
||||
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
|
||||
result = " <" + this.(PartialPathNodePriv).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
/** Holds if this is a source in a forward-flow path. */
|
||||
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
|
||||
|
||||
/** Holds if this is a sink in a reverse-flow path. */
|
||||
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3246,7 +3126,7 @@ private module FlowExploration {
|
||||
query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b }
|
||||
}
|
||||
|
||||
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
|
||||
private class PartialPathNodePriv extends PartialPathNode {
|
||||
Node node;
|
||||
CallContext cc;
|
||||
TSummaryCtx1 sc1;
|
||||
@@ -3254,7 +3134,7 @@ private module FlowExploration {
|
||||
PartialAccessPath ap;
|
||||
Configuration config;
|
||||
|
||||
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
|
||||
PartialPathNodePriv() { this = TPartialPathNodeMk(node, cc, sc1, sc2, ap, config) }
|
||||
|
||||
override Node getNode() { result = node }
|
||||
|
||||
@@ -3268,54 +3148,14 @@ private module FlowExploration {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PartialPathNodeFwd getASuccessor() {
|
||||
override PartialPathNodePriv getASuccessor() {
|
||||
partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
|
||||
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
|
||||
}
|
||||
|
||||
predicate isSource() {
|
||||
config.isSource(node) and
|
||||
cc instanceof CallContextAny and
|
||||
sc1 = TSummaryCtx1None() and
|
||||
sc2 = TSummaryCtx2None() and
|
||||
ap instanceof TPartialNil
|
||||
}
|
||||
}
|
||||
|
||||
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
|
||||
Node node;
|
||||
TRevSummaryCtx1 sc1;
|
||||
TRevSummaryCtx2 sc2;
|
||||
RevPartialAccessPath ap;
|
||||
Configuration config;
|
||||
|
||||
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
|
||||
|
||||
override Node getNode() { result = node }
|
||||
|
||||
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
|
||||
|
||||
TRevSummaryCtx2 getSummaryCtx2() { result = sc2 }
|
||||
|
||||
RevPartialAccessPath getAp() { result = ap }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PartialPathNodeRev getASuccessor() {
|
||||
revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
|
||||
this.getAp(), this.getConfiguration())
|
||||
}
|
||||
|
||||
predicate isSink() {
|
||||
config.isSink(node) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = TRevPartialNil()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
@@ -3381,7 +3221,8 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
@@ -3397,7 +3238,7 @@ private module FlowExploration {
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
@@ -3405,7 +3246,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node midNode |
|
||||
@@ -3419,7 +3260,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
|
||||
PartialPathNodePriv mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
@@ -3431,7 +3272,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathOutOfCallable1(
|
||||
PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
@@ -3445,7 +3286,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable(
|
||||
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt kind, DataFlowCall call |
|
||||
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
||||
@@ -3456,7 +3297,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathIntoArg(
|
||||
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||
PartialPathNodePriv mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ArgumentNode arg |
|
||||
@@ -3470,7 +3311,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathIntoCallable0(
|
||||
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||
PartialPathNodePriv mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||
DataFlowCall call, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
partialPathIntoArg(mid, i, outercc, call, ap, config) and
|
||||
@@ -3478,7 +3319,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathIntoCallable(
|
||||
PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
|
||||
PartialPathNodePriv mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
|
||||
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
@@ -3499,7 +3340,7 @@ private module FlowExploration {
|
||||
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
|
||||
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -3512,7 +3353,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
|
||||
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
|
||||
@@ -3522,164 +3363,13 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathThroughCallable(
|
||||
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate revPartialPathStep(
|
||||
PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
|
||||
RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(node, mid.getNode(), config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(node, mid.getNode(), config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
mid.getAp() instanceof RevPartialAccessPathNil and
|
||||
ap = TRevPartialNil() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
jumpStep(node, mid.getNode(), config) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalJumpStep(node, mid.getNode(), config) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
mid.getAp() instanceof RevPartialAccessPathNil and
|
||||
ap = TRevPartialNil() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
revPartialPathReadStep(mid, _, _, node, ap) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(RevPartialAccessPath ap0, Content c |
|
||||
revPartialPathStoreStep(mid, ap0, c, node, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsRev(ap, c, ap0, config)
|
||||
)
|
||||
or
|
||||
exists(ParameterNode p |
|
||||
mid.getNode() = p and
|
||||
viableParamArg(_, p, node) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
exists(ReturnPosition pos |
|
||||
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
|
||||
pos = getReturnPosition(node)
|
||||
)
|
||||
or
|
||||
revPartialPathThroughCallable(mid, node, ap, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate revPartialPathReadStep(
|
||||
PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
|
||||
) {
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
read(node, c, midNode) and
|
||||
ap2.getHead() = c and
|
||||
ap2.len() = unbindInt(ap1.len() + 1)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsRev(
|
||||
RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathReadStep(mid, ap1, c, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathStoreStep(
|
||||
PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
|
||||
) {
|
||||
exists(Node midNode, TypedContent tc |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
store(node, tc, midNode, _) and
|
||||
ap.getHead() = c and
|
||||
config = mid.getConfiguration() and
|
||||
tc.getContent() = c
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathIntoReturn(
|
||||
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
|
||||
DataFlowCall call, RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node out |
|
||||
mid.getNode() = out and
|
||||
viableReturnPosOut(call, pos, out) and
|
||||
sc1 = TRevSummaryCtx1Some(pos) and
|
||||
sc2 = TRevSummaryCtx2Some(ap) and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathFlowsThrough(
|
||||
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeRev mid, ParameterNode p |
|
||||
mid.getNode() = p and
|
||||
p.isParameterOf(_, pos) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
|
||||
revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and
|
||||
revPartialPathFlowsThrough(pos, sc1, sc2, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathThroughCallable(
|
||||
PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, int pos |
|
||||
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
|
||||
node.argumentOf(call, pos)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import FlowExploration
|
||||
@@ -3688,14 +3378,6 @@ private predicate partialFlow(
|
||||
PartialPathNode source, PartialPathNode node, Configuration configuration
|
||||
) {
|
||||
source.getConfiguration() = configuration and
|
||||
source.isFwdSource() and
|
||||
configuration.isSource(source.getNode()) and
|
||||
node = source.getASuccessor+()
|
||||
}
|
||||
|
||||
private predicate revPartialFlow(
|
||||
PartialPathNode node, PartialPathNode sink, Configuration configuration
|
||||
) {
|
||||
sink.getConfiguration() = configuration and
|
||||
sink.isRevSink() and
|
||||
node.getASuccessor+() = sink
|
||||
}
|
||||
|
||||
@@ -112,8 +112,8 @@ abstract class Configuration extends string {
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
* measured in approximate number of interprocedural steps.
|
||||
* Gets the exploration limit for `hasPartialFlow` measured in approximate
|
||||
* number of interprocedural steps.
|
||||
*/
|
||||
int explorationLimit() { none() }
|
||||
|
||||
@@ -123,7 +123,7 @@ abstract class Configuration extends string {
|
||||
* is restricted to be less than or equal to `explorationLimit()`. This
|
||||
* predicate completely disregards sink definitions.
|
||||
*
|
||||
* This predicate is intended for data-flow exploration and debugging and may
|
||||
* This predicate is intended for dataflow exploration and debugging and may
|
||||
* perform poorly if the number of sources is too big and/or the exploration
|
||||
* limit is set too high without using barriers.
|
||||
*
|
||||
@@ -136,29 +136,6 @@ abstract class Configuration extends string {
|
||||
partialFlow(source, node, this) and
|
||||
dist = node.getSourceDistance()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a partial data flow path from `node` to `sink`. The
|
||||
* approximate distance between `node` and the closest sink is `dist` and
|
||||
* is restricted to be less than or equal to `explorationLimit()`. This
|
||||
* predicate completely disregards source definitions.
|
||||
*
|
||||
* This predicate is intended for data-flow exploration and debugging and may
|
||||
* perform poorly if the number of sinks is too big and/or the exploration
|
||||
* limit is set too high without using barriers.
|
||||
*
|
||||
* This predicate is disabled (has no results) by default. Override
|
||||
* `explorationLimit()` with a suitable number to enable this predicate.
|
||||
*
|
||||
* To use this in a `path-problem` query, import the module `PartialPathGraph`.
|
||||
*
|
||||
* Note that reverse flow has slightly lower precision than the corresponding
|
||||
* forward flow, as reverse flow disregards type pruning among other features.
|
||||
*/
|
||||
final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) {
|
||||
revPartialFlow(node, sink, this) and
|
||||
dist = node.getSinkDistance()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2966,26 +2943,12 @@ private module FlowExploration {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
|
||||
exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
|
||||
or
|
||||
exists(DataFlowCallable mid |
|
||||
interestingCallableSink(mid, config) and callableStep(c, mid, config)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TCallableExt =
|
||||
TCallable(DataFlowCallable c, Configuration config) {
|
||||
interestingCallableSrc(c, config) or
|
||||
interestingCallableSink(c, config)
|
||||
} or
|
||||
TCallableSrc() or
|
||||
TCallableSink()
|
||||
TCallable(DataFlowCallable c, Configuration config) { interestingCallableSrc(c, config) } or
|
||||
TCallableSrc()
|
||||
|
||||
private predicate callableExtSrc(TCallableSrc src) { any() }
|
||||
|
||||
private predicate callableExtSink(TCallableSink sink) { any() }
|
||||
|
||||
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
|
||||
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
|
||||
callableStep(c1, c2, config) and
|
||||
@@ -2998,32 +2961,15 @@ private module FlowExploration {
|
||||
config.isSource(n) and
|
||||
ce2 = TCallable(n.getEnclosingCallable(), config)
|
||||
)
|
||||
or
|
||||
exists(Node n, Configuration config |
|
||||
ce2 = TCallableSink() and
|
||||
config.isSink(n) and
|
||||
ce1 = TCallable(n.getEnclosingCallable(), config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) {
|
||||
callableExtStepFwd(ce2, ce1)
|
||||
}
|
||||
|
||||
private int distSrcExt(TCallableExt c) =
|
||||
shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result)
|
||||
|
||||
private int distSinkExt(TCallableExt c) =
|
||||
shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result)
|
||||
|
||||
private int distSrc(DataFlowCallable c, Configuration config) {
|
||||
result = distSrcExt(TCallable(c, config)) - 1
|
||||
}
|
||||
|
||||
private int distSink(DataFlowCallable c, Configuration config) {
|
||||
result = distSinkExt(TCallable(c, config)) - 1
|
||||
}
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
|
||||
@@ -3051,12 +2997,18 @@ private module FlowExploration {
|
||||
or
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
}
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = TFrontNil(t))
|
||||
}
|
||||
}
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
@@ -3067,39 +3019,9 @@ private module FlowExploration {
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TRevPartialAccessPath =
|
||||
TRevPartialNil() or
|
||||
TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s, but only the first
|
||||
* element of the list and its length are tracked.
|
||||
*/
|
||||
private class RevPartialAccessPath extends TRevPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TRevPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TRevPartialNil() and result = 0
|
||||
or
|
||||
this = TRevPartialCons(_, result)
|
||||
}
|
||||
}
|
||||
|
||||
private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil {
|
||||
override string toString() { result = "" }
|
||||
}
|
||||
|
||||
private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons {
|
||||
override string toString() {
|
||||
exists(Content c, int len | this = TRevPartialCons(c, len) |
|
||||
if len = 1
|
||||
then result = "[" + c.toString() + "]"
|
||||
else result = "[" + c.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
override AccessPathFront getFront() {
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3111,16 +3033,8 @@ private module FlowExploration {
|
||||
TSummaryCtx2None() or
|
||||
TSummaryCtx2Some(PartialAccessPath ap)
|
||||
|
||||
private newtype TRevSummaryCtx1 =
|
||||
TRevSummaryCtx1None() or
|
||||
TRevSummaryCtx1Some(ReturnPosition pos)
|
||||
|
||||
private newtype TRevSummaryCtx2 =
|
||||
TRevSummaryCtx2None() or
|
||||
TRevSummaryCtx2Some(RevPartialAccessPath ap)
|
||||
|
||||
private newtype TPartialPathNode =
|
||||
TPartialPathNodeFwd(
|
||||
TPartialPathNodeMk(
|
||||
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
@@ -3134,23 +3048,6 @@ private module FlowExploration {
|
||||
or
|
||||
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
|
||||
distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
} or
|
||||
TPartialPathNodeRev(
|
||||
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
config.isSink(node) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = TRevPartialNil() and
|
||||
not fullBarrier(node, config) and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -3158,7 +3055,7 @@ private module FlowExploration {
|
||||
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
exists(PartialPathNode mid |
|
||||
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
if node instanceof CastingNode
|
||||
@@ -3210,32 +3107,15 @@ private module FlowExploration {
|
||||
result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the approximate distance to the nearest sink measured in number
|
||||
* of interprocedural steps.
|
||||
*/
|
||||
int getSinkDistance() {
|
||||
result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s |
|
||||
s = this.(PartialPathNodeFwd).getAp().toString() or
|
||||
s = this.(PartialPathNodeRev).getAp().toString()
|
||||
|
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
private string ppCtx() {
|
||||
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
|
||||
result = " <" + this.(PartialPathNodePriv).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
/** Holds if this is a source in a forward-flow path. */
|
||||
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
|
||||
|
||||
/** Holds if this is a sink in a reverse-flow path. */
|
||||
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3246,7 +3126,7 @@ private module FlowExploration {
|
||||
query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b }
|
||||
}
|
||||
|
||||
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
|
||||
private class PartialPathNodePriv extends PartialPathNode {
|
||||
Node node;
|
||||
CallContext cc;
|
||||
TSummaryCtx1 sc1;
|
||||
@@ -3254,7 +3134,7 @@ private module FlowExploration {
|
||||
PartialAccessPath ap;
|
||||
Configuration config;
|
||||
|
||||
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
|
||||
PartialPathNodePriv() { this = TPartialPathNodeMk(node, cc, sc1, sc2, ap, config) }
|
||||
|
||||
override Node getNode() { result = node }
|
||||
|
||||
@@ -3268,54 +3148,14 @@ private module FlowExploration {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PartialPathNodeFwd getASuccessor() {
|
||||
override PartialPathNodePriv getASuccessor() {
|
||||
partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
|
||||
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
|
||||
}
|
||||
|
||||
predicate isSource() {
|
||||
config.isSource(node) and
|
||||
cc instanceof CallContextAny and
|
||||
sc1 = TSummaryCtx1None() and
|
||||
sc2 = TSummaryCtx2None() and
|
||||
ap instanceof TPartialNil
|
||||
}
|
||||
}
|
||||
|
||||
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
|
||||
Node node;
|
||||
TRevSummaryCtx1 sc1;
|
||||
TRevSummaryCtx2 sc2;
|
||||
RevPartialAccessPath ap;
|
||||
Configuration config;
|
||||
|
||||
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
|
||||
|
||||
override Node getNode() { result = node }
|
||||
|
||||
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
|
||||
|
||||
TRevSummaryCtx2 getSummaryCtx2() { result = sc2 }
|
||||
|
||||
RevPartialAccessPath getAp() { result = ap }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PartialPathNodeRev getASuccessor() {
|
||||
revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
|
||||
this.getAp(), this.getConfiguration())
|
||||
}
|
||||
|
||||
predicate isSink() {
|
||||
config.isSink(node) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = TRevPartialNil()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
@@ -3381,7 +3221,8 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
@@ -3397,7 +3238,7 @@ private module FlowExploration {
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
@@ -3405,7 +3246,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node midNode |
|
||||
@@ -3419,7 +3260,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
|
||||
PartialPathNodePriv mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
@@ -3431,7 +3272,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathOutOfCallable1(
|
||||
PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
@@ -3445,7 +3286,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable(
|
||||
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt kind, DataFlowCall call |
|
||||
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
||||
@@ -3456,7 +3297,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathIntoArg(
|
||||
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||
PartialPathNodePriv mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ArgumentNode arg |
|
||||
@@ -3470,7 +3311,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathIntoCallable0(
|
||||
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||
PartialPathNodePriv mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||
DataFlowCall call, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
partialPathIntoArg(mid, i, outercc, call, ap, config) and
|
||||
@@ -3478,7 +3319,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathIntoCallable(
|
||||
PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
|
||||
PartialPathNodePriv mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
|
||||
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
@@ -3499,7 +3340,7 @@ private module FlowExploration {
|
||||
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
|
||||
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -3512,7 +3353,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
|
||||
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
|
||||
@@ -3522,164 +3363,13 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathThroughCallable(
|
||||
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate revPartialPathStep(
|
||||
PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
|
||||
RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(node, mid.getNode(), config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(node, mid.getNode(), config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
mid.getAp() instanceof RevPartialAccessPathNil and
|
||||
ap = TRevPartialNil() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
jumpStep(node, mid.getNode(), config) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalJumpStep(node, mid.getNode(), config) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
mid.getAp() instanceof RevPartialAccessPathNil and
|
||||
ap = TRevPartialNil() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
revPartialPathReadStep(mid, _, _, node, ap) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(RevPartialAccessPath ap0, Content c |
|
||||
revPartialPathStoreStep(mid, ap0, c, node, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsRev(ap, c, ap0, config)
|
||||
)
|
||||
or
|
||||
exists(ParameterNode p |
|
||||
mid.getNode() = p and
|
||||
viableParamArg(_, p, node) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
exists(ReturnPosition pos |
|
||||
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
|
||||
pos = getReturnPosition(node)
|
||||
)
|
||||
or
|
||||
revPartialPathThroughCallable(mid, node, ap, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate revPartialPathReadStep(
|
||||
PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
|
||||
) {
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
read(node, c, midNode) and
|
||||
ap2.getHead() = c and
|
||||
ap2.len() = unbindInt(ap1.len() + 1)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsRev(
|
||||
RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathReadStep(mid, ap1, c, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathStoreStep(
|
||||
PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
|
||||
) {
|
||||
exists(Node midNode, TypedContent tc |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
store(node, tc, midNode, _) and
|
||||
ap.getHead() = c and
|
||||
config = mid.getConfiguration() and
|
||||
tc.getContent() = c
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathIntoReturn(
|
||||
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
|
||||
DataFlowCall call, RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node out |
|
||||
mid.getNode() = out and
|
||||
viableReturnPosOut(call, pos, out) and
|
||||
sc1 = TRevSummaryCtx1Some(pos) and
|
||||
sc2 = TRevSummaryCtx2Some(ap) and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathFlowsThrough(
|
||||
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeRev mid, ParameterNode p |
|
||||
mid.getNode() = p and
|
||||
p.isParameterOf(_, pos) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
|
||||
revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and
|
||||
revPartialPathFlowsThrough(pos, sc1, sc2, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathThroughCallable(
|
||||
PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, int pos |
|
||||
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
|
||||
node.argumentOf(call, pos)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import FlowExploration
|
||||
@@ -3688,14 +3378,6 @@ private predicate partialFlow(
|
||||
PartialPathNode source, PartialPathNode node, Configuration configuration
|
||||
) {
|
||||
source.getConfiguration() = configuration and
|
||||
source.isFwdSource() and
|
||||
configuration.isSource(source.getNode()) and
|
||||
node = source.getASuccessor+()
|
||||
}
|
||||
|
||||
private predicate revPartialFlow(
|
||||
PartialPathNode node, PartialPathNode sink, Configuration configuration
|
||||
) {
|
||||
sink.getConfiguration() = configuration and
|
||||
sink.isRevSink() and
|
||||
node.getASuccessor+() = sink
|
||||
}
|
||||
|
||||
@@ -112,8 +112,8 @@ abstract class Configuration extends string {
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
* measured in approximate number of interprocedural steps.
|
||||
* Gets the exploration limit for `hasPartialFlow` measured in approximate
|
||||
* number of interprocedural steps.
|
||||
*/
|
||||
int explorationLimit() { none() }
|
||||
|
||||
@@ -123,7 +123,7 @@ abstract class Configuration extends string {
|
||||
* is restricted to be less than or equal to `explorationLimit()`. This
|
||||
* predicate completely disregards sink definitions.
|
||||
*
|
||||
* This predicate is intended for data-flow exploration and debugging and may
|
||||
* This predicate is intended for dataflow exploration and debugging and may
|
||||
* perform poorly if the number of sources is too big and/or the exploration
|
||||
* limit is set too high without using barriers.
|
||||
*
|
||||
@@ -136,29 +136,6 @@ abstract class Configuration extends string {
|
||||
partialFlow(source, node, this) and
|
||||
dist = node.getSourceDistance()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a partial data flow path from `node` to `sink`. The
|
||||
* approximate distance between `node` and the closest sink is `dist` and
|
||||
* is restricted to be less than or equal to `explorationLimit()`. This
|
||||
* predicate completely disregards source definitions.
|
||||
*
|
||||
* This predicate is intended for data-flow exploration and debugging and may
|
||||
* perform poorly if the number of sinks is too big and/or the exploration
|
||||
* limit is set too high without using barriers.
|
||||
*
|
||||
* This predicate is disabled (has no results) by default. Override
|
||||
* `explorationLimit()` with a suitable number to enable this predicate.
|
||||
*
|
||||
* To use this in a `path-problem` query, import the module `PartialPathGraph`.
|
||||
*
|
||||
* Note that reverse flow has slightly lower precision than the corresponding
|
||||
* forward flow, as reverse flow disregards type pruning among other features.
|
||||
*/
|
||||
final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) {
|
||||
revPartialFlow(node, sink, this) and
|
||||
dist = node.getSinkDistance()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2966,26 +2943,12 @@ private module FlowExploration {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
|
||||
exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
|
||||
or
|
||||
exists(DataFlowCallable mid |
|
||||
interestingCallableSink(mid, config) and callableStep(c, mid, config)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TCallableExt =
|
||||
TCallable(DataFlowCallable c, Configuration config) {
|
||||
interestingCallableSrc(c, config) or
|
||||
interestingCallableSink(c, config)
|
||||
} or
|
||||
TCallableSrc() or
|
||||
TCallableSink()
|
||||
TCallable(DataFlowCallable c, Configuration config) { interestingCallableSrc(c, config) } or
|
||||
TCallableSrc()
|
||||
|
||||
private predicate callableExtSrc(TCallableSrc src) { any() }
|
||||
|
||||
private predicate callableExtSink(TCallableSink sink) { any() }
|
||||
|
||||
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
|
||||
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
|
||||
callableStep(c1, c2, config) and
|
||||
@@ -2998,32 +2961,15 @@ private module FlowExploration {
|
||||
config.isSource(n) and
|
||||
ce2 = TCallable(n.getEnclosingCallable(), config)
|
||||
)
|
||||
or
|
||||
exists(Node n, Configuration config |
|
||||
ce2 = TCallableSink() and
|
||||
config.isSink(n) and
|
||||
ce1 = TCallable(n.getEnclosingCallable(), config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) {
|
||||
callableExtStepFwd(ce2, ce1)
|
||||
}
|
||||
|
||||
private int distSrcExt(TCallableExt c) =
|
||||
shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result)
|
||||
|
||||
private int distSinkExt(TCallableExt c) =
|
||||
shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result)
|
||||
|
||||
private int distSrc(DataFlowCallable c, Configuration config) {
|
||||
result = distSrcExt(TCallable(c, config)) - 1
|
||||
}
|
||||
|
||||
private int distSink(DataFlowCallable c, Configuration config) {
|
||||
result = distSinkExt(TCallable(c, config)) - 1
|
||||
}
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
|
||||
@@ -3051,12 +2997,18 @@ private module FlowExploration {
|
||||
or
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
}
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = TFrontNil(t))
|
||||
}
|
||||
}
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
@@ -3067,39 +3019,9 @@ private module FlowExploration {
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TRevPartialAccessPath =
|
||||
TRevPartialNil() or
|
||||
TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s, but only the first
|
||||
* element of the list and its length are tracked.
|
||||
*/
|
||||
private class RevPartialAccessPath extends TRevPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TRevPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TRevPartialNil() and result = 0
|
||||
or
|
||||
this = TRevPartialCons(_, result)
|
||||
}
|
||||
}
|
||||
|
||||
private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil {
|
||||
override string toString() { result = "" }
|
||||
}
|
||||
|
||||
private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons {
|
||||
override string toString() {
|
||||
exists(Content c, int len | this = TRevPartialCons(c, len) |
|
||||
if len = 1
|
||||
then result = "[" + c.toString() + "]"
|
||||
else result = "[" + c.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
override AccessPathFront getFront() {
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3111,16 +3033,8 @@ private module FlowExploration {
|
||||
TSummaryCtx2None() or
|
||||
TSummaryCtx2Some(PartialAccessPath ap)
|
||||
|
||||
private newtype TRevSummaryCtx1 =
|
||||
TRevSummaryCtx1None() or
|
||||
TRevSummaryCtx1Some(ReturnPosition pos)
|
||||
|
||||
private newtype TRevSummaryCtx2 =
|
||||
TRevSummaryCtx2None() or
|
||||
TRevSummaryCtx2Some(RevPartialAccessPath ap)
|
||||
|
||||
private newtype TPartialPathNode =
|
||||
TPartialPathNodeFwd(
|
||||
TPartialPathNodeMk(
|
||||
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
@@ -3134,23 +3048,6 @@ private module FlowExploration {
|
||||
or
|
||||
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
|
||||
distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
} or
|
||||
TPartialPathNodeRev(
|
||||
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
config.isSink(node) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = TRevPartialNil() and
|
||||
not fullBarrier(node, config) and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -3158,7 +3055,7 @@ private module FlowExploration {
|
||||
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
exists(PartialPathNode mid |
|
||||
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
if node instanceof CastingNode
|
||||
@@ -3210,32 +3107,15 @@ private module FlowExploration {
|
||||
result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the approximate distance to the nearest sink measured in number
|
||||
* of interprocedural steps.
|
||||
*/
|
||||
int getSinkDistance() {
|
||||
result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s |
|
||||
s = this.(PartialPathNodeFwd).getAp().toString() or
|
||||
s = this.(PartialPathNodeRev).getAp().toString()
|
||||
|
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
private string ppCtx() {
|
||||
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
|
||||
result = " <" + this.(PartialPathNodePriv).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
/** Holds if this is a source in a forward-flow path. */
|
||||
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
|
||||
|
||||
/** Holds if this is a sink in a reverse-flow path. */
|
||||
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3246,7 +3126,7 @@ private module FlowExploration {
|
||||
query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b }
|
||||
}
|
||||
|
||||
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
|
||||
private class PartialPathNodePriv extends PartialPathNode {
|
||||
Node node;
|
||||
CallContext cc;
|
||||
TSummaryCtx1 sc1;
|
||||
@@ -3254,7 +3134,7 @@ private module FlowExploration {
|
||||
PartialAccessPath ap;
|
||||
Configuration config;
|
||||
|
||||
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
|
||||
PartialPathNodePriv() { this = TPartialPathNodeMk(node, cc, sc1, sc2, ap, config) }
|
||||
|
||||
override Node getNode() { result = node }
|
||||
|
||||
@@ -3268,54 +3148,14 @@ private module FlowExploration {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PartialPathNodeFwd getASuccessor() {
|
||||
override PartialPathNodePriv getASuccessor() {
|
||||
partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
|
||||
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
|
||||
}
|
||||
|
||||
predicate isSource() {
|
||||
config.isSource(node) and
|
||||
cc instanceof CallContextAny and
|
||||
sc1 = TSummaryCtx1None() and
|
||||
sc2 = TSummaryCtx2None() and
|
||||
ap instanceof TPartialNil
|
||||
}
|
||||
}
|
||||
|
||||
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
|
||||
Node node;
|
||||
TRevSummaryCtx1 sc1;
|
||||
TRevSummaryCtx2 sc2;
|
||||
RevPartialAccessPath ap;
|
||||
Configuration config;
|
||||
|
||||
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
|
||||
|
||||
override Node getNode() { result = node }
|
||||
|
||||
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
|
||||
|
||||
TRevSummaryCtx2 getSummaryCtx2() { result = sc2 }
|
||||
|
||||
RevPartialAccessPath getAp() { result = ap }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PartialPathNodeRev getASuccessor() {
|
||||
revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
|
||||
this.getAp(), this.getConfiguration())
|
||||
}
|
||||
|
||||
predicate isSink() {
|
||||
config.isSink(node) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = TRevPartialNil()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
@@ -3381,7 +3221,8 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
@@ -3397,7 +3238,7 @@ private module FlowExploration {
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
@@ -3405,7 +3246,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node midNode |
|
||||
@@ -3419,7 +3260,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
|
||||
PartialPathNodePriv mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
@@ -3431,7 +3272,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathOutOfCallable1(
|
||||
PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
@@ -3445,7 +3286,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable(
|
||||
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt kind, DataFlowCall call |
|
||||
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
||||
@@ -3456,7 +3297,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathIntoArg(
|
||||
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||
PartialPathNodePriv mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ArgumentNode arg |
|
||||
@@ -3470,7 +3311,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathIntoCallable0(
|
||||
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||
PartialPathNodePriv mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||
DataFlowCall call, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
partialPathIntoArg(mid, i, outercc, call, ap, config) and
|
||||
@@ -3478,7 +3319,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathIntoCallable(
|
||||
PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
|
||||
PartialPathNodePriv mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
|
||||
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
@@ -3499,7 +3340,7 @@ private module FlowExploration {
|
||||
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
|
||||
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -3512,7 +3353,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
|
||||
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
|
||||
@@ -3522,164 +3363,13 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathThroughCallable(
|
||||
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate revPartialPathStep(
|
||||
PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
|
||||
RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(node, mid.getNode(), config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(node, mid.getNode(), config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
mid.getAp() instanceof RevPartialAccessPathNil and
|
||||
ap = TRevPartialNil() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
jumpStep(node, mid.getNode(), config) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalJumpStep(node, mid.getNode(), config) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
mid.getAp() instanceof RevPartialAccessPathNil and
|
||||
ap = TRevPartialNil() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
revPartialPathReadStep(mid, _, _, node, ap) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(RevPartialAccessPath ap0, Content c |
|
||||
revPartialPathStoreStep(mid, ap0, c, node, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsRev(ap, c, ap0, config)
|
||||
)
|
||||
or
|
||||
exists(ParameterNode p |
|
||||
mid.getNode() = p and
|
||||
viableParamArg(_, p, node) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
exists(ReturnPosition pos |
|
||||
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
|
||||
pos = getReturnPosition(node)
|
||||
)
|
||||
or
|
||||
revPartialPathThroughCallable(mid, node, ap, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate revPartialPathReadStep(
|
||||
PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
|
||||
) {
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
read(node, c, midNode) and
|
||||
ap2.getHead() = c and
|
||||
ap2.len() = unbindInt(ap1.len() + 1)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsRev(
|
||||
RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathReadStep(mid, ap1, c, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathStoreStep(
|
||||
PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
|
||||
) {
|
||||
exists(Node midNode, TypedContent tc |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
store(node, tc, midNode, _) and
|
||||
ap.getHead() = c and
|
||||
config = mid.getConfiguration() and
|
||||
tc.getContent() = c
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathIntoReturn(
|
||||
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
|
||||
DataFlowCall call, RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node out |
|
||||
mid.getNode() = out and
|
||||
viableReturnPosOut(call, pos, out) and
|
||||
sc1 = TRevSummaryCtx1Some(pos) and
|
||||
sc2 = TRevSummaryCtx2Some(ap) and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathFlowsThrough(
|
||||
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeRev mid, ParameterNode p |
|
||||
mid.getNode() = p and
|
||||
p.isParameterOf(_, pos) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
|
||||
revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and
|
||||
revPartialPathFlowsThrough(pos, sc1, sc2, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathThroughCallable(
|
||||
PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, int pos |
|
||||
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
|
||||
node.argumentOf(call, pos)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import FlowExploration
|
||||
@@ -3688,14 +3378,6 @@ private predicate partialFlow(
|
||||
PartialPathNode source, PartialPathNode node, Configuration configuration
|
||||
) {
|
||||
source.getConfiguration() = configuration and
|
||||
source.isFwdSource() and
|
||||
configuration.isSource(source.getNode()) and
|
||||
node = source.getASuccessor+()
|
||||
}
|
||||
|
||||
private predicate revPartialFlow(
|
||||
PartialPathNode node, PartialPathNode sink, Configuration configuration
|
||||
) {
|
||||
sink.getConfiguration() = configuration and
|
||||
sink.isRevSink() and
|
||||
node.getASuccessor+() = sink
|
||||
}
|
||||
|
||||
@@ -112,8 +112,8 @@ abstract class Configuration extends string {
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
* measured in approximate number of interprocedural steps.
|
||||
* Gets the exploration limit for `hasPartialFlow` measured in approximate
|
||||
* number of interprocedural steps.
|
||||
*/
|
||||
int explorationLimit() { none() }
|
||||
|
||||
@@ -123,7 +123,7 @@ abstract class Configuration extends string {
|
||||
* is restricted to be less than or equal to `explorationLimit()`. This
|
||||
* predicate completely disregards sink definitions.
|
||||
*
|
||||
* This predicate is intended for data-flow exploration and debugging and may
|
||||
* This predicate is intended for dataflow exploration and debugging and may
|
||||
* perform poorly if the number of sources is too big and/or the exploration
|
||||
* limit is set too high without using barriers.
|
||||
*
|
||||
@@ -136,29 +136,6 @@ abstract class Configuration extends string {
|
||||
partialFlow(source, node, this) and
|
||||
dist = node.getSourceDistance()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a partial data flow path from `node` to `sink`. The
|
||||
* approximate distance between `node` and the closest sink is `dist` and
|
||||
* is restricted to be less than or equal to `explorationLimit()`. This
|
||||
* predicate completely disregards source definitions.
|
||||
*
|
||||
* This predicate is intended for data-flow exploration and debugging and may
|
||||
* perform poorly if the number of sinks is too big and/or the exploration
|
||||
* limit is set too high without using barriers.
|
||||
*
|
||||
* This predicate is disabled (has no results) by default. Override
|
||||
* `explorationLimit()` with a suitable number to enable this predicate.
|
||||
*
|
||||
* To use this in a `path-problem` query, import the module `PartialPathGraph`.
|
||||
*
|
||||
* Note that reverse flow has slightly lower precision than the corresponding
|
||||
* forward flow, as reverse flow disregards type pruning among other features.
|
||||
*/
|
||||
final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) {
|
||||
revPartialFlow(node, sink, this) and
|
||||
dist = node.getSinkDistance()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2966,26 +2943,12 @@ private module FlowExploration {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
|
||||
exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
|
||||
or
|
||||
exists(DataFlowCallable mid |
|
||||
interestingCallableSink(mid, config) and callableStep(c, mid, config)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TCallableExt =
|
||||
TCallable(DataFlowCallable c, Configuration config) {
|
||||
interestingCallableSrc(c, config) or
|
||||
interestingCallableSink(c, config)
|
||||
} or
|
||||
TCallableSrc() or
|
||||
TCallableSink()
|
||||
TCallable(DataFlowCallable c, Configuration config) { interestingCallableSrc(c, config) } or
|
||||
TCallableSrc()
|
||||
|
||||
private predicate callableExtSrc(TCallableSrc src) { any() }
|
||||
|
||||
private predicate callableExtSink(TCallableSink sink) { any() }
|
||||
|
||||
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
|
||||
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
|
||||
callableStep(c1, c2, config) and
|
||||
@@ -2998,32 +2961,15 @@ private module FlowExploration {
|
||||
config.isSource(n) and
|
||||
ce2 = TCallable(n.getEnclosingCallable(), config)
|
||||
)
|
||||
or
|
||||
exists(Node n, Configuration config |
|
||||
ce2 = TCallableSink() and
|
||||
config.isSink(n) and
|
||||
ce1 = TCallable(n.getEnclosingCallable(), config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) {
|
||||
callableExtStepFwd(ce2, ce1)
|
||||
}
|
||||
|
||||
private int distSrcExt(TCallableExt c) =
|
||||
shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result)
|
||||
|
||||
private int distSinkExt(TCallableExt c) =
|
||||
shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result)
|
||||
|
||||
private int distSrc(DataFlowCallable c, Configuration config) {
|
||||
result = distSrcExt(TCallable(c, config)) - 1
|
||||
}
|
||||
|
||||
private int distSink(DataFlowCallable c, Configuration config) {
|
||||
result = distSinkExt(TCallable(c, config)) - 1
|
||||
}
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
|
||||
@@ -3051,12 +2997,18 @@ private module FlowExploration {
|
||||
or
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
}
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = TFrontNil(t))
|
||||
}
|
||||
}
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
@@ -3067,39 +3019,9 @@ private module FlowExploration {
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TRevPartialAccessPath =
|
||||
TRevPartialNil() or
|
||||
TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s, but only the first
|
||||
* element of the list and its length are tracked.
|
||||
*/
|
||||
private class RevPartialAccessPath extends TRevPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TRevPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TRevPartialNil() and result = 0
|
||||
or
|
||||
this = TRevPartialCons(_, result)
|
||||
}
|
||||
}
|
||||
|
||||
private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil {
|
||||
override string toString() { result = "" }
|
||||
}
|
||||
|
||||
private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons {
|
||||
override string toString() {
|
||||
exists(Content c, int len | this = TRevPartialCons(c, len) |
|
||||
if len = 1
|
||||
then result = "[" + c.toString() + "]"
|
||||
else result = "[" + c.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
override AccessPathFront getFront() {
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3111,16 +3033,8 @@ private module FlowExploration {
|
||||
TSummaryCtx2None() or
|
||||
TSummaryCtx2Some(PartialAccessPath ap)
|
||||
|
||||
private newtype TRevSummaryCtx1 =
|
||||
TRevSummaryCtx1None() or
|
||||
TRevSummaryCtx1Some(ReturnPosition pos)
|
||||
|
||||
private newtype TRevSummaryCtx2 =
|
||||
TRevSummaryCtx2None() or
|
||||
TRevSummaryCtx2Some(RevPartialAccessPath ap)
|
||||
|
||||
private newtype TPartialPathNode =
|
||||
TPartialPathNodeFwd(
|
||||
TPartialPathNodeMk(
|
||||
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
@@ -3134,23 +3048,6 @@ private module FlowExploration {
|
||||
or
|
||||
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
|
||||
distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
} or
|
||||
TPartialPathNodeRev(
|
||||
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
config.isSink(node) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = TRevPartialNil() and
|
||||
not fullBarrier(node, config) and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -3158,7 +3055,7 @@ private module FlowExploration {
|
||||
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
exists(PartialPathNode mid |
|
||||
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
if node instanceof CastingNode
|
||||
@@ -3210,32 +3107,15 @@ private module FlowExploration {
|
||||
result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the approximate distance to the nearest sink measured in number
|
||||
* of interprocedural steps.
|
||||
*/
|
||||
int getSinkDistance() {
|
||||
result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s |
|
||||
s = this.(PartialPathNodeFwd).getAp().toString() or
|
||||
s = this.(PartialPathNodeRev).getAp().toString()
|
||||
|
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
private string ppCtx() {
|
||||
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
|
||||
result = " <" + this.(PartialPathNodePriv).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
/** Holds if this is a source in a forward-flow path. */
|
||||
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
|
||||
|
||||
/** Holds if this is a sink in a reverse-flow path. */
|
||||
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3246,7 +3126,7 @@ private module FlowExploration {
|
||||
query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b }
|
||||
}
|
||||
|
||||
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
|
||||
private class PartialPathNodePriv extends PartialPathNode {
|
||||
Node node;
|
||||
CallContext cc;
|
||||
TSummaryCtx1 sc1;
|
||||
@@ -3254,7 +3134,7 @@ private module FlowExploration {
|
||||
PartialAccessPath ap;
|
||||
Configuration config;
|
||||
|
||||
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
|
||||
PartialPathNodePriv() { this = TPartialPathNodeMk(node, cc, sc1, sc2, ap, config) }
|
||||
|
||||
override Node getNode() { result = node }
|
||||
|
||||
@@ -3268,54 +3148,14 @@ private module FlowExploration {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PartialPathNodeFwd getASuccessor() {
|
||||
override PartialPathNodePriv getASuccessor() {
|
||||
partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
|
||||
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
|
||||
}
|
||||
|
||||
predicate isSource() {
|
||||
config.isSource(node) and
|
||||
cc instanceof CallContextAny and
|
||||
sc1 = TSummaryCtx1None() and
|
||||
sc2 = TSummaryCtx2None() and
|
||||
ap instanceof TPartialNil
|
||||
}
|
||||
}
|
||||
|
||||
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
|
||||
Node node;
|
||||
TRevSummaryCtx1 sc1;
|
||||
TRevSummaryCtx2 sc2;
|
||||
RevPartialAccessPath ap;
|
||||
Configuration config;
|
||||
|
||||
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
|
||||
|
||||
override Node getNode() { result = node }
|
||||
|
||||
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
|
||||
|
||||
TRevSummaryCtx2 getSummaryCtx2() { result = sc2 }
|
||||
|
||||
RevPartialAccessPath getAp() { result = ap }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PartialPathNodeRev getASuccessor() {
|
||||
revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
|
||||
this.getAp(), this.getConfiguration())
|
||||
}
|
||||
|
||||
predicate isSink() {
|
||||
config.isSink(node) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = TRevPartialNil()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
@@ -3381,7 +3221,8 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
@@ -3397,7 +3238,7 @@ private module FlowExploration {
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
@@ -3405,7 +3246,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node midNode |
|
||||
@@ -3419,7 +3260,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
|
||||
PartialPathNodePriv mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
@@ -3431,7 +3272,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathOutOfCallable1(
|
||||
PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
@@ -3445,7 +3286,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable(
|
||||
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt kind, DataFlowCall call |
|
||||
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
||||
@@ -3456,7 +3297,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathIntoArg(
|
||||
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||
PartialPathNodePriv mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ArgumentNode arg |
|
||||
@@ -3470,7 +3311,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathIntoCallable0(
|
||||
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||
PartialPathNodePriv mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||
DataFlowCall call, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
partialPathIntoArg(mid, i, outercc, call, ap, config) and
|
||||
@@ -3478,7 +3319,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathIntoCallable(
|
||||
PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
|
||||
PartialPathNodePriv mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
|
||||
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
@@ -3499,7 +3340,7 @@ private module FlowExploration {
|
||||
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
|
||||
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -3512,7 +3353,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
|
||||
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
|
||||
@@ -3522,164 +3363,13 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathThroughCallable(
|
||||
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate revPartialPathStep(
|
||||
PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
|
||||
RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(node, mid.getNode(), config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(node, mid.getNode(), config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
mid.getAp() instanceof RevPartialAccessPathNil and
|
||||
ap = TRevPartialNil() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
jumpStep(node, mid.getNode(), config) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalJumpStep(node, mid.getNode(), config) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
mid.getAp() instanceof RevPartialAccessPathNil and
|
||||
ap = TRevPartialNil() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
revPartialPathReadStep(mid, _, _, node, ap) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(RevPartialAccessPath ap0, Content c |
|
||||
revPartialPathStoreStep(mid, ap0, c, node, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsRev(ap, c, ap0, config)
|
||||
)
|
||||
or
|
||||
exists(ParameterNode p |
|
||||
mid.getNode() = p and
|
||||
viableParamArg(_, p, node) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
exists(ReturnPosition pos |
|
||||
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
|
||||
pos = getReturnPosition(node)
|
||||
)
|
||||
or
|
||||
revPartialPathThroughCallable(mid, node, ap, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate revPartialPathReadStep(
|
||||
PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
|
||||
) {
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
read(node, c, midNode) and
|
||||
ap2.getHead() = c and
|
||||
ap2.len() = unbindInt(ap1.len() + 1)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsRev(
|
||||
RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathReadStep(mid, ap1, c, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathStoreStep(
|
||||
PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
|
||||
) {
|
||||
exists(Node midNode, TypedContent tc |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
store(node, tc, midNode, _) and
|
||||
ap.getHead() = c and
|
||||
config = mid.getConfiguration() and
|
||||
tc.getContent() = c
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathIntoReturn(
|
||||
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
|
||||
DataFlowCall call, RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node out |
|
||||
mid.getNode() = out and
|
||||
viableReturnPosOut(call, pos, out) and
|
||||
sc1 = TRevSummaryCtx1Some(pos) and
|
||||
sc2 = TRevSummaryCtx2Some(ap) and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathFlowsThrough(
|
||||
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeRev mid, ParameterNode p |
|
||||
mid.getNode() = p and
|
||||
p.isParameterOf(_, pos) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
|
||||
revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and
|
||||
revPartialPathFlowsThrough(pos, sc1, sc2, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathThroughCallable(
|
||||
PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, int pos |
|
||||
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
|
||||
node.argumentOf(call, pos)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import FlowExploration
|
||||
@@ -3688,14 +3378,6 @@ private predicate partialFlow(
|
||||
PartialPathNode source, PartialPathNode node, Configuration configuration
|
||||
) {
|
||||
source.getConfiguration() = configuration and
|
||||
source.isFwdSource() and
|
||||
configuration.isSource(source.getNode()) and
|
||||
node = source.getASuccessor+()
|
||||
}
|
||||
|
||||
private predicate revPartialFlow(
|
||||
PartialPathNode node, PartialPathNode sink, Configuration configuration
|
||||
) {
|
||||
sink.getConfiguration() = configuration and
|
||||
sink.isRevSink() and
|
||||
node.getASuccessor+() = sink
|
||||
}
|
||||
|
||||
@@ -112,8 +112,8 @@ abstract class Configuration extends string {
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
* measured in approximate number of interprocedural steps.
|
||||
* Gets the exploration limit for `hasPartialFlow` measured in approximate
|
||||
* number of interprocedural steps.
|
||||
*/
|
||||
int explorationLimit() { none() }
|
||||
|
||||
@@ -123,7 +123,7 @@ abstract class Configuration extends string {
|
||||
* is restricted to be less than or equal to `explorationLimit()`. This
|
||||
* predicate completely disregards sink definitions.
|
||||
*
|
||||
* This predicate is intended for data-flow exploration and debugging and may
|
||||
* This predicate is intended for dataflow exploration and debugging and may
|
||||
* perform poorly if the number of sources is too big and/or the exploration
|
||||
* limit is set too high without using barriers.
|
||||
*
|
||||
@@ -136,29 +136,6 @@ abstract class Configuration extends string {
|
||||
partialFlow(source, node, this) and
|
||||
dist = node.getSourceDistance()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a partial data flow path from `node` to `sink`. The
|
||||
* approximate distance between `node` and the closest sink is `dist` and
|
||||
* is restricted to be less than or equal to `explorationLimit()`. This
|
||||
* predicate completely disregards source definitions.
|
||||
*
|
||||
* This predicate is intended for data-flow exploration and debugging and may
|
||||
* perform poorly if the number of sinks is too big and/or the exploration
|
||||
* limit is set too high without using barriers.
|
||||
*
|
||||
* This predicate is disabled (has no results) by default. Override
|
||||
* `explorationLimit()` with a suitable number to enable this predicate.
|
||||
*
|
||||
* To use this in a `path-problem` query, import the module `PartialPathGraph`.
|
||||
*
|
||||
* Note that reverse flow has slightly lower precision than the corresponding
|
||||
* forward flow, as reverse flow disregards type pruning among other features.
|
||||
*/
|
||||
final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) {
|
||||
revPartialFlow(node, sink, this) and
|
||||
dist = node.getSinkDistance()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2966,26 +2943,12 @@ private module FlowExploration {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
|
||||
exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
|
||||
or
|
||||
exists(DataFlowCallable mid |
|
||||
interestingCallableSink(mid, config) and callableStep(c, mid, config)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TCallableExt =
|
||||
TCallable(DataFlowCallable c, Configuration config) {
|
||||
interestingCallableSrc(c, config) or
|
||||
interestingCallableSink(c, config)
|
||||
} or
|
||||
TCallableSrc() or
|
||||
TCallableSink()
|
||||
TCallable(DataFlowCallable c, Configuration config) { interestingCallableSrc(c, config) } or
|
||||
TCallableSrc()
|
||||
|
||||
private predicate callableExtSrc(TCallableSrc src) { any() }
|
||||
|
||||
private predicate callableExtSink(TCallableSink sink) { any() }
|
||||
|
||||
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
|
||||
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
|
||||
callableStep(c1, c2, config) and
|
||||
@@ -2998,32 +2961,15 @@ private module FlowExploration {
|
||||
config.isSource(n) and
|
||||
ce2 = TCallable(n.getEnclosingCallable(), config)
|
||||
)
|
||||
or
|
||||
exists(Node n, Configuration config |
|
||||
ce2 = TCallableSink() and
|
||||
config.isSink(n) and
|
||||
ce1 = TCallable(n.getEnclosingCallable(), config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) {
|
||||
callableExtStepFwd(ce2, ce1)
|
||||
}
|
||||
|
||||
private int distSrcExt(TCallableExt c) =
|
||||
shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result)
|
||||
|
||||
private int distSinkExt(TCallableExt c) =
|
||||
shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result)
|
||||
|
||||
private int distSrc(DataFlowCallable c, Configuration config) {
|
||||
result = distSrcExt(TCallable(c, config)) - 1
|
||||
}
|
||||
|
||||
private int distSink(DataFlowCallable c, Configuration config) {
|
||||
result = distSinkExt(TCallable(c, config)) - 1
|
||||
}
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
|
||||
@@ -3051,12 +2997,18 @@ private module FlowExploration {
|
||||
or
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
}
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = TFrontNil(t))
|
||||
}
|
||||
}
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
@@ -3067,39 +3019,9 @@ private module FlowExploration {
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TRevPartialAccessPath =
|
||||
TRevPartialNil() or
|
||||
TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s, but only the first
|
||||
* element of the list and its length are tracked.
|
||||
*/
|
||||
private class RevPartialAccessPath extends TRevPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TRevPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TRevPartialNil() and result = 0
|
||||
or
|
||||
this = TRevPartialCons(_, result)
|
||||
}
|
||||
}
|
||||
|
||||
private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil {
|
||||
override string toString() { result = "" }
|
||||
}
|
||||
|
||||
private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons {
|
||||
override string toString() {
|
||||
exists(Content c, int len | this = TRevPartialCons(c, len) |
|
||||
if len = 1
|
||||
then result = "[" + c.toString() + "]"
|
||||
else result = "[" + c.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
override AccessPathFront getFront() {
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3111,16 +3033,8 @@ private module FlowExploration {
|
||||
TSummaryCtx2None() or
|
||||
TSummaryCtx2Some(PartialAccessPath ap)
|
||||
|
||||
private newtype TRevSummaryCtx1 =
|
||||
TRevSummaryCtx1None() or
|
||||
TRevSummaryCtx1Some(ReturnPosition pos)
|
||||
|
||||
private newtype TRevSummaryCtx2 =
|
||||
TRevSummaryCtx2None() or
|
||||
TRevSummaryCtx2Some(RevPartialAccessPath ap)
|
||||
|
||||
private newtype TPartialPathNode =
|
||||
TPartialPathNodeFwd(
|
||||
TPartialPathNodeMk(
|
||||
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
@@ -3134,23 +3048,6 @@ private module FlowExploration {
|
||||
or
|
||||
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
|
||||
distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
} or
|
||||
TPartialPathNodeRev(
|
||||
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
config.isSink(node) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = TRevPartialNil() and
|
||||
not fullBarrier(node, config) and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -3158,7 +3055,7 @@ private module FlowExploration {
|
||||
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
exists(PartialPathNode mid |
|
||||
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
if node instanceof CastingNode
|
||||
@@ -3210,32 +3107,15 @@ private module FlowExploration {
|
||||
result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the approximate distance to the nearest sink measured in number
|
||||
* of interprocedural steps.
|
||||
*/
|
||||
int getSinkDistance() {
|
||||
result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s |
|
||||
s = this.(PartialPathNodeFwd).getAp().toString() or
|
||||
s = this.(PartialPathNodeRev).getAp().toString()
|
||||
|
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
private string ppCtx() {
|
||||
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
|
||||
result = " <" + this.(PartialPathNodePriv).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
/** Holds if this is a source in a forward-flow path. */
|
||||
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
|
||||
|
||||
/** Holds if this is a sink in a reverse-flow path. */
|
||||
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3246,7 +3126,7 @@ private module FlowExploration {
|
||||
query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b }
|
||||
}
|
||||
|
||||
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
|
||||
private class PartialPathNodePriv extends PartialPathNode {
|
||||
Node node;
|
||||
CallContext cc;
|
||||
TSummaryCtx1 sc1;
|
||||
@@ -3254,7 +3134,7 @@ private module FlowExploration {
|
||||
PartialAccessPath ap;
|
||||
Configuration config;
|
||||
|
||||
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
|
||||
PartialPathNodePriv() { this = TPartialPathNodeMk(node, cc, sc1, sc2, ap, config) }
|
||||
|
||||
override Node getNode() { result = node }
|
||||
|
||||
@@ -3268,54 +3148,14 @@ private module FlowExploration {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PartialPathNodeFwd getASuccessor() {
|
||||
override PartialPathNodePriv getASuccessor() {
|
||||
partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
|
||||
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
|
||||
}
|
||||
|
||||
predicate isSource() {
|
||||
config.isSource(node) and
|
||||
cc instanceof CallContextAny and
|
||||
sc1 = TSummaryCtx1None() and
|
||||
sc2 = TSummaryCtx2None() and
|
||||
ap instanceof TPartialNil
|
||||
}
|
||||
}
|
||||
|
||||
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
|
||||
Node node;
|
||||
TRevSummaryCtx1 sc1;
|
||||
TRevSummaryCtx2 sc2;
|
||||
RevPartialAccessPath ap;
|
||||
Configuration config;
|
||||
|
||||
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
|
||||
|
||||
override Node getNode() { result = node }
|
||||
|
||||
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
|
||||
|
||||
TRevSummaryCtx2 getSummaryCtx2() { result = sc2 }
|
||||
|
||||
RevPartialAccessPath getAp() { result = ap }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PartialPathNodeRev getASuccessor() {
|
||||
revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
|
||||
this.getAp(), this.getConfiguration())
|
||||
}
|
||||
|
||||
predicate isSink() {
|
||||
config.isSink(node) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = TRevPartialNil()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
@@ -3381,7 +3221,8 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
@@ -3397,7 +3238,7 @@ private module FlowExploration {
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
@@ -3405,7 +3246,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node midNode |
|
||||
@@ -3419,7 +3260,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
|
||||
PartialPathNodePriv mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
@@ -3431,7 +3272,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathOutOfCallable1(
|
||||
PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
@@ -3445,7 +3286,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable(
|
||||
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt kind, DataFlowCall call |
|
||||
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
||||
@@ -3456,7 +3297,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathIntoArg(
|
||||
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||
PartialPathNodePriv mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ArgumentNode arg |
|
||||
@@ -3470,7 +3311,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathIntoCallable0(
|
||||
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||
PartialPathNodePriv mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||
DataFlowCall call, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
partialPathIntoArg(mid, i, outercc, call, ap, config) and
|
||||
@@ -3478,7 +3319,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathIntoCallable(
|
||||
PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
|
||||
PartialPathNodePriv mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
|
||||
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
@@ -3499,7 +3340,7 @@ private module FlowExploration {
|
||||
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
|
||||
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -3512,7 +3353,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
|
||||
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
|
||||
@@ -3522,164 +3363,13 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathThroughCallable(
|
||||
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate revPartialPathStep(
|
||||
PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
|
||||
RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(node, mid.getNode(), config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(node, mid.getNode(), config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
mid.getAp() instanceof RevPartialAccessPathNil and
|
||||
ap = TRevPartialNil() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
jumpStep(node, mid.getNode(), config) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalJumpStep(node, mid.getNode(), config) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
mid.getAp() instanceof RevPartialAccessPathNil and
|
||||
ap = TRevPartialNil() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
revPartialPathReadStep(mid, _, _, node, ap) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(RevPartialAccessPath ap0, Content c |
|
||||
revPartialPathStoreStep(mid, ap0, c, node, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsRev(ap, c, ap0, config)
|
||||
)
|
||||
or
|
||||
exists(ParameterNode p |
|
||||
mid.getNode() = p and
|
||||
viableParamArg(_, p, node) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
exists(ReturnPosition pos |
|
||||
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
|
||||
pos = getReturnPosition(node)
|
||||
)
|
||||
or
|
||||
revPartialPathThroughCallable(mid, node, ap, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate revPartialPathReadStep(
|
||||
PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
|
||||
) {
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
read(node, c, midNode) and
|
||||
ap2.getHead() = c and
|
||||
ap2.len() = unbindInt(ap1.len() + 1)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsRev(
|
||||
RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathReadStep(mid, ap1, c, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathStoreStep(
|
||||
PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
|
||||
) {
|
||||
exists(Node midNode, TypedContent tc |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
store(node, tc, midNode, _) and
|
||||
ap.getHead() = c and
|
||||
config = mid.getConfiguration() and
|
||||
tc.getContent() = c
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathIntoReturn(
|
||||
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
|
||||
DataFlowCall call, RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node out |
|
||||
mid.getNode() = out and
|
||||
viableReturnPosOut(call, pos, out) and
|
||||
sc1 = TRevSummaryCtx1Some(pos) and
|
||||
sc2 = TRevSummaryCtx2Some(ap) and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathFlowsThrough(
|
||||
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeRev mid, ParameterNode p |
|
||||
mid.getNode() = p and
|
||||
p.isParameterOf(_, pos) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
|
||||
revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and
|
||||
revPartialPathFlowsThrough(pos, sc1, sc2, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathThroughCallable(
|
||||
PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, int pos |
|
||||
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
|
||||
node.argumentOf(call, pos)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import FlowExploration
|
||||
@@ -3688,14 +3378,6 @@ private predicate partialFlow(
|
||||
PartialPathNode source, PartialPathNode node, Configuration configuration
|
||||
) {
|
||||
source.getConfiguration() = configuration and
|
||||
source.isFwdSource() and
|
||||
configuration.isSource(source.getNode()) and
|
||||
node = source.getASuccessor+()
|
||||
}
|
||||
|
||||
private predicate revPartialFlow(
|
||||
PartialPathNode node, PartialPathNode sink, Configuration configuration
|
||||
) {
|
||||
sink.getConfiguration() = configuration and
|
||||
sink.isRevSink() and
|
||||
node.getASuccessor+() = sink
|
||||
}
|
||||
|
||||
@@ -677,11 +677,6 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) {
|
||||
exists(DataFlowFunction f, FunctionInput inModel, FunctionOutput outModel |
|
||||
f.hasDataFlow(inModel, outModel) and
|
||||
(
|
||||
exists(int iIn |
|
||||
inModel.isParameterDeref(iIn) and
|
||||
call.passesByReference(iIn, fromExpr)
|
||||
)
|
||||
or
|
||||
exists(int iIn |
|
||||
inModel.isParameter(iIn) and
|
||||
fromExpr = call.getArgument(iIn)
|
||||
|
||||
@@ -620,7 +620,8 @@ module FlowVar_internal {
|
||||
private predicate largeVariable(Variable v, int liveBlocks, int defs) {
|
||||
liveBlocks = strictcount(SubBasicBlock sbb | variableLiveInSBB(sbb, v)) and
|
||||
defs = strictcount(SubBasicBlock sbb | exists(TBlockVar(sbb, v))) and
|
||||
liveBlocks * defs > 1000000
|
||||
// Convert to float to avoid int overflow (32-bit two's complement)
|
||||
liveBlocks.(float) * defs.(float) > 100000.0
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -441,6 +441,40 @@ class ConstructorCall extends FunctionCall {
|
||||
override Constructor getTarget() { result = super.getTarget() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `throw` expression.
|
||||
* ```
|
||||
* throw Exc(2);
|
||||
* ```
|
||||
*/
|
||||
class ThrowExpr extends Expr, @throw_expr {
|
||||
/**
|
||||
* Gets the expression that will be thrown, if any. There is no result if
|
||||
* `this` is a `ReThrowExpr`.
|
||||
*/
|
||||
Expr getExpr() { result = this.getChild(0) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ThrowExpr" }
|
||||
|
||||
override string toString() { result = "throw ..." }
|
||||
|
||||
override int getPrecedence() { result = 1 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `throw` expression with no argument (which causes the current exception to be re-thrown).
|
||||
* ```
|
||||
* throw;
|
||||
* ```
|
||||
*/
|
||||
class ReThrowExpr extends ThrowExpr {
|
||||
ReThrowExpr() { this.getType() instanceof VoidType }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ReThrowExpr" }
|
||||
|
||||
override string toString() { result = "re-throw exception " }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a destructor.
|
||||
* ```
|
||||
|
||||
@@ -840,28 +840,6 @@ class ArrayToPointerConversion extends Conversion, @array_to_pointer {
|
||||
override predicate mayBeGloballyImpure() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing a temporary object created as part of an expression.
|
||||
*
|
||||
* This is most commonly seen in the following cases:
|
||||
* ```c++
|
||||
* // when binding a reference to a prvalue
|
||||
* const std::string& r = std::string("text");
|
||||
*
|
||||
* // when performing member access on a class prvalue
|
||||
* strlen(std::string("text").c_str());
|
||||
*
|
||||
* // when a prvalue of a type with a destructor is discarded
|
||||
* s.substr(0, 5); // Return value is discarded but requires destruction
|
||||
* ```
|
||||
*/
|
||||
class TemporaryObjectExpr extends Conversion, @temp_init {
|
||||
/** Gets a textual representation of this conversion. */
|
||||
override string toString() { result = "temporary object" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TemporaryObjectExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing the Cast sub-class of entity `cast`.
|
||||
*/
|
||||
|
||||
@@ -64,7 +64,7 @@ class RelationalOperation extends ComparisonOperation, @rel_op_expr {
|
||||
* if the overall expression evaluates to `true`; for example on
|
||||
* `x <= 20` this is the `20`, and on `y > 0` it is `y`.
|
||||
*/
|
||||
Expr getGreaterOperand() { none() } // overridden in subclasses
|
||||
abstract Expr getGreaterOperand();
|
||||
|
||||
/**
|
||||
* Gets the operand on the "lesser" (or "lesser-or-equal") side
|
||||
@@ -72,7 +72,7 @@ class RelationalOperation extends ComparisonOperation, @rel_op_expr {
|
||||
* if the overall expression evaluates to `true`; for example on
|
||||
* `x <= 20` this is `x`, and on `y > 0` it is the `0`.
|
||||
*/
|
||||
Expr getLesserOperand() { none() } // overridden in subclasses
|
||||
abstract Expr getLesserOperand();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,7 @@ import semmle.code.cpp.Element
|
||||
private import semmle.code.cpp.Enclosing
|
||||
private import semmle.code.cpp.internal.ResolveClass
|
||||
private import semmle.code.cpp.internal.AddressConstantExpression
|
||||
private import semmle.code.cpp.models.implementations.Allocation
|
||||
|
||||
/**
|
||||
* A C/C++ expression.
|
||||
@@ -838,7 +839,7 @@ class NewOrNewArrayExpr extends Expr, @any_new_expr {
|
||||
* For example, for `new int` the result is `int`.
|
||||
* For `new int[5]` the result is `int[5]`.
|
||||
*/
|
||||
Type getAllocatedType() { none() } // overridden in subclasses
|
||||
abstract Type getAllocatedType();
|
||||
|
||||
/**
|
||||
* Gets the pointer `p` if this expression is of the form `new(p) T...`.
|
||||
@@ -1145,40 +1146,6 @@ class BlockExpr extends Literal {
|
||||
Function getFunction() { code_block(underlyingElement(this), unresolveElement(result)) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `throw` expression.
|
||||
* ```
|
||||
* throw Exc(2);
|
||||
* ```
|
||||
*/
|
||||
class ThrowExpr extends Expr, @throw_expr {
|
||||
/**
|
||||
* Gets the expression that will be thrown, if any. There is no result if
|
||||
* `this` is a `ReThrowExpr`.
|
||||
*/
|
||||
Expr getExpr() { result = this.getChild(0) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ThrowExpr" }
|
||||
|
||||
override string toString() { result = "throw ..." }
|
||||
|
||||
override int getPrecedence() { result = 1 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `throw` expression with no argument (which causes the current exception to be re-thrown).
|
||||
* ```
|
||||
* throw;
|
||||
* ```
|
||||
*/
|
||||
class ReThrowExpr extends ThrowExpr {
|
||||
ReThrowExpr() { this.getType() instanceof VoidType }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ReThrowExpr" }
|
||||
|
||||
override string toString() { result = "re-throw exception " }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++11 `noexcept` expression, returning `true` if its subexpression is guaranteed
|
||||
* not to `throw` exceptions. For example:
|
||||
|
||||
@@ -47,17 +47,7 @@ class LabelLiteral extends Literal {
|
||||
}
|
||||
|
||||
/** A character literal or a string literal. */
|
||||
class TextLiteral extends Literal {
|
||||
TextLiteral() {
|
||||
// String Literal
|
||||
// Note that `AggregateLiteral`s can also have an array type, but they derive from
|
||||
// @aggregateliteral rather than @literal.
|
||||
this.getType() instanceof ArrayType
|
||||
or
|
||||
// Char literal
|
||||
this.getValueText().regexpMatch("(?s)\\s*L?'.*")
|
||||
}
|
||||
|
||||
abstract class TextLiteral extends Literal {
|
||||
/** Gets a hex escape sequence that appears in the character or string literal (see [lex.ccon] in the C++ Standard). */
|
||||
string getAHexEscapeSequence(int occurrence, int offset) {
|
||||
result = getValueText().regexpFind("(?<!\\\\)\\\\x[0-9a-fA-F]+", occurrence, offset)
|
||||
|
||||
@@ -39,7 +39,7 @@ class BinaryLogicalOperation extends BinaryOperation, @bin_log_op_expr {
|
||||
* is true, `x` and `y` must also be true, so `impliesValue(x, true, true)` and
|
||||
* `impliesValue(y, true, true)` hold.
|
||||
*/
|
||||
predicate impliesValue(Expr part, boolean partIsTrue, boolean wholeIsTrue) { none() } // overridden in subclasses
|
||||
abstract predicate impliesValue(Expr part, boolean partIsTrue, boolean wholeIsTrue);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -59,7 +59,7 @@ class Namespace extends @namespace {
|
||||
}
|
||||
}
|
||||
|
||||
class Declaration extends @declaration {
|
||||
abstract class Declaration extends @declaration {
|
||||
string toString() { result = "QualifiedName Declaration" }
|
||||
|
||||
/** Gets the name of this declaration. */
|
||||
|
||||
@@ -83,7 +83,7 @@ private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
|
||||
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
commonTaintStep(n1, n2)
|
||||
instructionTaintStep(n1.asInstruction(), n2.asInstruction())
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
@@ -101,7 +101,7 @@ private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
commonTaintStep(n1, n2)
|
||||
instructionTaintStep(n1.asInstruction(), n2.asInstruction())
|
||||
or
|
||||
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
|
||||
or
|
||||
@@ -125,7 +125,7 @@ private class FromGlobalVarTaintTrackingCfg extends DataFlow2::Configuration {
|
||||
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
commonTaintStep(n1, n2)
|
||||
instructionTaintStep(n1.asInstruction(), n2.asInstruction())
|
||||
or
|
||||
// Additional step for flow out of variables. There is no flow _into_
|
||||
// variables in this configuration, so this step only serves to take flow
|
||||
@@ -215,62 +215,19 @@ private predicate nodeIsBarrierIn(DataFlow::Node node) {
|
||||
}
|
||||
|
||||
cached
|
||||
private predicate commonTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
|
||||
instructionToInstructionTaintStep(fromNode.asInstruction(), toNode.asInstruction())
|
||||
or
|
||||
operandToInstructionTaintStep(fromNode.asOperand(), toNode.asInstruction())
|
||||
or
|
||||
operandToOperandTaintStep(fromNode.asOperand(), toNode.asOperand())
|
||||
}
|
||||
|
||||
private predicate operandToOperandTaintStep(Operand fromOperand, Operand toOperand) {
|
||||
exists(ReadSideEffectInstruction readInstr |
|
||||
fromOperand = readInstr.getArgumentOperand() and
|
||||
toOperand = readInstr.getSideEffectOperand()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate operandToInstructionTaintStep(Operand fromOperand, Instruction toInstr) {
|
||||
private predicate instructionTaintStep(Instruction i1, Instruction i2) {
|
||||
// Expressions computed from tainted data are also tainted
|
||||
exists(CallInstruction call, int argIndex | call = toInstr |
|
||||
exists(CallInstruction call, int argIndex | call = i2 |
|
||||
isPureFunction(call.getStaticCallTarget().getName()) and
|
||||
fromOperand = getACallArgumentOrIndirection(call, argIndex) and
|
||||
forall(Operand argOperand | argOperand = call.getAnArgumentOperand() |
|
||||
argOperand = getACallArgumentOrIndirection(call, argIndex) or
|
||||
predictableInstruction(argOperand.getAnyDef())
|
||||
i1 = getACallArgumentOrIndirection(call, argIndex) and
|
||||
forall(Instruction arg | arg = call.getAnArgument() |
|
||||
arg = getACallArgumentOrIndirection(call, argIndex) or predictableInstruction(arg)
|
||||
) and
|
||||
// flow through `strlen` tends to cause dubious results, if the length is
|
||||
// bounded.
|
||||
not call.getStaticCallTarget().getName() = "strlen"
|
||||
)
|
||||
or
|
||||
// Flow from argument to return value
|
||||
toInstr =
|
||||
any(CallInstruction call |
|
||||
exists(int indexIn |
|
||||
modelTaintToReturnValue(call.getStaticCallTarget(), indexIn) and
|
||||
fromOperand = getACallArgumentOrIndirection(call, indexIn) and
|
||||
not predictableOnlyFlow(call.getStaticCallTarget().getName())
|
||||
)
|
||||
)
|
||||
or
|
||||
// Flow from input argument to output argument
|
||||
// TODO: This won't work in practice as long as all aliased memory is tracked
|
||||
// together in a single virtual variable.
|
||||
// TODO: Will this work on the test for `TaintedPath.ql`, where the output arg
|
||||
// is a pointer addition expression?
|
||||
toInstr =
|
||||
any(WriteSideEffectInstruction outInstr |
|
||||
exists(CallInstruction call, int indexIn, int indexOut |
|
||||
modelTaintToParameter(call.getStaticCallTarget(), indexIn, indexOut) and
|
||||
fromOperand = getACallArgumentOrIndirection(call, indexIn) and
|
||||
outInstr.getIndex() = indexOut and
|
||||
outInstr.getPrimaryInstruction() = call
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate instructionToInstructionTaintStep(Instruction i1, Instruction i2) {
|
||||
// Flow through pointer dereference
|
||||
i2.(LoadInstruction).getSourceAddress() = i1
|
||||
or
|
||||
@@ -334,6 +291,29 @@ private predicate instructionToInstructionTaintStep(Instruction i1, Instruction
|
||||
read.getAnOperand().(SideEffectOperand).getAnyDef() = i1 and
|
||||
read.getArgumentDef() = i2
|
||||
)
|
||||
or
|
||||
// Flow from argument to return value
|
||||
i2 =
|
||||
any(CallInstruction call |
|
||||
exists(int indexIn |
|
||||
modelTaintToReturnValue(call.getStaticCallTarget(), indexIn) and
|
||||
i1 = getACallArgumentOrIndirection(call, indexIn) and
|
||||
not predictableOnlyFlow(call.getStaticCallTarget().getName())
|
||||
)
|
||||
)
|
||||
or
|
||||
// Flow from input argument to output argument
|
||||
// TODO: Will this work on the test for `TaintedPath.ql`, where the output arg
|
||||
// is a pointer addition expression?
|
||||
i2 =
|
||||
any(WriteSideEffectInstruction outNode |
|
||||
exists(CallInstruction call, int indexIn, int indexOut |
|
||||
modelTaintToParameter(call.getStaticCallTarget(), indexIn, indexOut) and
|
||||
i1 = getACallArgumentOrIndirection(call, indexIn) and
|
||||
outNode.getIndex() = indexOut and
|
||||
outNode.getPrimaryInstruction() = call
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -349,25 +329,15 @@ private InitializeParameterInstruction getInitializeParameter(IRFunction f, Para
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the side effect instruction corresponding to the specified function output,
|
||||
* if one exists.
|
||||
*/
|
||||
private int getWriteSideEffectIndex(FunctionOutput output) {
|
||||
output.isParameterDeref(result)
|
||||
or
|
||||
output.isQualifierObject() and result = -1
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an operand that goes into argument `argumentIndex` of `call`. This
|
||||
* Get an instruction that goes into argument `argumentIndex` of `call`. This
|
||||
* can be either directly or through one pointer indirection.
|
||||
*/
|
||||
private Operand getACallArgumentOrIndirection(CallInstruction call, int argumentIndex) {
|
||||
result = call.getPositionalArgumentOperand(argumentIndex)
|
||||
private Instruction getACallArgumentOrIndirection(CallInstruction call, int argumentIndex) {
|
||||
result = call.getPositionalArgument(argumentIndex)
|
||||
or
|
||||
exists(ReadSideEffectInstruction readSE |
|
||||
// TODO: why are read side effect operands imprecise?
|
||||
result = readSE.getSideEffectOperand() and
|
||||
result = readSE.getSideEffectOperand().getAnyDef() and
|
||||
readSE.getPrimaryInstruction() = call and
|
||||
readSE.getIndex() = argumentIndex
|
||||
)
|
||||
@@ -381,7 +351,7 @@ private predicate modelTaintToParameter(Function f, int parameterIn, int paramet
|
||||
f.(TaintFunction).hasTaintFlow(modelIn, modelOut)
|
||||
) and
|
||||
(modelIn.isParameter(parameterIn) or modelIn.isParameterDeref(parameterIn)) and
|
||||
parameterOut = getWriteSideEffectIndex(modelOut)
|
||||
modelOut.isParameterDeref(parameterOut)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -570,7 +540,7 @@ module TaintedWithPath {
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
commonTaintStep(n1, n2)
|
||||
instructionTaintStep(n1.asInstruction(), n2.asInstruction())
|
||||
or
|
||||
exists(TaintTrackingConfiguration cfg | cfg.taintThroughGlobals() |
|
||||
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
|
||||
|
||||
@@ -112,8 +112,8 @@ abstract class Configuration extends string {
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
* measured in approximate number of interprocedural steps.
|
||||
* Gets the exploration limit for `hasPartialFlow` measured in approximate
|
||||
* number of interprocedural steps.
|
||||
*/
|
||||
int explorationLimit() { none() }
|
||||
|
||||
@@ -123,7 +123,7 @@ abstract class Configuration extends string {
|
||||
* is restricted to be less than or equal to `explorationLimit()`. This
|
||||
* predicate completely disregards sink definitions.
|
||||
*
|
||||
* This predicate is intended for data-flow exploration and debugging and may
|
||||
* This predicate is intended for dataflow exploration and debugging and may
|
||||
* perform poorly if the number of sources is too big and/or the exploration
|
||||
* limit is set too high without using barriers.
|
||||
*
|
||||
@@ -136,29 +136,6 @@ abstract class Configuration extends string {
|
||||
partialFlow(source, node, this) and
|
||||
dist = node.getSourceDistance()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a partial data flow path from `node` to `sink`. The
|
||||
* approximate distance between `node` and the closest sink is `dist` and
|
||||
* is restricted to be less than or equal to `explorationLimit()`. This
|
||||
* predicate completely disregards source definitions.
|
||||
*
|
||||
* This predicate is intended for data-flow exploration and debugging and may
|
||||
* perform poorly if the number of sinks is too big and/or the exploration
|
||||
* limit is set too high without using barriers.
|
||||
*
|
||||
* This predicate is disabled (has no results) by default. Override
|
||||
* `explorationLimit()` with a suitable number to enable this predicate.
|
||||
*
|
||||
* To use this in a `path-problem` query, import the module `PartialPathGraph`.
|
||||
*
|
||||
* Note that reverse flow has slightly lower precision than the corresponding
|
||||
* forward flow, as reverse flow disregards type pruning among other features.
|
||||
*/
|
||||
final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) {
|
||||
revPartialFlow(node, sink, this) and
|
||||
dist = node.getSinkDistance()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2966,26 +2943,12 @@ private module FlowExploration {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
|
||||
exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
|
||||
or
|
||||
exists(DataFlowCallable mid |
|
||||
interestingCallableSink(mid, config) and callableStep(c, mid, config)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TCallableExt =
|
||||
TCallable(DataFlowCallable c, Configuration config) {
|
||||
interestingCallableSrc(c, config) or
|
||||
interestingCallableSink(c, config)
|
||||
} or
|
||||
TCallableSrc() or
|
||||
TCallableSink()
|
||||
TCallable(DataFlowCallable c, Configuration config) { interestingCallableSrc(c, config) } or
|
||||
TCallableSrc()
|
||||
|
||||
private predicate callableExtSrc(TCallableSrc src) { any() }
|
||||
|
||||
private predicate callableExtSink(TCallableSink sink) { any() }
|
||||
|
||||
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
|
||||
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
|
||||
callableStep(c1, c2, config) and
|
||||
@@ -2998,32 +2961,15 @@ private module FlowExploration {
|
||||
config.isSource(n) and
|
||||
ce2 = TCallable(n.getEnclosingCallable(), config)
|
||||
)
|
||||
or
|
||||
exists(Node n, Configuration config |
|
||||
ce2 = TCallableSink() and
|
||||
config.isSink(n) and
|
||||
ce1 = TCallable(n.getEnclosingCallable(), config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) {
|
||||
callableExtStepFwd(ce2, ce1)
|
||||
}
|
||||
|
||||
private int distSrcExt(TCallableExt c) =
|
||||
shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result)
|
||||
|
||||
private int distSinkExt(TCallableExt c) =
|
||||
shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result)
|
||||
|
||||
private int distSrc(DataFlowCallable c, Configuration config) {
|
||||
result = distSrcExt(TCallable(c, config)) - 1
|
||||
}
|
||||
|
||||
private int distSink(DataFlowCallable c, Configuration config) {
|
||||
result = distSinkExt(TCallable(c, config)) - 1
|
||||
}
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
|
||||
@@ -3051,12 +2997,18 @@ private module FlowExploration {
|
||||
or
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
}
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = TFrontNil(t))
|
||||
}
|
||||
}
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
@@ -3067,39 +3019,9 @@ private module FlowExploration {
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TRevPartialAccessPath =
|
||||
TRevPartialNil() or
|
||||
TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s, but only the first
|
||||
* element of the list and its length are tracked.
|
||||
*/
|
||||
private class RevPartialAccessPath extends TRevPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TRevPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TRevPartialNil() and result = 0
|
||||
or
|
||||
this = TRevPartialCons(_, result)
|
||||
}
|
||||
}
|
||||
|
||||
private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil {
|
||||
override string toString() { result = "" }
|
||||
}
|
||||
|
||||
private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons {
|
||||
override string toString() {
|
||||
exists(Content c, int len | this = TRevPartialCons(c, len) |
|
||||
if len = 1
|
||||
then result = "[" + c.toString() + "]"
|
||||
else result = "[" + c.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
override AccessPathFront getFront() {
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3111,16 +3033,8 @@ private module FlowExploration {
|
||||
TSummaryCtx2None() or
|
||||
TSummaryCtx2Some(PartialAccessPath ap)
|
||||
|
||||
private newtype TRevSummaryCtx1 =
|
||||
TRevSummaryCtx1None() or
|
||||
TRevSummaryCtx1Some(ReturnPosition pos)
|
||||
|
||||
private newtype TRevSummaryCtx2 =
|
||||
TRevSummaryCtx2None() or
|
||||
TRevSummaryCtx2Some(RevPartialAccessPath ap)
|
||||
|
||||
private newtype TPartialPathNode =
|
||||
TPartialPathNodeFwd(
|
||||
TPartialPathNodeMk(
|
||||
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
@@ -3134,23 +3048,6 @@ private module FlowExploration {
|
||||
or
|
||||
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
|
||||
distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
} or
|
||||
TPartialPathNodeRev(
|
||||
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
config.isSink(node) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = TRevPartialNil() and
|
||||
not fullBarrier(node, config) and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -3158,7 +3055,7 @@ private module FlowExploration {
|
||||
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
exists(PartialPathNode mid |
|
||||
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
if node instanceof CastingNode
|
||||
@@ -3210,32 +3107,15 @@ private module FlowExploration {
|
||||
result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the approximate distance to the nearest sink measured in number
|
||||
* of interprocedural steps.
|
||||
*/
|
||||
int getSinkDistance() {
|
||||
result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s |
|
||||
s = this.(PartialPathNodeFwd).getAp().toString() or
|
||||
s = this.(PartialPathNodeRev).getAp().toString()
|
||||
|
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
private string ppCtx() {
|
||||
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
|
||||
result = " <" + this.(PartialPathNodePriv).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
/** Holds if this is a source in a forward-flow path. */
|
||||
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
|
||||
|
||||
/** Holds if this is a sink in a reverse-flow path. */
|
||||
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3246,7 +3126,7 @@ private module FlowExploration {
|
||||
query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b }
|
||||
}
|
||||
|
||||
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
|
||||
private class PartialPathNodePriv extends PartialPathNode {
|
||||
Node node;
|
||||
CallContext cc;
|
||||
TSummaryCtx1 sc1;
|
||||
@@ -3254,7 +3134,7 @@ private module FlowExploration {
|
||||
PartialAccessPath ap;
|
||||
Configuration config;
|
||||
|
||||
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
|
||||
PartialPathNodePriv() { this = TPartialPathNodeMk(node, cc, sc1, sc2, ap, config) }
|
||||
|
||||
override Node getNode() { result = node }
|
||||
|
||||
@@ -3268,54 +3148,14 @@ private module FlowExploration {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PartialPathNodeFwd getASuccessor() {
|
||||
override PartialPathNodePriv getASuccessor() {
|
||||
partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
|
||||
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
|
||||
}
|
||||
|
||||
predicate isSource() {
|
||||
config.isSource(node) and
|
||||
cc instanceof CallContextAny and
|
||||
sc1 = TSummaryCtx1None() and
|
||||
sc2 = TSummaryCtx2None() and
|
||||
ap instanceof TPartialNil
|
||||
}
|
||||
}
|
||||
|
||||
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
|
||||
Node node;
|
||||
TRevSummaryCtx1 sc1;
|
||||
TRevSummaryCtx2 sc2;
|
||||
RevPartialAccessPath ap;
|
||||
Configuration config;
|
||||
|
||||
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
|
||||
|
||||
override Node getNode() { result = node }
|
||||
|
||||
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
|
||||
|
||||
TRevSummaryCtx2 getSummaryCtx2() { result = sc2 }
|
||||
|
||||
RevPartialAccessPath getAp() { result = ap }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PartialPathNodeRev getASuccessor() {
|
||||
revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
|
||||
this.getAp(), this.getConfiguration())
|
||||
}
|
||||
|
||||
predicate isSink() {
|
||||
config.isSink(node) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = TRevPartialNil()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
@@ -3381,7 +3221,8 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
@@ -3397,7 +3238,7 @@ private module FlowExploration {
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
@@ -3405,7 +3246,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node midNode |
|
||||
@@ -3419,7 +3260,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
|
||||
PartialPathNodePriv mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
@@ -3431,7 +3272,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathOutOfCallable1(
|
||||
PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
@@ -3445,7 +3286,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable(
|
||||
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt kind, DataFlowCall call |
|
||||
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
||||
@@ -3456,7 +3297,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathIntoArg(
|
||||
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||
PartialPathNodePriv mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ArgumentNode arg |
|
||||
@@ -3470,7 +3311,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathIntoCallable0(
|
||||
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||
PartialPathNodePriv mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||
DataFlowCall call, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
partialPathIntoArg(mid, i, outercc, call, ap, config) and
|
||||
@@ -3478,7 +3319,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathIntoCallable(
|
||||
PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
|
||||
PartialPathNodePriv mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
|
||||
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
@@ -3499,7 +3340,7 @@ private module FlowExploration {
|
||||
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
|
||||
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -3512,7 +3353,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
|
||||
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
|
||||
@@ -3522,164 +3363,13 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathThroughCallable(
|
||||
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate revPartialPathStep(
|
||||
PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
|
||||
RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(node, mid.getNode(), config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(node, mid.getNode(), config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
mid.getAp() instanceof RevPartialAccessPathNil and
|
||||
ap = TRevPartialNil() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
jumpStep(node, mid.getNode(), config) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalJumpStep(node, mid.getNode(), config) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
mid.getAp() instanceof RevPartialAccessPathNil and
|
||||
ap = TRevPartialNil() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
revPartialPathReadStep(mid, _, _, node, ap) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(RevPartialAccessPath ap0, Content c |
|
||||
revPartialPathStoreStep(mid, ap0, c, node, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsRev(ap, c, ap0, config)
|
||||
)
|
||||
or
|
||||
exists(ParameterNode p |
|
||||
mid.getNode() = p and
|
||||
viableParamArg(_, p, node) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
exists(ReturnPosition pos |
|
||||
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
|
||||
pos = getReturnPosition(node)
|
||||
)
|
||||
or
|
||||
revPartialPathThroughCallable(mid, node, ap, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate revPartialPathReadStep(
|
||||
PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
|
||||
) {
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
read(node, c, midNode) and
|
||||
ap2.getHead() = c and
|
||||
ap2.len() = unbindInt(ap1.len() + 1)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsRev(
|
||||
RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathReadStep(mid, ap1, c, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathStoreStep(
|
||||
PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
|
||||
) {
|
||||
exists(Node midNode, TypedContent tc |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
store(node, tc, midNode, _) and
|
||||
ap.getHead() = c and
|
||||
config = mid.getConfiguration() and
|
||||
tc.getContent() = c
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathIntoReturn(
|
||||
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
|
||||
DataFlowCall call, RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node out |
|
||||
mid.getNode() = out and
|
||||
viableReturnPosOut(call, pos, out) and
|
||||
sc1 = TRevSummaryCtx1Some(pos) and
|
||||
sc2 = TRevSummaryCtx2Some(ap) and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathFlowsThrough(
|
||||
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeRev mid, ParameterNode p |
|
||||
mid.getNode() = p and
|
||||
p.isParameterOf(_, pos) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
|
||||
revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and
|
||||
revPartialPathFlowsThrough(pos, sc1, sc2, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathThroughCallable(
|
||||
PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, int pos |
|
||||
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
|
||||
node.argumentOf(call, pos)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import FlowExploration
|
||||
@@ -3688,14 +3378,6 @@ private predicate partialFlow(
|
||||
PartialPathNode source, PartialPathNode node, Configuration configuration
|
||||
) {
|
||||
source.getConfiguration() = configuration and
|
||||
source.isFwdSource() and
|
||||
configuration.isSource(source.getNode()) and
|
||||
node = source.getASuccessor+()
|
||||
}
|
||||
|
||||
private predicate revPartialFlow(
|
||||
PartialPathNode node, PartialPathNode sink, Configuration configuration
|
||||
) {
|
||||
sink.getConfiguration() = configuration and
|
||||
sink.isRevSink() and
|
||||
node.getASuccessor+() = sink
|
||||
}
|
||||
|
||||
@@ -112,8 +112,8 @@ abstract class Configuration extends string {
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
* measured in approximate number of interprocedural steps.
|
||||
* Gets the exploration limit for `hasPartialFlow` measured in approximate
|
||||
* number of interprocedural steps.
|
||||
*/
|
||||
int explorationLimit() { none() }
|
||||
|
||||
@@ -123,7 +123,7 @@ abstract class Configuration extends string {
|
||||
* is restricted to be less than or equal to `explorationLimit()`. This
|
||||
* predicate completely disregards sink definitions.
|
||||
*
|
||||
* This predicate is intended for data-flow exploration and debugging and may
|
||||
* This predicate is intended for dataflow exploration and debugging and may
|
||||
* perform poorly if the number of sources is too big and/or the exploration
|
||||
* limit is set too high without using barriers.
|
||||
*
|
||||
@@ -136,29 +136,6 @@ abstract class Configuration extends string {
|
||||
partialFlow(source, node, this) and
|
||||
dist = node.getSourceDistance()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a partial data flow path from `node` to `sink`. The
|
||||
* approximate distance between `node` and the closest sink is `dist` and
|
||||
* is restricted to be less than or equal to `explorationLimit()`. This
|
||||
* predicate completely disregards source definitions.
|
||||
*
|
||||
* This predicate is intended for data-flow exploration and debugging and may
|
||||
* perform poorly if the number of sinks is too big and/or the exploration
|
||||
* limit is set too high without using barriers.
|
||||
*
|
||||
* This predicate is disabled (has no results) by default. Override
|
||||
* `explorationLimit()` with a suitable number to enable this predicate.
|
||||
*
|
||||
* To use this in a `path-problem` query, import the module `PartialPathGraph`.
|
||||
*
|
||||
* Note that reverse flow has slightly lower precision than the corresponding
|
||||
* forward flow, as reverse flow disregards type pruning among other features.
|
||||
*/
|
||||
final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) {
|
||||
revPartialFlow(node, sink, this) and
|
||||
dist = node.getSinkDistance()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2966,26 +2943,12 @@ private module FlowExploration {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
|
||||
exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
|
||||
or
|
||||
exists(DataFlowCallable mid |
|
||||
interestingCallableSink(mid, config) and callableStep(c, mid, config)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TCallableExt =
|
||||
TCallable(DataFlowCallable c, Configuration config) {
|
||||
interestingCallableSrc(c, config) or
|
||||
interestingCallableSink(c, config)
|
||||
} or
|
||||
TCallableSrc() or
|
||||
TCallableSink()
|
||||
TCallable(DataFlowCallable c, Configuration config) { interestingCallableSrc(c, config) } or
|
||||
TCallableSrc()
|
||||
|
||||
private predicate callableExtSrc(TCallableSrc src) { any() }
|
||||
|
||||
private predicate callableExtSink(TCallableSink sink) { any() }
|
||||
|
||||
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
|
||||
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
|
||||
callableStep(c1, c2, config) and
|
||||
@@ -2998,32 +2961,15 @@ private module FlowExploration {
|
||||
config.isSource(n) and
|
||||
ce2 = TCallable(n.getEnclosingCallable(), config)
|
||||
)
|
||||
or
|
||||
exists(Node n, Configuration config |
|
||||
ce2 = TCallableSink() and
|
||||
config.isSink(n) and
|
||||
ce1 = TCallable(n.getEnclosingCallable(), config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) {
|
||||
callableExtStepFwd(ce2, ce1)
|
||||
}
|
||||
|
||||
private int distSrcExt(TCallableExt c) =
|
||||
shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result)
|
||||
|
||||
private int distSinkExt(TCallableExt c) =
|
||||
shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result)
|
||||
|
||||
private int distSrc(DataFlowCallable c, Configuration config) {
|
||||
result = distSrcExt(TCallable(c, config)) - 1
|
||||
}
|
||||
|
||||
private int distSink(DataFlowCallable c, Configuration config) {
|
||||
result = distSinkExt(TCallable(c, config)) - 1
|
||||
}
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
|
||||
@@ -3051,12 +2997,18 @@ private module FlowExploration {
|
||||
or
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
}
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = TFrontNil(t))
|
||||
}
|
||||
}
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
@@ -3067,39 +3019,9 @@ private module FlowExploration {
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TRevPartialAccessPath =
|
||||
TRevPartialNil() or
|
||||
TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s, but only the first
|
||||
* element of the list and its length are tracked.
|
||||
*/
|
||||
private class RevPartialAccessPath extends TRevPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TRevPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TRevPartialNil() and result = 0
|
||||
or
|
||||
this = TRevPartialCons(_, result)
|
||||
}
|
||||
}
|
||||
|
||||
private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil {
|
||||
override string toString() { result = "" }
|
||||
}
|
||||
|
||||
private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons {
|
||||
override string toString() {
|
||||
exists(Content c, int len | this = TRevPartialCons(c, len) |
|
||||
if len = 1
|
||||
then result = "[" + c.toString() + "]"
|
||||
else result = "[" + c.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
override AccessPathFront getFront() {
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3111,16 +3033,8 @@ private module FlowExploration {
|
||||
TSummaryCtx2None() or
|
||||
TSummaryCtx2Some(PartialAccessPath ap)
|
||||
|
||||
private newtype TRevSummaryCtx1 =
|
||||
TRevSummaryCtx1None() or
|
||||
TRevSummaryCtx1Some(ReturnPosition pos)
|
||||
|
||||
private newtype TRevSummaryCtx2 =
|
||||
TRevSummaryCtx2None() or
|
||||
TRevSummaryCtx2Some(RevPartialAccessPath ap)
|
||||
|
||||
private newtype TPartialPathNode =
|
||||
TPartialPathNodeFwd(
|
||||
TPartialPathNodeMk(
|
||||
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
@@ -3134,23 +3048,6 @@ private module FlowExploration {
|
||||
or
|
||||
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
|
||||
distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
} or
|
||||
TPartialPathNodeRev(
|
||||
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
config.isSink(node) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = TRevPartialNil() and
|
||||
not fullBarrier(node, config) and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -3158,7 +3055,7 @@ private module FlowExploration {
|
||||
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
exists(PartialPathNode mid |
|
||||
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
if node instanceof CastingNode
|
||||
@@ -3210,32 +3107,15 @@ private module FlowExploration {
|
||||
result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the approximate distance to the nearest sink measured in number
|
||||
* of interprocedural steps.
|
||||
*/
|
||||
int getSinkDistance() {
|
||||
result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s |
|
||||
s = this.(PartialPathNodeFwd).getAp().toString() or
|
||||
s = this.(PartialPathNodeRev).getAp().toString()
|
||||
|
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
private string ppCtx() {
|
||||
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
|
||||
result = " <" + this.(PartialPathNodePriv).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
/** Holds if this is a source in a forward-flow path. */
|
||||
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
|
||||
|
||||
/** Holds if this is a sink in a reverse-flow path. */
|
||||
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3246,7 +3126,7 @@ private module FlowExploration {
|
||||
query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b }
|
||||
}
|
||||
|
||||
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
|
||||
private class PartialPathNodePriv extends PartialPathNode {
|
||||
Node node;
|
||||
CallContext cc;
|
||||
TSummaryCtx1 sc1;
|
||||
@@ -3254,7 +3134,7 @@ private module FlowExploration {
|
||||
PartialAccessPath ap;
|
||||
Configuration config;
|
||||
|
||||
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
|
||||
PartialPathNodePriv() { this = TPartialPathNodeMk(node, cc, sc1, sc2, ap, config) }
|
||||
|
||||
override Node getNode() { result = node }
|
||||
|
||||
@@ -3268,54 +3148,14 @@ private module FlowExploration {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PartialPathNodeFwd getASuccessor() {
|
||||
override PartialPathNodePriv getASuccessor() {
|
||||
partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
|
||||
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
|
||||
}
|
||||
|
||||
predicate isSource() {
|
||||
config.isSource(node) and
|
||||
cc instanceof CallContextAny and
|
||||
sc1 = TSummaryCtx1None() and
|
||||
sc2 = TSummaryCtx2None() and
|
||||
ap instanceof TPartialNil
|
||||
}
|
||||
}
|
||||
|
||||
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
|
||||
Node node;
|
||||
TRevSummaryCtx1 sc1;
|
||||
TRevSummaryCtx2 sc2;
|
||||
RevPartialAccessPath ap;
|
||||
Configuration config;
|
||||
|
||||
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
|
||||
|
||||
override Node getNode() { result = node }
|
||||
|
||||
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
|
||||
|
||||
TRevSummaryCtx2 getSummaryCtx2() { result = sc2 }
|
||||
|
||||
RevPartialAccessPath getAp() { result = ap }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PartialPathNodeRev getASuccessor() {
|
||||
revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
|
||||
this.getAp(), this.getConfiguration())
|
||||
}
|
||||
|
||||
predicate isSink() {
|
||||
config.isSink(node) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = TRevPartialNil()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
@@ -3381,7 +3221,8 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
@@ -3397,7 +3238,7 @@ private module FlowExploration {
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
@@ -3405,7 +3246,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node midNode |
|
||||
@@ -3419,7 +3260,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
|
||||
PartialPathNodePriv mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
@@ -3431,7 +3272,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathOutOfCallable1(
|
||||
PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
@@ -3445,7 +3286,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable(
|
||||
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt kind, DataFlowCall call |
|
||||
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
||||
@@ -3456,7 +3297,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathIntoArg(
|
||||
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||
PartialPathNodePriv mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ArgumentNode arg |
|
||||
@@ -3470,7 +3311,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathIntoCallable0(
|
||||
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||
PartialPathNodePriv mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||
DataFlowCall call, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
partialPathIntoArg(mid, i, outercc, call, ap, config) and
|
||||
@@ -3478,7 +3319,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathIntoCallable(
|
||||
PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
|
||||
PartialPathNodePriv mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
|
||||
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
@@ -3499,7 +3340,7 @@ private module FlowExploration {
|
||||
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
|
||||
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -3512,7 +3353,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
|
||||
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
|
||||
@@ -3522,164 +3363,13 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathThroughCallable(
|
||||
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate revPartialPathStep(
|
||||
PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
|
||||
RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(node, mid.getNode(), config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(node, mid.getNode(), config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
mid.getAp() instanceof RevPartialAccessPathNil and
|
||||
ap = TRevPartialNil() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
jumpStep(node, mid.getNode(), config) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalJumpStep(node, mid.getNode(), config) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
mid.getAp() instanceof RevPartialAccessPathNil and
|
||||
ap = TRevPartialNil() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
revPartialPathReadStep(mid, _, _, node, ap) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(RevPartialAccessPath ap0, Content c |
|
||||
revPartialPathStoreStep(mid, ap0, c, node, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsRev(ap, c, ap0, config)
|
||||
)
|
||||
or
|
||||
exists(ParameterNode p |
|
||||
mid.getNode() = p and
|
||||
viableParamArg(_, p, node) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
exists(ReturnPosition pos |
|
||||
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
|
||||
pos = getReturnPosition(node)
|
||||
)
|
||||
or
|
||||
revPartialPathThroughCallable(mid, node, ap, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate revPartialPathReadStep(
|
||||
PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
|
||||
) {
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
read(node, c, midNode) and
|
||||
ap2.getHead() = c and
|
||||
ap2.len() = unbindInt(ap1.len() + 1)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsRev(
|
||||
RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathReadStep(mid, ap1, c, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathStoreStep(
|
||||
PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
|
||||
) {
|
||||
exists(Node midNode, TypedContent tc |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
store(node, tc, midNode, _) and
|
||||
ap.getHead() = c and
|
||||
config = mid.getConfiguration() and
|
||||
tc.getContent() = c
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathIntoReturn(
|
||||
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
|
||||
DataFlowCall call, RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node out |
|
||||
mid.getNode() = out and
|
||||
viableReturnPosOut(call, pos, out) and
|
||||
sc1 = TRevSummaryCtx1Some(pos) and
|
||||
sc2 = TRevSummaryCtx2Some(ap) and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathFlowsThrough(
|
||||
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeRev mid, ParameterNode p |
|
||||
mid.getNode() = p and
|
||||
p.isParameterOf(_, pos) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
|
||||
revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and
|
||||
revPartialPathFlowsThrough(pos, sc1, sc2, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathThroughCallable(
|
||||
PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, int pos |
|
||||
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
|
||||
node.argumentOf(call, pos)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import FlowExploration
|
||||
@@ -3688,14 +3378,6 @@ private predicate partialFlow(
|
||||
PartialPathNode source, PartialPathNode node, Configuration configuration
|
||||
) {
|
||||
source.getConfiguration() = configuration and
|
||||
source.isFwdSource() and
|
||||
configuration.isSource(source.getNode()) and
|
||||
node = source.getASuccessor+()
|
||||
}
|
||||
|
||||
private predicate revPartialFlow(
|
||||
PartialPathNode node, PartialPathNode sink, Configuration configuration
|
||||
) {
|
||||
sink.getConfiguration() = configuration and
|
||||
sink.isRevSink() and
|
||||
node.getASuccessor+() = sink
|
||||
}
|
||||
|
||||
@@ -112,8 +112,8 @@ abstract class Configuration extends string {
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
* measured in approximate number of interprocedural steps.
|
||||
* Gets the exploration limit for `hasPartialFlow` measured in approximate
|
||||
* number of interprocedural steps.
|
||||
*/
|
||||
int explorationLimit() { none() }
|
||||
|
||||
@@ -123,7 +123,7 @@ abstract class Configuration extends string {
|
||||
* is restricted to be less than or equal to `explorationLimit()`. This
|
||||
* predicate completely disregards sink definitions.
|
||||
*
|
||||
* This predicate is intended for data-flow exploration and debugging and may
|
||||
* This predicate is intended for dataflow exploration and debugging and may
|
||||
* perform poorly if the number of sources is too big and/or the exploration
|
||||
* limit is set too high without using barriers.
|
||||
*
|
||||
@@ -136,29 +136,6 @@ abstract class Configuration extends string {
|
||||
partialFlow(source, node, this) and
|
||||
dist = node.getSourceDistance()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a partial data flow path from `node` to `sink`. The
|
||||
* approximate distance between `node` and the closest sink is `dist` and
|
||||
* is restricted to be less than or equal to `explorationLimit()`. This
|
||||
* predicate completely disregards source definitions.
|
||||
*
|
||||
* This predicate is intended for data-flow exploration and debugging and may
|
||||
* perform poorly if the number of sinks is too big and/or the exploration
|
||||
* limit is set too high without using barriers.
|
||||
*
|
||||
* This predicate is disabled (has no results) by default. Override
|
||||
* `explorationLimit()` with a suitable number to enable this predicate.
|
||||
*
|
||||
* To use this in a `path-problem` query, import the module `PartialPathGraph`.
|
||||
*
|
||||
* Note that reverse flow has slightly lower precision than the corresponding
|
||||
* forward flow, as reverse flow disregards type pruning among other features.
|
||||
*/
|
||||
final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) {
|
||||
revPartialFlow(node, sink, this) and
|
||||
dist = node.getSinkDistance()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2966,26 +2943,12 @@ private module FlowExploration {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
|
||||
exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
|
||||
or
|
||||
exists(DataFlowCallable mid |
|
||||
interestingCallableSink(mid, config) and callableStep(c, mid, config)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TCallableExt =
|
||||
TCallable(DataFlowCallable c, Configuration config) {
|
||||
interestingCallableSrc(c, config) or
|
||||
interestingCallableSink(c, config)
|
||||
} or
|
||||
TCallableSrc() or
|
||||
TCallableSink()
|
||||
TCallable(DataFlowCallable c, Configuration config) { interestingCallableSrc(c, config) } or
|
||||
TCallableSrc()
|
||||
|
||||
private predicate callableExtSrc(TCallableSrc src) { any() }
|
||||
|
||||
private predicate callableExtSink(TCallableSink sink) { any() }
|
||||
|
||||
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
|
||||
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
|
||||
callableStep(c1, c2, config) and
|
||||
@@ -2998,32 +2961,15 @@ private module FlowExploration {
|
||||
config.isSource(n) and
|
||||
ce2 = TCallable(n.getEnclosingCallable(), config)
|
||||
)
|
||||
or
|
||||
exists(Node n, Configuration config |
|
||||
ce2 = TCallableSink() and
|
||||
config.isSink(n) and
|
||||
ce1 = TCallable(n.getEnclosingCallable(), config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) {
|
||||
callableExtStepFwd(ce2, ce1)
|
||||
}
|
||||
|
||||
private int distSrcExt(TCallableExt c) =
|
||||
shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result)
|
||||
|
||||
private int distSinkExt(TCallableExt c) =
|
||||
shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result)
|
||||
|
||||
private int distSrc(DataFlowCallable c, Configuration config) {
|
||||
result = distSrcExt(TCallable(c, config)) - 1
|
||||
}
|
||||
|
||||
private int distSink(DataFlowCallable c, Configuration config) {
|
||||
result = distSinkExt(TCallable(c, config)) - 1
|
||||
}
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
|
||||
@@ -3051,12 +2997,18 @@ private module FlowExploration {
|
||||
or
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
}
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = TFrontNil(t))
|
||||
}
|
||||
}
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
@@ -3067,39 +3019,9 @@ private module FlowExploration {
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TRevPartialAccessPath =
|
||||
TRevPartialNil() or
|
||||
TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s, but only the first
|
||||
* element of the list and its length are tracked.
|
||||
*/
|
||||
private class RevPartialAccessPath extends TRevPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TRevPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TRevPartialNil() and result = 0
|
||||
or
|
||||
this = TRevPartialCons(_, result)
|
||||
}
|
||||
}
|
||||
|
||||
private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil {
|
||||
override string toString() { result = "" }
|
||||
}
|
||||
|
||||
private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons {
|
||||
override string toString() {
|
||||
exists(Content c, int len | this = TRevPartialCons(c, len) |
|
||||
if len = 1
|
||||
then result = "[" + c.toString() + "]"
|
||||
else result = "[" + c.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
override AccessPathFront getFront() {
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3111,16 +3033,8 @@ private module FlowExploration {
|
||||
TSummaryCtx2None() or
|
||||
TSummaryCtx2Some(PartialAccessPath ap)
|
||||
|
||||
private newtype TRevSummaryCtx1 =
|
||||
TRevSummaryCtx1None() or
|
||||
TRevSummaryCtx1Some(ReturnPosition pos)
|
||||
|
||||
private newtype TRevSummaryCtx2 =
|
||||
TRevSummaryCtx2None() or
|
||||
TRevSummaryCtx2Some(RevPartialAccessPath ap)
|
||||
|
||||
private newtype TPartialPathNode =
|
||||
TPartialPathNodeFwd(
|
||||
TPartialPathNodeMk(
|
||||
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
@@ -3134,23 +3048,6 @@ private module FlowExploration {
|
||||
or
|
||||
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
|
||||
distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
} or
|
||||
TPartialPathNodeRev(
|
||||
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
config.isSink(node) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = TRevPartialNil() and
|
||||
not fullBarrier(node, config) and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -3158,7 +3055,7 @@ private module FlowExploration {
|
||||
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
exists(PartialPathNode mid |
|
||||
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
if node instanceof CastingNode
|
||||
@@ -3210,32 +3107,15 @@ private module FlowExploration {
|
||||
result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the approximate distance to the nearest sink measured in number
|
||||
* of interprocedural steps.
|
||||
*/
|
||||
int getSinkDistance() {
|
||||
result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s |
|
||||
s = this.(PartialPathNodeFwd).getAp().toString() or
|
||||
s = this.(PartialPathNodeRev).getAp().toString()
|
||||
|
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
private string ppCtx() {
|
||||
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
|
||||
result = " <" + this.(PartialPathNodePriv).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
/** Holds if this is a source in a forward-flow path. */
|
||||
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
|
||||
|
||||
/** Holds if this is a sink in a reverse-flow path. */
|
||||
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3246,7 +3126,7 @@ private module FlowExploration {
|
||||
query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b }
|
||||
}
|
||||
|
||||
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
|
||||
private class PartialPathNodePriv extends PartialPathNode {
|
||||
Node node;
|
||||
CallContext cc;
|
||||
TSummaryCtx1 sc1;
|
||||
@@ -3254,7 +3134,7 @@ private module FlowExploration {
|
||||
PartialAccessPath ap;
|
||||
Configuration config;
|
||||
|
||||
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
|
||||
PartialPathNodePriv() { this = TPartialPathNodeMk(node, cc, sc1, sc2, ap, config) }
|
||||
|
||||
override Node getNode() { result = node }
|
||||
|
||||
@@ -3268,54 +3148,14 @@ private module FlowExploration {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PartialPathNodeFwd getASuccessor() {
|
||||
override PartialPathNodePriv getASuccessor() {
|
||||
partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
|
||||
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
|
||||
}
|
||||
|
||||
predicate isSource() {
|
||||
config.isSource(node) and
|
||||
cc instanceof CallContextAny and
|
||||
sc1 = TSummaryCtx1None() and
|
||||
sc2 = TSummaryCtx2None() and
|
||||
ap instanceof TPartialNil
|
||||
}
|
||||
}
|
||||
|
||||
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
|
||||
Node node;
|
||||
TRevSummaryCtx1 sc1;
|
||||
TRevSummaryCtx2 sc2;
|
||||
RevPartialAccessPath ap;
|
||||
Configuration config;
|
||||
|
||||
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
|
||||
|
||||
override Node getNode() { result = node }
|
||||
|
||||
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
|
||||
|
||||
TRevSummaryCtx2 getSummaryCtx2() { result = sc2 }
|
||||
|
||||
RevPartialAccessPath getAp() { result = ap }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PartialPathNodeRev getASuccessor() {
|
||||
revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
|
||||
this.getAp(), this.getConfiguration())
|
||||
}
|
||||
|
||||
predicate isSink() {
|
||||
config.isSink(node) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = TRevPartialNil()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
@@ -3381,7 +3221,8 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
@@ -3397,7 +3238,7 @@ private module FlowExploration {
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
@@ -3405,7 +3246,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node midNode |
|
||||
@@ -3419,7 +3260,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
|
||||
PartialPathNodePriv mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
@@ -3431,7 +3272,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathOutOfCallable1(
|
||||
PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
@@ -3445,7 +3286,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable(
|
||||
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt kind, DataFlowCall call |
|
||||
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
||||
@@ -3456,7 +3297,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathIntoArg(
|
||||
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||
PartialPathNodePriv mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ArgumentNode arg |
|
||||
@@ -3470,7 +3311,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathIntoCallable0(
|
||||
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||
PartialPathNodePriv mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||
DataFlowCall call, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
partialPathIntoArg(mid, i, outercc, call, ap, config) and
|
||||
@@ -3478,7 +3319,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathIntoCallable(
|
||||
PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
|
||||
PartialPathNodePriv mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
|
||||
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
@@ -3499,7 +3340,7 @@ private module FlowExploration {
|
||||
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
|
||||
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -3512,7 +3353,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
|
||||
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
|
||||
@@ -3522,164 +3363,13 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathThroughCallable(
|
||||
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate revPartialPathStep(
|
||||
PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
|
||||
RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(node, mid.getNode(), config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(node, mid.getNode(), config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
mid.getAp() instanceof RevPartialAccessPathNil and
|
||||
ap = TRevPartialNil() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
jumpStep(node, mid.getNode(), config) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalJumpStep(node, mid.getNode(), config) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
mid.getAp() instanceof RevPartialAccessPathNil and
|
||||
ap = TRevPartialNil() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
revPartialPathReadStep(mid, _, _, node, ap) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(RevPartialAccessPath ap0, Content c |
|
||||
revPartialPathStoreStep(mid, ap0, c, node, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsRev(ap, c, ap0, config)
|
||||
)
|
||||
or
|
||||
exists(ParameterNode p |
|
||||
mid.getNode() = p and
|
||||
viableParamArg(_, p, node) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
exists(ReturnPosition pos |
|
||||
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
|
||||
pos = getReturnPosition(node)
|
||||
)
|
||||
or
|
||||
revPartialPathThroughCallable(mid, node, ap, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate revPartialPathReadStep(
|
||||
PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
|
||||
) {
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
read(node, c, midNode) and
|
||||
ap2.getHead() = c and
|
||||
ap2.len() = unbindInt(ap1.len() + 1)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsRev(
|
||||
RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathReadStep(mid, ap1, c, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathStoreStep(
|
||||
PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
|
||||
) {
|
||||
exists(Node midNode, TypedContent tc |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
store(node, tc, midNode, _) and
|
||||
ap.getHead() = c and
|
||||
config = mid.getConfiguration() and
|
||||
tc.getContent() = c
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathIntoReturn(
|
||||
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
|
||||
DataFlowCall call, RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node out |
|
||||
mid.getNode() = out and
|
||||
viableReturnPosOut(call, pos, out) and
|
||||
sc1 = TRevSummaryCtx1Some(pos) and
|
||||
sc2 = TRevSummaryCtx2Some(ap) and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathFlowsThrough(
|
||||
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeRev mid, ParameterNode p |
|
||||
mid.getNode() = p and
|
||||
p.isParameterOf(_, pos) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
|
||||
revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and
|
||||
revPartialPathFlowsThrough(pos, sc1, sc2, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathThroughCallable(
|
||||
PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, int pos |
|
||||
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
|
||||
node.argumentOf(call, pos)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import FlowExploration
|
||||
@@ -3688,14 +3378,6 @@ private predicate partialFlow(
|
||||
PartialPathNode source, PartialPathNode node, Configuration configuration
|
||||
) {
|
||||
source.getConfiguration() = configuration and
|
||||
source.isFwdSource() and
|
||||
configuration.isSource(source.getNode()) and
|
||||
node = source.getASuccessor+()
|
||||
}
|
||||
|
||||
private predicate revPartialFlow(
|
||||
PartialPathNode node, PartialPathNode sink, Configuration configuration
|
||||
) {
|
||||
sink.getConfiguration() = configuration and
|
||||
sink.isRevSink() and
|
||||
node.getASuccessor+() = sink
|
||||
}
|
||||
|
||||
@@ -112,8 +112,8 @@ abstract class Configuration extends string {
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
* measured in approximate number of interprocedural steps.
|
||||
* Gets the exploration limit for `hasPartialFlow` measured in approximate
|
||||
* number of interprocedural steps.
|
||||
*/
|
||||
int explorationLimit() { none() }
|
||||
|
||||
@@ -123,7 +123,7 @@ abstract class Configuration extends string {
|
||||
* is restricted to be less than or equal to `explorationLimit()`. This
|
||||
* predicate completely disregards sink definitions.
|
||||
*
|
||||
* This predicate is intended for data-flow exploration and debugging and may
|
||||
* This predicate is intended for dataflow exploration and debugging and may
|
||||
* perform poorly if the number of sources is too big and/or the exploration
|
||||
* limit is set too high without using barriers.
|
||||
*
|
||||
@@ -136,29 +136,6 @@ abstract class Configuration extends string {
|
||||
partialFlow(source, node, this) and
|
||||
dist = node.getSourceDistance()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a partial data flow path from `node` to `sink`. The
|
||||
* approximate distance between `node` and the closest sink is `dist` and
|
||||
* is restricted to be less than or equal to `explorationLimit()`. This
|
||||
* predicate completely disregards source definitions.
|
||||
*
|
||||
* This predicate is intended for data-flow exploration and debugging and may
|
||||
* perform poorly if the number of sinks is too big and/or the exploration
|
||||
* limit is set too high without using barriers.
|
||||
*
|
||||
* This predicate is disabled (has no results) by default. Override
|
||||
* `explorationLimit()` with a suitable number to enable this predicate.
|
||||
*
|
||||
* To use this in a `path-problem` query, import the module `PartialPathGraph`.
|
||||
*
|
||||
* Note that reverse flow has slightly lower precision than the corresponding
|
||||
* forward flow, as reverse flow disregards type pruning among other features.
|
||||
*/
|
||||
final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) {
|
||||
revPartialFlow(node, sink, this) and
|
||||
dist = node.getSinkDistance()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2966,26 +2943,12 @@ private module FlowExploration {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
|
||||
exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
|
||||
or
|
||||
exists(DataFlowCallable mid |
|
||||
interestingCallableSink(mid, config) and callableStep(c, mid, config)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TCallableExt =
|
||||
TCallable(DataFlowCallable c, Configuration config) {
|
||||
interestingCallableSrc(c, config) or
|
||||
interestingCallableSink(c, config)
|
||||
} or
|
||||
TCallableSrc() or
|
||||
TCallableSink()
|
||||
TCallable(DataFlowCallable c, Configuration config) { interestingCallableSrc(c, config) } or
|
||||
TCallableSrc()
|
||||
|
||||
private predicate callableExtSrc(TCallableSrc src) { any() }
|
||||
|
||||
private predicate callableExtSink(TCallableSink sink) { any() }
|
||||
|
||||
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
|
||||
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
|
||||
callableStep(c1, c2, config) and
|
||||
@@ -2998,32 +2961,15 @@ private module FlowExploration {
|
||||
config.isSource(n) and
|
||||
ce2 = TCallable(n.getEnclosingCallable(), config)
|
||||
)
|
||||
or
|
||||
exists(Node n, Configuration config |
|
||||
ce2 = TCallableSink() and
|
||||
config.isSink(n) and
|
||||
ce1 = TCallable(n.getEnclosingCallable(), config)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) {
|
||||
callableExtStepFwd(ce2, ce1)
|
||||
}
|
||||
|
||||
private int distSrcExt(TCallableExt c) =
|
||||
shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result)
|
||||
|
||||
private int distSinkExt(TCallableExt c) =
|
||||
shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result)
|
||||
|
||||
private int distSrc(DataFlowCallable c, Configuration config) {
|
||||
result = distSrcExt(TCallable(c, config)) - 1
|
||||
}
|
||||
|
||||
private int distSink(DataFlowCallable c, Configuration config) {
|
||||
result = distSinkExt(TCallable(c, config)) - 1
|
||||
}
|
||||
|
||||
private newtype TPartialAccessPath =
|
||||
TPartialNil(DataFlowType t) or
|
||||
TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
|
||||
@@ -3051,12 +2997,18 @@ private module FlowExploration {
|
||||
or
|
||||
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
|
||||
}
|
||||
|
||||
abstract AccessPathFront getFront();
|
||||
}
|
||||
|
||||
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
|
||||
override string toString() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
|
||||
}
|
||||
|
||||
override AccessPathFront getFront() {
|
||||
exists(DataFlowType t | this = TPartialNil(t) | result = TFrontNil(t))
|
||||
}
|
||||
}
|
||||
|
||||
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
|
||||
@@ -3067,39 +3019,9 @@ private module FlowExploration {
|
||||
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TRevPartialAccessPath =
|
||||
TRevPartialNil() or
|
||||
TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] }
|
||||
|
||||
/**
|
||||
* Conceptually a list of `Content`s, but only the first
|
||||
* element of the list and its length are tracked.
|
||||
*/
|
||||
private class RevPartialAccessPath extends TRevPartialAccessPath {
|
||||
abstract string toString();
|
||||
|
||||
Content getHead() { this = TRevPartialCons(result, _) }
|
||||
|
||||
int len() {
|
||||
this = TRevPartialNil() and result = 0
|
||||
or
|
||||
this = TRevPartialCons(_, result)
|
||||
}
|
||||
}
|
||||
|
||||
private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil {
|
||||
override string toString() { result = "" }
|
||||
}
|
||||
|
||||
private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons {
|
||||
override string toString() {
|
||||
exists(Content c, int len | this = TRevPartialCons(c, len) |
|
||||
if len = 1
|
||||
then result = "[" + c.toString() + "]"
|
||||
else result = "[" + c.toString() + ", ... (" + len.toString() + ")]"
|
||||
)
|
||||
override AccessPathFront getFront() {
|
||||
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3111,16 +3033,8 @@ private module FlowExploration {
|
||||
TSummaryCtx2None() or
|
||||
TSummaryCtx2Some(PartialAccessPath ap)
|
||||
|
||||
private newtype TRevSummaryCtx1 =
|
||||
TRevSummaryCtx1None() or
|
||||
TRevSummaryCtx1Some(ReturnPosition pos)
|
||||
|
||||
private newtype TRevSummaryCtx2 =
|
||||
TRevSummaryCtx2None() or
|
||||
TRevSummaryCtx2Some(RevPartialAccessPath ap)
|
||||
|
||||
private newtype TPartialPathNode =
|
||||
TPartialPathNodeFwd(
|
||||
TPartialPathNodeMk(
|
||||
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
@@ -3134,23 +3048,6 @@ private module FlowExploration {
|
||||
or
|
||||
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
|
||||
distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
} or
|
||||
TPartialPathNodeRev(
|
||||
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
config.isSink(node) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = TRevPartialNil() and
|
||||
not fullBarrier(node, config) and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -3158,7 +3055,7 @@ private module FlowExploration {
|
||||
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
exists(PartialPathNode mid |
|
||||
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
if node instanceof CastingNode
|
||||
@@ -3210,32 +3107,15 @@ private module FlowExploration {
|
||||
result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the approximate distance to the nearest sink measured in number
|
||||
* of interprocedural steps.
|
||||
*/
|
||||
int getSinkDistance() {
|
||||
result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
exists(string s |
|
||||
s = this.(PartialPathNodeFwd).getAp().toString() or
|
||||
s = this.(PartialPathNodeRev).getAp().toString()
|
||||
|
|
||||
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
|
||||
if s = "" then result = "" else result = " " + s
|
||||
)
|
||||
}
|
||||
|
||||
private string ppCtx() {
|
||||
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
|
||||
result = " <" + this.(PartialPathNodePriv).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
/** Holds if this is a source in a forward-flow path. */
|
||||
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
|
||||
|
||||
/** Holds if this is a sink in a reverse-flow path. */
|
||||
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3246,7 +3126,7 @@ private module FlowExploration {
|
||||
query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b }
|
||||
}
|
||||
|
||||
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
|
||||
private class PartialPathNodePriv extends PartialPathNode {
|
||||
Node node;
|
||||
CallContext cc;
|
||||
TSummaryCtx1 sc1;
|
||||
@@ -3254,7 +3134,7 @@ private module FlowExploration {
|
||||
PartialAccessPath ap;
|
||||
Configuration config;
|
||||
|
||||
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
|
||||
PartialPathNodePriv() { this = TPartialPathNodeMk(node, cc, sc1, sc2, ap, config) }
|
||||
|
||||
override Node getNode() { result = node }
|
||||
|
||||
@@ -3268,54 +3148,14 @@ private module FlowExploration {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PartialPathNodeFwd getASuccessor() {
|
||||
override PartialPathNodePriv getASuccessor() {
|
||||
partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
|
||||
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
|
||||
}
|
||||
|
||||
predicate isSource() {
|
||||
config.isSource(node) and
|
||||
cc instanceof CallContextAny and
|
||||
sc1 = TSummaryCtx1None() and
|
||||
sc2 = TSummaryCtx2None() and
|
||||
ap instanceof TPartialNil
|
||||
}
|
||||
}
|
||||
|
||||
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
|
||||
Node node;
|
||||
TRevSummaryCtx1 sc1;
|
||||
TRevSummaryCtx2 sc2;
|
||||
RevPartialAccessPath ap;
|
||||
Configuration config;
|
||||
|
||||
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
|
||||
|
||||
override Node getNode() { result = node }
|
||||
|
||||
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
|
||||
|
||||
TRevSummaryCtx2 getSummaryCtx2() { result = sc2 }
|
||||
|
||||
RevPartialAccessPath getAp() { result = ap }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PartialPathNodeRev getASuccessor() {
|
||||
revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
|
||||
this.getAp(), this.getConfiguration())
|
||||
}
|
||||
|
||||
predicate isSink() {
|
||||
config.isSink(node) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = TRevPartialNil()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialPathNodePriv mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
|
||||
@@ -3381,7 +3221,8 @@ private module FlowExploration {
|
||||
|
||||
pragma[inline]
|
||||
private predicate partialPathStoreStep(
|
||||
PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
|
||||
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
|
||||
PartialAccessPath ap2
|
||||
) {
|
||||
exists(Node midNode, DataFlowType contentType |
|
||||
midNode = mid.getNode() and
|
||||
@@ -3397,7 +3238,7 @@ private module FlowExploration {
|
||||
private predicate apConsFwd(
|
||||
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
exists(PartialPathNodePriv mid |
|
||||
partialPathStoreStep(mid, ap1, tc, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
@@ -3405,7 +3246,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathReadStep(
|
||||
PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
|
||||
Configuration config
|
||||
) {
|
||||
exists(Node midNode |
|
||||
@@ -3419,7 +3260,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable0(
|
||||
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
|
||||
PartialPathNodePriv mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
pos = getReturnPosition(mid.getNode()) and
|
||||
@@ -3431,7 +3272,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathOutOfCallable1(
|
||||
PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
|
||||
@@ -3445,7 +3286,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathOutOfCallable(
|
||||
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ReturnKindExt kind, DataFlowCall call |
|
||||
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
||||
@@ -3456,7 +3297,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathIntoArg(
|
||||
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||
PartialPathNodePriv mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ArgumentNode arg |
|
||||
@@ -3470,7 +3311,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathIntoCallable0(
|
||||
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||
PartialPathNodePriv mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||
DataFlowCall call, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
partialPathIntoArg(mid, i, outercc, call, ap, config) and
|
||||
@@ -3478,7 +3319,7 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathIntoCallable(
|
||||
PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
|
||||
PartialPathNodePriv mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
|
||||
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
@@ -3499,7 +3340,7 @@ private module FlowExploration {
|
||||
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
|
||||
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
|
||||
mid.getNode() = ret and
|
||||
kind = ret.getKind() and
|
||||
cc = mid.getCallContext() and
|
||||
@@ -3512,7 +3353,7 @@ private module FlowExploration {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate partialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
|
||||
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
|
||||
PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
|
||||
@@ -3522,164 +3363,13 @@ private module FlowExploration {
|
||||
}
|
||||
|
||||
private predicate partialPathThroughCallable(
|
||||
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind |
|
||||
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
|
||||
out = kind.getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate revPartialPathStep(
|
||||
PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
|
||||
RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
localFlowStep(node, mid.getNode(), config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalLocalFlowStep(node, mid.getNode(), config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
mid.getAp() instanceof RevPartialAccessPathNil and
|
||||
ap = TRevPartialNil() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
jumpStep(node, mid.getNode(), config) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
additionalJumpStep(node, mid.getNode(), config) and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
mid.getAp() instanceof RevPartialAccessPathNil and
|
||||
ap = TRevPartialNil() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
revPartialPathReadStep(mid, _, _, node, ap) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
config = mid.getConfiguration()
|
||||
or
|
||||
exists(RevPartialAccessPath ap0, Content c |
|
||||
revPartialPathStoreStep(mid, ap0, c, node, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
apConsRev(ap, c, ap0, config)
|
||||
)
|
||||
or
|
||||
exists(ParameterNode p |
|
||||
mid.getNode() = p and
|
||||
viableParamArg(_, p, node) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
sc1 = TRevSummaryCtx1None() and
|
||||
sc2 = TRevSummaryCtx2None() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
or
|
||||
exists(ReturnPosition pos |
|
||||
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
|
||||
pos = getReturnPosition(node)
|
||||
)
|
||||
or
|
||||
revPartialPathThroughCallable(mid, node, ap, config) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2()
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate revPartialPathReadStep(
|
||||
PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
|
||||
) {
|
||||
exists(Node midNode |
|
||||
midNode = mid.getNode() and
|
||||
ap1 = mid.getAp() and
|
||||
read(node, c, midNode) and
|
||||
ap2.getHead() = c and
|
||||
ap2.len() = unbindInt(ap1.len() + 1)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate apConsRev(
|
||||
RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathReadStep(mid, ap1, c, _, ap2) and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathStoreStep(
|
||||
PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
|
||||
) {
|
||||
exists(Node midNode, TypedContent tc |
|
||||
midNode = mid.getNode() and
|
||||
ap = mid.getAp() and
|
||||
store(node, tc, midNode, _) and
|
||||
ap.getHead() = c and
|
||||
config = mid.getConfiguration() and
|
||||
tc.getContent() = c
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathIntoReturn(
|
||||
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
|
||||
DataFlowCall call, RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(Node out |
|
||||
mid.getNode() = out and
|
||||
viableReturnPosOut(call, pos, out) and
|
||||
sc1 = TRevSummaryCtx1Some(pos) and
|
||||
sc2 = TRevSummaryCtx2Some(ap) and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathFlowsThrough(
|
||||
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeRev mid, ParameterNode p |
|
||||
mid.getNode() = p and
|
||||
p.isParameterOf(_, pos) and
|
||||
sc1 = mid.getSummaryCtx1() and
|
||||
sc2 = mid.getSummaryCtx2() and
|
||||
ap = mid.getAp() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathThroughCallable0(
|
||||
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
|
||||
revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and
|
||||
revPartialPathFlowsThrough(pos, sc1, sc2, ap, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revPartialPathThroughCallable(
|
||||
PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall call, int pos |
|
||||
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
|
||||
node.argumentOf(call, pos)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import FlowExploration
|
||||
@@ -3688,14 +3378,6 @@ private predicate partialFlow(
|
||||
PartialPathNode source, PartialPathNode node, Configuration configuration
|
||||
) {
|
||||
source.getConfiguration() = configuration and
|
||||
source.isFwdSource() and
|
||||
configuration.isSource(source.getNode()) and
|
||||
node = source.getASuccessor+()
|
||||
}
|
||||
|
||||
private predicate revPartialFlow(
|
||||
PartialPathNode node, PartialPathNode sink, Configuration configuration
|
||||
) {
|
||||
sink.getConfiguration() = configuration and
|
||||
sink.isRevSink() and
|
||||
node.getASuccessor+() = sink
|
||||
}
|
||||
|
||||
@@ -28,7 +28,11 @@ private class PrimaryArgumentNode extends ArgumentNode {
|
||||
|
||||
PrimaryArgumentNode() { exists(CallInstruction call | op = call.getAnArgumentOperand()) }
|
||||
|
||||
override predicate argumentOf(DataFlowCall call, int pos) { op = call.getArgumentOperand(pos) }
|
||||
override predicate argumentOf(DataFlowCall call, int pos) {
|
||||
op = call.getPositionalArgumentOperand(pos)
|
||||
or
|
||||
op = call.getThisArgumentOperand() and pos = -1
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Argument " + op.(PositionalArgumentOperand).getIndex()
|
||||
@@ -106,10 +110,10 @@ class ReturnIndirectionNode extends ReturnNode {
|
||||
override ReturnIndirectionInstruction primary;
|
||||
|
||||
override ReturnKind getKind() {
|
||||
exists(int index |
|
||||
primary.hasIndex(index) and
|
||||
result = TIndirectReturnKind(index)
|
||||
)
|
||||
result = TIndirectReturnKind(-1) and
|
||||
primary.isThisIndirection()
|
||||
or
|
||||
result = TIndirectReturnKind(primary.getParameter().getIndex())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +228,7 @@ private class ArrayContent extends Content, TArrayContent {
|
||||
private predicate fieldStoreStepNoChi(Node node1, FieldContent f, PostUpdateNode node2) {
|
||||
exists(StoreInstruction store, Class c |
|
||||
store = node2.asInstruction() and
|
||||
store.getSourceValueOperand() = node1.asOperand() and
|
||||
store.getSourceValue() = node1.asInstruction() and
|
||||
getWrittenField(store, f.(FieldContent).getAField(), c) and
|
||||
f.hasOffset(c, _, _)
|
||||
)
|
||||
@@ -239,20 +243,18 @@ pragma[noinline]
|
||||
private predicate getWrittenField(Instruction instr, Field f, Class c) {
|
||||
exists(FieldAddressInstruction fa |
|
||||
fa =
|
||||
getFieldInstruction([
|
||||
instr.(StoreInstruction).getDestinationAddress(),
|
||||
instr.(WriteSideEffectInstruction).getDestinationAddress()
|
||||
]) and
|
||||
getFieldInstruction([instr.(StoreInstruction).getDestinationAddress(),
|
||||
instr.(WriteSideEffectInstruction).getDestinationAddress()]) and
|
||||
f = fa.getField() and
|
||||
c = f.getDeclaringType()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fieldStoreStepChi(Node node1, FieldContent f, PostUpdateNode node2) {
|
||||
exists(ChiPartialOperand operand, ChiInstruction chi |
|
||||
chi.getPartialOperand() = operand and
|
||||
node1.asOperand() = operand and
|
||||
exists(StoreInstruction store, ChiInstruction chi |
|
||||
node1.asInstruction() = store and
|
||||
node2.asInstruction() = chi and
|
||||
chi.getPartial() = store and
|
||||
exists(Class c |
|
||||
c = chi.getResultType() and
|
||||
exists(int startBit, int endBit |
|
||||
@@ -260,7 +262,7 @@ private predicate fieldStoreStepChi(Node node1, FieldContent f, PostUpdateNode n
|
||||
f.hasOffset(c, startBit, endBit)
|
||||
)
|
||||
or
|
||||
getWrittenField(operand.getDef(), f.getAField(), c) and
|
||||
getWrittenField(store, f.getAField(), c) and
|
||||
f.hasOffset(c, _, _)
|
||||
)
|
||||
)
|
||||
@@ -268,13 +270,8 @@ private predicate fieldStoreStepChi(Node node1, FieldContent f, PostUpdateNode n
|
||||
|
||||
private predicate arrayStoreStepChi(Node node1, ArrayContent a, PostUpdateNode node2) {
|
||||
a = TArrayContent() and
|
||||
exists(ChiPartialOperand operand, ChiInstruction chi, StoreInstruction store |
|
||||
chi.getPartialOperand() = operand and
|
||||
store = operand.getDef() and
|
||||
node1.asOperand() = operand and
|
||||
// This `ChiInstruction` will always have a non-conflated result because both `ArrayStoreNode`
|
||||
// and `PointerStoreNode` require it in their characteristic predicates.
|
||||
node2.asInstruction() = chi and
|
||||
exists(StoreInstruction store |
|
||||
node1.asInstruction() = store and
|
||||
(
|
||||
// `x[i] = taint()`
|
||||
// This matches the characteristic predicate in `ArrayStoreNode`.
|
||||
@@ -283,7 +280,10 @@ private predicate arrayStoreStepChi(Node node1, ArrayContent a, PostUpdateNode n
|
||||
// `*p = taint()`
|
||||
// This matches the characteristic predicate in `PointerStoreNode`.
|
||||
store.getDestinationAddress().(CopyValueInstruction).getUnary() instanceof LoadInstruction
|
||||
)
|
||||
) and
|
||||
// This `ChiInstruction` will always have a non-conflated result because both `ArrayStoreNode`
|
||||
// and `PointerStoreNode` require it in their characteristic predicates.
|
||||
node2.asInstruction().(ChiInstruction).getPartial() = store
|
||||
)
|
||||
}
|
||||
|
||||
@@ -304,7 +304,7 @@ predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
|
||||
private predicate fieldStoreStepAfterArraySuppression(
|
||||
Node node1, FieldContent f, PostUpdateNode node2
|
||||
) {
|
||||
exists(WriteSideEffectInstruction write, ChiInstruction chi, Class c |
|
||||
exists(BufferMayWriteSideEffectInstruction write, ChiInstruction chi, Class c |
|
||||
not chi.isResultConflated() and
|
||||
node1.asInstruction() = chi and
|
||||
node2.asInstruction() = chi and
|
||||
@@ -332,17 +332,17 @@ private predicate getLoadedField(LoadInstruction load, Field f, Class c) {
|
||||
* `node2`.
|
||||
*/
|
||||
private predicate fieldReadStep(Node node1, FieldContent f, Node node2) {
|
||||
exists(LoadOperand operand |
|
||||
node2.asOperand() = operand and
|
||||
node1.asInstruction() = operand.getAnyDef() and
|
||||
exists(LoadInstruction load |
|
||||
node2.asInstruction() = load and
|
||||
node1.asInstruction() = load.getSourceValueOperand().getAnyDef() and
|
||||
exists(Class c |
|
||||
c = operand.getAnyDef().getResultType() and
|
||||
c = load.getSourceValueOperand().getAnyDef().getResultType() and
|
||||
exists(int startBit, int endBit |
|
||||
operand.getUsedInterval(unbindInt(startBit), unbindInt(endBit)) and
|
||||
load.getSourceValueOperand().getUsedInterval(unbindInt(startBit), unbindInt(endBit)) and
|
||||
f.hasOffset(c, startBit, endBit)
|
||||
)
|
||||
or
|
||||
getLoadedField(operand.getUse(), f.getAField(), c) and
|
||||
getLoadedField(load, f.getAField(), c) and
|
||||
f.hasOffset(c, _, _)
|
||||
)
|
||||
)
|
||||
@@ -363,7 +363,7 @@ private predicate fieldReadStep(Node node1, FieldContent f, Node node2) {
|
||||
*/
|
||||
predicate suppressArrayRead(Node node1, ArrayContent a, Node node2) {
|
||||
a = TArrayContent() and
|
||||
exists(WriteSideEffectInstruction write, ChiInstruction chi |
|
||||
exists(BufferMayWriteSideEffectInstruction write, ChiInstruction chi |
|
||||
node1.asInstruction() = write and
|
||||
node2.asInstruction() = chi and
|
||||
chi.getPartial() = write and
|
||||
@@ -384,20 +384,20 @@ private Instruction skipOneCopyValueInstructionRec(CopyValueInstruction copy) {
|
||||
result = skipOneCopyValueInstructionRec(copy.getUnary())
|
||||
}
|
||||
|
||||
private Instruction skipCopyValueInstructions(Operand op) {
|
||||
not result instanceof CopyValueInstruction and result = op.getDef()
|
||||
private Instruction skipCopyValueInstructions(Instruction instr) {
|
||||
not result instanceof CopyValueInstruction and result = instr
|
||||
or
|
||||
result = skipOneCopyValueInstructionRec(op.getDef())
|
||||
result = skipOneCopyValueInstructionRec(instr)
|
||||
}
|
||||
|
||||
private predicate arrayReadStep(Node node1, ArrayContent a, Node node2) {
|
||||
a = TArrayContent() and
|
||||
// Explicit dereferences such as `*p` or `p[i]` where `p` is a pointer or array.
|
||||
exists(LoadOperand operand, Instruction address |
|
||||
operand.isDefinitionInexact() and
|
||||
node1.asInstruction() = operand.getAnyDef() and
|
||||
operand = node2.asOperand() and
|
||||
address = skipCopyValueInstructions(operand.getAddressOperand()) and
|
||||
exists(LoadInstruction load, Instruction address |
|
||||
load.getSourceValueOperand().isDefinitionInexact() and
|
||||
node1.asInstruction() = load.getSourceValueOperand().getAnyDef() and
|
||||
load = node2.asInstruction() and
|
||||
address = skipCopyValueInstructions(load.getSourceAddress()) and
|
||||
(
|
||||
address instanceof LoadInstruction or
|
||||
address instanceof ArrayToPointerConvertInstruction or
|
||||
@@ -418,18 +418,18 @@ private predicate arrayReadStep(Node node1, ArrayContent a, Node node2) {
|
||||
* use(x);
|
||||
* ```
|
||||
* the load on `x` in `use(x)` will exactly overlap with its definition (in this case the definition
|
||||
* is a `WriteSideEffect`). This predicate pops the `ArrayContent` (pushed by the store in `f`)
|
||||
* is a `BufferMayWriteSideEffect`). This predicate pops the `ArrayContent` (pushed by the store in `f`)
|
||||
* from the access path.
|
||||
*/
|
||||
private predicate exactReadStep(Node node1, ArrayContent a, Node node2) {
|
||||
a = TArrayContent() and
|
||||
exists(WriteSideEffectInstruction write, ChiInstruction chi |
|
||||
exists(BufferMayWriteSideEffectInstruction write, ChiInstruction chi |
|
||||
not chi.isResultConflated() and
|
||||
chi.getPartial() = write and
|
||||
node1.asInstruction() = write and
|
||||
node2.asInstruction() = chi and
|
||||
// To distinquish this case from the `arrayReadStep` case we require that the entire variable was
|
||||
// overwritten by the `WriteSideEffectInstruction` (i.e., there is a load that reads the
|
||||
// overwritten by the `BufferMayWriteSideEffectInstruction` (i.e., there is a load that reads the
|
||||
// entire variable).
|
||||
exists(LoadInstruction load | load.getSourceValue() = chi)
|
||||
)
|
||||
@@ -496,6 +496,13 @@ class DataFlowType = IRType;
|
||||
|
||||
/** A function call relevant for data flow. */
|
||||
class DataFlowCall extends CallInstruction {
|
||||
/**
|
||||
* Gets the nth argument for this call.
|
||||
*
|
||||
* The range of `n` is from `0` to `getNumberOfArguments() - 1`.
|
||||
*/
|
||||
Node getArgument(int n) { result.asInstruction() = this.getPositionalArgument(n) }
|
||||
|
||||
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
|
||||
}
|
||||
|
||||
|
||||
@@ -266,8 +266,10 @@ class ParameterIndirectionNode extends ParameterNode {
|
||||
|
||||
override predicate isParameterOf(Function f, int pos) {
|
||||
exists(int index |
|
||||
instr.getEnclosingFunction() = f and
|
||||
instr.hasIndex(index)
|
||||
f.getParameter(index) = instr.getParameter()
|
||||
or
|
||||
index = -1 and
|
||||
instr.getIRVariable().(IRThisVariable).getEnclosingFunction() = f
|
||||
|
|
||||
pos = getArgumentPosOfSideEffect(index)
|
||||
)
|
||||
@@ -394,16 +396,16 @@ private FieldAddressInstruction getFieldInstruction(Instruction instr) {
|
||||
|
||||
/**
|
||||
* The target of a `fieldStoreStepAfterArraySuppression` store step, which is used to convert
|
||||
* an `ArrayContent` to a `FieldContent` when the `WriteSideEffect` instruction stores
|
||||
* an `ArrayContent` to a `FieldContent` when the `BufferMayWriteSideEffect` instruction stores
|
||||
* into a field. See the QLDoc for `suppressArrayRead` for an example of where such a conversion
|
||||
* is inserted.
|
||||
*/
|
||||
private class WriteSideEffectFieldStoreQualifierNode extends PartialDefinitionNode {
|
||||
private class BufferMayWriteSideEffectFieldStoreQualifierNode extends PartialDefinitionNode {
|
||||
override ChiInstruction instr;
|
||||
WriteSideEffectInstruction write;
|
||||
BufferMayWriteSideEffectInstruction write;
|
||||
FieldAddressInstruction field;
|
||||
|
||||
WriteSideEffectFieldStoreQualifierNode() {
|
||||
BufferMayWriteSideEffectFieldStoreQualifierNode() {
|
||||
not instr.isResultConflated() and
|
||||
instr.getPartial() = write and
|
||||
field = getFieldInstruction(write.getDestinationAddress())
|
||||
@@ -474,8 +476,16 @@ class DefinitionByReferenceNode extends InstructionNode {
|
||||
instr
|
||||
.getPrimaryInstruction()
|
||||
.(CallInstruction)
|
||||
.getArgument(instr.getIndex())
|
||||
.getPositionalArgument(instr.getIndex())
|
||||
.getUnconvertedResultExpression()
|
||||
or
|
||||
result =
|
||||
instr
|
||||
.getPrimaryInstruction()
|
||||
.(CallInstruction)
|
||||
.getThisArgument()
|
||||
.getUnconvertedResultExpression() and
|
||||
instr.getIndex() = -1
|
||||
}
|
||||
|
||||
/** Gets the parameter through which this value is assigned. */
|
||||
|
||||
@@ -26,6 +26,15 @@ class IRBlockBase extends TIRBlock {
|
||||
/** Gets the source location of the first non-`Phi` instruction in this block. */
|
||||
final Language::Location getLocation() { result = getFirstInstruction().getLocation() }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets a string that uniquely identifies this block within its enclosing function.
|
||||
*
|
||||
* This predicate is used by debugging and printing code only.
|
||||
*/
|
||||
final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
@@ -38,15 +47,14 @@ class IRBlockBase extends TIRBlock {
|
||||
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
|
||||
) and
|
||||
this =
|
||||
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
|
||||
rank[result + 1](IRBlock funcBlock, int sortOverride |
|
||||
funcBlock.getEnclosingFunction() = getEnclosingFunction() and
|
||||
funcBlock.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
|
||||
// Ensure that the block containing `EnterFunction` always comes first.
|
||||
if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction
|
||||
then sortOverride = 0
|
||||
else sortOverride = 1
|
||||
|
|
||||
funcBlock order by sortOverride, sortKey1, sortKey2
|
||||
funcBlock order by sortOverride, funcBlock.getUniqueId()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -163,46 +171,6 @@ class IRBlock extends IRBlockBase {
|
||||
not strictlyDominates(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block immediately post-dominates `block`.
|
||||
*
|
||||
* Block `A` immediate post-dominates block `B` if block `A` strictly post-dominates block `B` and
|
||||
* block `B` is a direct successor of block `A`.
|
||||
*/
|
||||
final predicate immediatelyPostDominates(IRBlock block) {
|
||||
blockImmediatelyPostDominates(this, block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block strictly post-dominates `block`.
|
||||
*
|
||||
* Block `A` strictly post-dominates block `B` if block `A` post-dominates block `B` and blocks `A`
|
||||
* and `B` are not the same block.
|
||||
*/
|
||||
final predicate strictlyPostDominates(IRBlock block) {
|
||||
blockImmediatelyPostDominates+(this, block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block is a post-dominator of `block`.
|
||||
*
|
||||
* Block `A` post-dominates block `B` if any control flow path from `B` to the exit block of the
|
||||
* function must pass through block `A`. A block always post-dominates itself.
|
||||
*/
|
||||
final predicate postDominates(IRBlock block) { strictlyPostDominates(block) or this = block }
|
||||
|
||||
/**
|
||||
* Gets a block on the post-dominance frontier of this block.
|
||||
*
|
||||
* The post-dominance frontier of block `A` is the set of blocks `B` such that block `A` does not
|
||||
* post-dominate block `B`, but block `A` does post-dominate an immediate successor of block `B`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final IRBlock postPominanceFrontier() {
|
||||
postDominates(result.getASuccessor()) and
|
||||
not strictlyPostDominates(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block is reachable from the entry block of its function.
|
||||
*/
|
||||
@@ -320,12 +288,3 @@ private module Cached {
|
||||
}
|
||||
|
||||
private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) }
|
||||
|
||||
private predicate blockFunctionExit(IRBlock exit) {
|
||||
exit.getLastInstruction() instanceof ExitFunctionInstruction
|
||||
}
|
||||
|
||||
private predicate blockPredecessor(IRBlock src, IRBlock pred) { src.getAPredecessor() = pred }
|
||||
|
||||
private predicate blockImmediatelyPostDominates(IRBlock postDominator, IRBlock block) =
|
||||
idominance(blockFunctionExit/1, blockPredecessor/2)(_, postDominator, block)
|
||||
|
||||
@@ -494,34 +494,4 @@ module InstructionConsistency {
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the object address operand for the given `FieldAddress` instruction does not have an
|
||||
* address type.
|
||||
*/
|
||||
query predicate fieldAddressOnNonPointer(
|
||||
FieldAddressInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not instr.getObjectAddressOperand().getIRType() instanceof IRAddressType and
|
||||
message =
|
||||
"FieldAddress instruction '" + instr.toString() +
|
||||
"' has an object address operand that is not an address, in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `this` argument operand for the given `Call` instruction does not have an address
|
||||
* type.
|
||||
*/
|
||||
query predicate thisArgumentIsNonPointer(
|
||||
CallInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(ThisArgumentOperand thisOperand | thisOperand = instr.getThisArgumentOperand() |
|
||||
not thisOperand.getIRType() instanceof IRAddressType
|
||||
) and
|
||||
message =
|
||||
"Call instruction '" + instr.toString() +
|
||||
"' has a `this` argument operand that is not an address, in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,16 +171,6 @@ class Instruction extends Construction::TStageInstruction {
|
||||
*/
|
||||
final string getUniqueId() { result = Construction::getInstructionUniqueId(this) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets two sort keys for this instruction - used to order instructions for printing
|
||||
* in test outputs.
|
||||
*/
|
||||
final predicate hasSortKeys(int key1, int key2) {
|
||||
Construction::instructionHasSortKeys(this, key1, key2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the basic block that contains this instruction.
|
||||
*/
|
||||
@@ -582,17 +572,6 @@ class InitializeParameterInstruction extends VariableInstruction {
|
||||
* Gets the parameter initialized by this instruction.
|
||||
*/
|
||||
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
|
||||
|
||||
/**
|
||||
* Holds if this instruction initializes the parameter with index `index`, or
|
||||
* if `index` is `-1` and this instruction initializes `this`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final predicate hasIndex(int index) {
|
||||
index >= 0 and index = this.getParameter().getIndex()
|
||||
or
|
||||
index = -1 and this.getIRVariable() instanceof IRThisVariable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -616,18 +595,6 @@ class InitializeIndirectionInstruction extends VariableInstruction {
|
||||
* Gets the parameter initialized by this instruction.
|
||||
*/
|
||||
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
|
||||
|
||||
/**
|
||||
* Holds if this instruction initializes the memory pointed to by the parameter with
|
||||
* index `index`, or if `index` is `-1` and this instruction initializes the memory
|
||||
* pointed to by `this`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final predicate hasIndex(int index) {
|
||||
index >= 0 and index = this.getParameter().getIndex()
|
||||
or
|
||||
index = -1 and this.getIRVariable() instanceof IRThisVariable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -802,17 +769,6 @@ class ReturnIndirectionInstruction extends VariableInstruction {
|
||||
* Holds if this instruction is the return indirection for `this`.
|
||||
*/
|
||||
final predicate isThisIndirection() { var instanceof IRThisVariable }
|
||||
|
||||
/**
|
||||
* Holds if this instruction is the return indirection for the parameter with index `index`, or
|
||||
* if this instruction is the return indirection for `this` and `index` is `-1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final predicate hasIndex(int index) {
|
||||
index >= 0 and index = this.getParameter().getIndex()
|
||||
or
|
||||
index = -1 and this.isThisIndirection()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1611,7 +1567,6 @@ class CallInstruction extends Instruction {
|
||||
/**
|
||||
* Gets the argument operand at the specified index.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final PositionalArgumentOperand getPositionalArgumentOperand(int index) {
|
||||
result = getAnOperand() and
|
||||
result.getIndex() = index
|
||||
@@ -1620,27 +1575,10 @@ class CallInstruction extends Instruction {
|
||||
/**
|
||||
* Gets the argument at the specified index.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final Instruction getPositionalArgument(int index) {
|
||||
result = getPositionalArgumentOperand(index).getDef()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument operand at the specified index, or `this` if `index` is `-1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final ArgumentOperand getArgumentOperand(int index) {
|
||||
index >= 0 and result = getPositionalArgumentOperand(index)
|
||||
or
|
||||
index = -1 and result = getThisArgumentOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument at the specified index, or `this` if `index` is `-1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final Instruction getArgument(int index) { result = getArgumentOperand(index).getDef() }
|
||||
|
||||
/**
|
||||
* Gets the number of arguments of the call, including the `this` pointer, if any.
|
||||
*/
|
||||
|
||||
@@ -562,9 +562,6 @@ private Overlap getVariableMemoryLocationOverlap(
|
||||
use.getEndBitOffset())
|
||||
}
|
||||
|
||||
bindingset[result, b]
|
||||
private boolean unbindBool(boolean b) { result != b.booleanNot() }
|
||||
|
||||
MemoryLocation getResultMemoryLocation(Instruction instr) {
|
||||
exists(MemoryAccessKind kind, boolean isMayAccess |
|
||||
kind = instr.getResultMemoryAccess() and
|
||||
@@ -577,8 +574,7 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
|
||||
exists(Allocation var, IRType type, IntValue startBitOffset, IntValue endBitOffset |
|
||||
hasResultMemoryAccess(instr, var, type, _, startBitOffset, endBitOffset, isMayAccess) and
|
||||
result =
|
||||
TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset,
|
||||
unbindBool(isMayAccess))
|
||||
TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset, isMayAccess)
|
||||
)
|
||||
else result = TUnknownMemoryLocation(instr.getEnclosingIRFunction(), isMayAccess)
|
||||
)
|
||||
@@ -586,7 +582,7 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
|
||||
kind instanceof EntireAllocationMemoryAccess and
|
||||
result =
|
||||
TEntireAllocationMemoryLocation(getAddressOperandAllocation(instr.getResultAddressOperand()),
|
||||
unbindBool(isMayAccess))
|
||||
isMayAccess)
|
||||
or
|
||||
kind instanceof EscapedMemoryAccess and
|
||||
result = TAllAliasedMemory(instr.getEnclosingIRFunction(), isMayAccess)
|
||||
|
||||
@@ -403,27 +403,16 @@ private import PhiInsertion
|
||||
* instruction for the virtual variable as a whole.
|
||||
*/
|
||||
private module PhiInsertion {
|
||||
/**
|
||||
* Holds if `phiBlock` is a block in the dominance frontier of a block that has a definition of the
|
||||
* memory location `defLocation`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate dominanceFrontierOfDefinition(
|
||||
Alias::MemoryLocation defLocation, OldBlock phiBlock
|
||||
) {
|
||||
exists(OldBlock defBlock |
|
||||
phiBlock = Dominance::getDominanceFrontier(defBlock) and
|
||||
definitionHasDefinitionInBlock(defLocation, defBlock)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `Phi` instruction needs to be inserted for location `defLocation` at the beginning of block `phiBlock`.
|
||||
*/
|
||||
predicate definitionHasPhiNode(Alias::MemoryLocation defLocation, OldBlock phiBlock) {
|
||||
dominanceFrontierOfDefinition(defLocation, phiBlock) and
|
||||
/* We can also eliminate those nodes where the definition is not live on any incoming edge */
|
||||
definitionLiveOnEntryToBlock(defLocation, phiBlock)
|
||||
exists(OldBlock defBlock |
|
||||
phiBlock = Dominance::getDominanceFrontier(defBlock) and
|
||||
definitionHasDefinitionInBlock(defLocation, defBlock) and
|
||||
/* We can also eliminate those nodes where the definition is not live on any incoming edge */
|
||||
definitionLiveOnEntryToBlock(defLocation, phiBlock)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -867,8 +856,7 @@ private module CachedForDebugging {
|
||||
exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity |
|
||||
instr = getPhi(phiBlock, location) and
|
||||
result =
|
||||
"Phi Block(" + phiBlock.getFirstInstruction().getUniqueId() + ")[" + specificity + "]: " +
|
||||
location.getUniqueId() and
|
||||
"Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and
|
||||
if location instanceof Alias::VirtualVariable
|
||||
then
|
||||
// Sort Phi nodes for virtual variables before Phi nodes for member locations.
|
||||
@@ -885,24 +873,6 @@ private module CachedForDebugging {
|
||||
result.getAST() = var.getAST() and
|
||||
result.getTag() = var.getTag()
|
||||
}
|
||||
|
||||
cached
|
||||
predicate instructionHasSortKeys(Instruction instr, int key1, int key2) {
|
||||
exists(OldInstruction oldInstr |
|
||||
oldInstr = getOldInstruction(instr) and
|
||||
oldInstr.hasSortKeys(key1, key2)
|
||||
)
|
||||
or
|
||||
instr instanceof TUnreachedInstruction and
|
||||
key1 = maxValue() and
|
||||
key2 = maxValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the maximum representable integer.
|
||||
*/
|
||||
cached
|
||||
int maxValue() { result = 2147483647 }
|
||||
}
|
||||
|
||||
module SSAConsistency {
|
||||
|
||||
@@ -26,6 +26,15 @@ class IRBlockBase extends TIRBlock {
|
||||
/** Gets the source location of the first non-`Phi` instruction in this block. */
|
||||
final Language::Location getLocation() { result = getFirstInstruction().getLocation() }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets a string that uniquely identifies this block within its enclosing function.
|
||||
*
|
||||
* This predicate is used by debugging and printing code only.
|
||||
*/
|
||||
final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
@@ -38,15 +47,14 @@ class IRBlockBase extends TIRBlock {
|
||||
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
|
||||
) and
|
||||
this =
|
||||
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
|
||||
rank[result + 1](IRBlock funcBlock, int sortOverride |
|
||||
funcBlock.getEnclosingFunction() = getEnclosingFunction() and
|
||||
funcBlock.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
|
||||
// Ensure that the block containing `EnterFunction` always comes first.
|
||||
if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction
|
||||
then sortOverride = 0
|
||||
else sortOverride = 1
|
||||
|
|
||||
funcBlock order by sortOverride, sortKey1, sortKey2
|
||||
funcBlock order by sortOverride, funcBlock.getUniqueId()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -163,46 +171,6 @@ class IRBlock extends IRBlockBase {
|
||||
not strictlyDominates(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block immediately post-dominates `block`.
|
||||
*
|
||||
* Block `A` immediate post-dominates block `B` if block `A` strictly post-dominates block `B` and
|
||||
* block `B` is a direct successor of block `A`.
|
||||
*/
|
||||
final predicate immediatelyPostDominates(IRBlock block) {
|
||||
blockImmediatelyPostDominates(this, block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block strictly post-dominates `block`.
|
||||
*
|
||||
* Block `A` strictly post-dominates block `B` if block `A` post-dominates block `B` and blocks `A`
|
||||
* and `B` are not the same block.
|
||||
*/
|
||||
final predicate strictlyPostDominates(IRBlock block) {
|
||||
blockImmediatelyPostDominates+(this, block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block is a post-dominator of `block`.
|
||||
*
|
||||
* Block `A` post-dominates block `B` if any control flow path from `B` to the exit block of the
|
||||
* function must pass through block `A`. A block always post-dominates itself.
|
||||
*/
|
||||
final predicate postDominates(IRBlock block) { strictlyPostDominates(block) or this = block }
|
||||
|
||||
/**
|
||||
* Gets a block on the post-dominance frontier of this block.
|
||||
*
|
||||
* The post-dominance frontier of block `A` is the set of blocks `B` such that block `A` does not
|
||||
* post-dominate block `B`, but block `A` does post-dominate an immediate successor of block `B`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final IRBlock postPominanceFrontier() {
|
||||
postDominates(result.getASuccessor()) and
|
||||
not strictlyPostDominates(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block is reachable from the entry block of its function.
|
||||
*/
|
||||
@@ -320,12 +288,3 @@ private module Cached {
|
||||
}
|
||||
|
||||
private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) }
|
||||
|
||||
private predicate blockFunctionExit(IRBlock exit) {
|
||||
exit.getLastInstruction() instanceof ExitFunctionInstruction
|
||||
}
|
||||
|
||||
private predicate blockPredecessor(IRBlock src, IRBlock pred) { src.getAPredecessor() = pred }
|
||||
|
||||
private predicate blockImmediatelyPostDominates(IRBlock postDominator, IRBlock block) =
|
||||
idominance(blockFunctionExit/1, blockPredecessor/2)(_, postDominator, block)
|
||||
|
||||
@@ -494,34 +494,4 @@ module InstructionConsistency {
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the object address operand for the given `FieldAddress` instruction does not have an
|
||||
* address type.
|
||||
*/
|
||||
query predicate fieldAddressOnNonPointer(
|
||||
FieldAddressInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not instr.getObjectAddressOperand().getIRType() instanceof IRAddressType and
|
||||
message =
|
||||
"FieldAddress instruction '" + instr.toString() +
|
||||
"' has an object address operand that is not an address, in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `this` argument operand for the given `Call` instruction does not have an address
|
||||
* type.
|
||||
*/
|
||||
query predicate thisArgumentIsNonPointer(
|
||||
CallInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(ThisArgumentOperand thisOperand | thisOperand = instr.getThisArgumentOperand() |
|
||||
not thisOperand.getIRType() instanceof IRAddressType
|
||||
) and
|
||||
message =
|
||||
"Call instruction '" + instr.toString() +
|
||||
"' has a `this` argument operand that is not an address, in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,16 +171,6 @@ class Instruction extends Construction::TStageInstruction {
|
||||
*/
|
||||
final string getUniqueId() { result = Construction::getInstructionUniqueId(this) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets two sort keys for this instruction - used to order instructions for printing
|
||||
* in test outputs.
|
||||
*/
|
||||
final predicate hasSortKeys(int key1, int key2) {
|
||||
Construction::instructionHasSortKeys(this, key1, key2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the basic block that contains this instruction.
|
||||
*/
|
||||
@@ -582,17 +572,6 @@ class InitializeParameterInstruction extends VariableInstruction {
|
||||
* Gets the parameter initialized by this instruction.
|
||||
*/
|
||||
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
|
||||
|
||||
/**
|
||||
* Holds if this instruction initializes the parameter with index `index`, or
|
||||
* if `index` is `-1` and this instruction initializes `this`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final predicate hasIndex(int index) {
|
||||
index >= 0 and index = this.getParameter().getIndex()
|
||||
or
|
||||
index = -1 and this.getIRVariable() instanceof IRThisVariable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -616,18 +595,6 @@ class InitializeIndirectionInstruction extends VariableInstruction {
|
||||
* Gets the parameter initialized by this instruction.
|
||||
*/
|
||||
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
|
||||
|
||||
/**
|
||||
* Holds if this instruction initializes the memory pointed to by the parameter with
|
||||
* index `index`, or if `index` is `-1` and this instruction initializes the memory
|
||||
* pointed to by `this`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final predicate hasIndex(int index) {
|
||||
index >= 0 and index = this.getParameter().getIndex()
|
||||
or
|
||||
index = -1 and this.getIRVariable() instanceof IRThisVariable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -802,17 +769,6 @@ class ReturnIndirectionInstruction extends VariableInstruction {
|
||||
* Holds if this instruction is the return indirection for `this`.
|
||||
*/
|
||||
final predicate isThisIndirection() { var instanceof IRThisVariable }
|
||||
|
||||
/**
|
||||
* Holds if this instruction is the return indirection for the parameter with index `index`, or
|
||||
* if this instruction is the return indirection for `this` and `index` is `-1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final predicate hasIndex(int index) {
|
||||
index >= 0 and index = this.getParameter().getIndex()
|
||||
or
|
||||
index = -1 and this.isThisIndirection()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1611,7 +1567,6 @@ class CallInstruction extends Instruction {
|
||||
/**
|
||||
* Gets the argument operand at the specified index.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final PositionalArgumentOperand getPositionalArgumentOperand(int index) {
|
||||
result = getAnOperand() and
|
||||
result.getIndex() = index
|
||||
@@ -1620,27 +1575,10 @@ class CallInstruction extends Instruction {
|
||||
/**
|
||||
* Gets the argument at the specified index.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final Instruction getPositionalArgument(int index) {
|
||||
result = getPositionalArgumentOperand(index).getDef()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument operand at the specified index, or `this` if `index` is `-1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final ArgumentOperand getArgumentOperand(int index) {
|
||||
index >= 0 and result = getPositionalArgumentOperand(index)
|
||||
or
|
||||
index = -1 and result = getThisArgumentOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument at the specified index, or `this` if `index` is `-1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final Instruction getArgument(int index) { result = getArgumentOperand(index).getDef() }
|
||||
|
||||
/**
|
||||
* Gets the number of arguments of the call, including the `this` pointer, if any.
|
||||
*/
|
||||
|
||||
@@ -380,21 +380,10 @@ private module CachedForDebugging {
|
||||
string getTempVariableUniqueId(IRTempVariable var) {
|
||||
exists(TranslatedElement element |
|
||||
var = element.getTempVariable(_) and
|
||||
result = element.getId().toString() + ":" + getTempVariableTagId(var.getTag())
|
||||
result = element.getId() + ":" + getTempVariableTagId(var.getTag())
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate instructionHasSortKeys(Instruction instruction, int key1, int key2) {
|
||||
key1 = getInstructionTranslatedElement(instruction).getId() and
|
||||
getInstructionTag(instruction) =
|
||||
rank[key2](InstructionTag tag, string tagId |
|
||||
tagId = getInstructionTagId(tag)
|
||||
|
|
||||
tag order by tagId
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
string getInstructionUniqueId(Instruction instruction) {
|
||||
result =
|
||||
|
||||
@@ -8,16 +8,6 @@ private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
private import TranslatedFunction
|
||||
|
||||
/**
|
||||
* Gets the `CallInstruction` from the `TranslatedCallExpr` for the specified expression.
|
||||
*/
|
||||
private CallInstruction getTranslatedCallInstruction(Call call) {
|
||||
exists(TranslatedCallExpr translatedCall |
|
||||
translatedCall.getExpr() = call and
|
||||
result = translatedCall.getInstruction(CallTag())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a call to a function. The call may be from an actual
|
||||
* call in the source code, or could be a call that is part of the translation
|
||||
@@ -398,7 +388,7 @@ class TranslatedAllocationSideEffects extends TranslatedSideEffects,
|
||||
tag = OnlyInstructionTag() and
|
||||
if expr instanceof NewOrNewArrayExpr
|
||||
then result = getTranslatedAllocatorCall(expr).getInstruction(CallTag())
|
||||
else result = getTranslatedCallInstruction(expr)
|
||||
else result = getTranslatedExpr(expr).getInstruction(CallTag())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,7 +409,7 @@ class TranslatedCallSideEffects extends TranslatedSideEffects, TTranslatedCallSi
|
||||
|
||||
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
result = getTranslatedCallInstruction(expr)
|
||||
result = getTranslatedExpr(expr).getInstruction(CallTag())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -609,7 +599,7 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff
|
||||
|
||||
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
result = getTranslatedCallInstruction(call)
|
||||
result = getTranslatedExpr(call).getInstruction(CallTag())
|
||||
}
|
||||
|
||||
final override int getInstructionIndex(InstructionTag tag) {
|
||||
|
||||
@@ -227,125 +227,6 @@ private predicate usedAsCondition(Expr expr) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `conv` is an `InheritanceConversion` that requires a `TranslatedLoad`, despite not being
|
||||
* marked as having an lvalue-to-rvalue conversion.
|
||||
*
|
||||
* This is necessary for an `InheritanceConversion` that is originally modeled as a
|
||||
* prvalue-to-prvalue conversion, since we transform it into a glvalue-to-glvalue conversion. If it
|
||||
* is actually consumed as a prvalue, such as on the right hand side of an assignment, we need to
|
||||
* load the resulting glvalue.
|
||||
*/
|
||||
private predicate isInheritanceConversionWithImplicitLoad(InheritanceConversion conv) {
|
||||
// Must have originally been a prvalue-to-prvalue conversion.
|
||||
isClassPRValue(conv.getExpr()) and
|
||||
not conv.hasLValueToRValueConversion() and
|
||||
// Exclude that case where this will be consumed as a glvalue, such as when used as the qualifier
|
||||
// of a field access.
|
||||
not isPRValueConversionOnGLValue(conv)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expr` is the result of a field access whose qualifier was a prvalue and whose result is
|
||||
* a prvalue. These accesses are not marked as having loads, but we do need a load in the IR.
|
||||
*/
|
||||
private predicate isPRValueFieldAccessWithImplicitLoad(Expr expr) {
|
||||
expr instanceof ValueFieldAccess and
|
||||
expr.isPRValueCategory() and
|
||||
// No need to do a load if we're replacing the result with a constant anyway.
|
||||
not isIRConstant(expr) and
|
||||
// Model an array prvalue as the address of the array, just like an array glvalue.
|
||||
not expr.getUnspecifiedType() instanceof ArrayType
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expr` is a prvalue of class type.
|
||||
*
|
||||
* This same test is used in several places.
|
||||
*/
|
||||
pragma[inline]
|
||||
private predicate isClassPRValue(Expr expr) {
|
||||
expr.isPRValueCategory() and
|
||||
expr.getUnspecifiedType() instanceof Class
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expr` is consumed as a glvalue by its parent. If `expr` is actually a prvalue, it will
|
||||
* have any lvalue-to-rvalue conversion ignored. If it does not have an lvalue-to-rvalue conversion,
|
||||
* it will be materialized into a temporary object.
|
||||
*/
|
||||
private predicate consumedAsGLValue(Expr expr) {
|
||||
isClassPRValue(expr) and
|
||||
(
|
||||
// Qualifier of a field access.
|
||||
expr = any(FieldAccess a).getQualifier().getFullyConverted()
|
||||
or
|
||||
// Qualifier of a member function call.
|
||||
expr = any(Call c).getQualifier().getFullyConverted()
|
||||
or
|
||||
// The operand of an inheritance conversion.
|
||||
expr = any(InheritanceConversion c).getExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expr` is a conversion that is originally a prvalue-to-prvalue conversion, but which is
|
||||
* applied to a prvalue that will actually be consumed as a glvalue.
|
||||
*/
|
||||
predicate isPRValueConversionOnGLValue(Conversion conv) {
|
||||
exists(Expr consumed |
|
||||
consumedAsGLValue(consumed) and
|
||||
isClassPRValue(conv.getExpr()) and
|
||||
(
|
||||
// Example: The conversion of `std::string` to `const std::string` when evaluating
|
||||
// `std::string("foo").c_str()`.
|
||||
conv instanceof PrvalueAdjustmentConversion
|
||||
or
|
||||
// Parentheses are transparent.
|
||||
conv instanceof ParenthesisExpr
|
||||
or
|
||||
// Example: The base class conversion in `f().m()`, when `m` is member function of a base
|
||||
// class of the return type of `f()`.
|
||||
conv instanceof InheritanceConversion
|
||||
) and
|
||||
(
|
||||
// Base case: The conversion is consumed directly.
|
||||
conv = consumed
|
||||
or
|
||||
// Recursive case: The conversion is the operand of another prvalue conversion.
|
||||
isPRValueConversionOnGLValue(conv.getConversion())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expr` is a prvalue of class type that is used in a context that requires a glvalue.
|
||||
*
|
||||
* Any conversions between `expr` and the ancestor that consumes the glvalue will also be treated
|
||||
* as glvalues, but are not part of this relation.
|
||||
*
|
||||
* For example:
|
||||
* ```c++
|
||||
* std::string("s").c_str();
|
||||
* ```
|
||||
* The object for the qualifier is a prvalue(load) of type `std::string`, but the actual
|
||||
* fully-converted qualifier of the call to `c_str()` is a prvalue adjustment conversion that
|
||||
* converts the type to `const std::string` to match the type of the `this` pointer of the
|
||||
* member function. In this case, `mustTransformToGLValue()` will hold for the temporary
|
||||
* `std::string` object, but not the prvalue adjustment on top of it.
|
||||
* `isPRValueConversionOnGLValue()` would hold for the prvalue adjustment.
|
||||
*/
|
||||
private predicate mustTransformToGLValue(Expr expr) {
|
||||
not isPRValueConversionOnGLValue(expr) and
|
||||
(
|
||||
// The expression is the fully converted qualifier, with no prvalue adjustments on top.
|
||||
consumedAsGLValue(expr)
|
||||
or
|
||||
// The expression has conversions on top, but they are all prvalue adjustments.
|
||||
isPRValueConversionOnGLValue(expr.getConversion())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expr` has an lvalue-to-rvalue conversion that should be ignored
|
||||
* when generating IR. This occurs for conversion from an lvalue of function type
|
||||
@@ -353,24 +234,15 @@ private predicate mustTransformToGLValue(Expr expr) {
|
||||
* AST as an lvalue-to-rvalue conversion, but the IR represents both a function
|
||||
* lvalue and a function pointer prvalue the same.
|
||||
*/
|
||||
predicate ignoreLoad(Expr expr) {
|
||||
private predicate ignoreLoad(Expr expr) {
|
||||
expr.hasLValueToRValueConversion() and
|
||||
(
|
||||
expr instanceof ThisExpr
|
||||
or
|
||||
expr instanceof FunctionAccess
|
||||
or
|
||||
expr instanceof ThisExpr or
|
||||
expr instanceof FunctionAccess or
|
||||
expr.(PointerDereferenceExpr).getOperand().getFullyConverted().getType().getUnspecifiedType()
|
||||
instanceof FunctionPointerType
|
||||
or
|
||||
instanceof FunctionPointerType or
|
||||
expr.(ReferenceDereferenceExpr).getExpr().getType().getUnspecifiedType() instanceof
|
||||
FunctionReferenceType
|
||||
or
|
||||
// The extractor represents the qualifier of a field access or member function call as a load of
|
||||
// the temporary object if the original qualifier was a prvalue. For IR purposes, we always want
|
||||
// to use the address of the temporary object as the qualifier of a field access or the `this`
|
||||
// argument to a member function call.
|
||||
mustTransformToGLValue(expr)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -385,17 +257,6 @@ private predicate needsLoadForParentExpr(Expr expr) {
|
||||
exists(CrementOperation crement | expr = crement.getOperand().getFullyConverted())
|
||||
or
|
||||
exists(AssignOperation ao | expr = ao.getLValue().getFullyConverted())
|
||||
or
|
||||
// For arguments that are passed by value but require a constructor call, the extractor emits a
|
||||
// `TemporaryObjectExpr` as the argument, and marks it as a glvalue. This is roughly how a code-
|
||||
// generating compiler would implement this, passing the address of the temporary so that the
|
||||
// callee is using the exact same memory location allocated by the caller. We don't fully model
|
||||
// this yet, though, so we'll synthesize a load so that we appear to be passing the temporary
|
||||
// object via a bitwise copy.
|
||||
exists(Call call |
|
||||
expr = call.getAnArgument().getFullyConverted().(TemporaryObjectExpr) and
|
||||
expr.isGLValueCategory()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -406,10 +267,6 @@ predicate hasTranslatedLoad(Expr expr) {
|
||||
expr.hasLValueToRValueConversion()
|
||||
or
|
||||
needsLoadForParentExpr(expr)
|
||||
or
|
||||
isPRValueFieldAccessWithImplicitLoad(expr)
|
||||
or
|
||||
isInheritanceConversionWithImplicitLoad(expr)
|
||||
) and
|
||||
not ignoreExpr(expr) and
|
||||
not isNativeCondition(expr) and
|
||||
@@ -417,16 +274,6 @@ predicate hasTranslatedLoad(Expr expr) {
|
||||
not ignoreLoad(expr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expr` should have a `TranslatedSyntheticTemporaryObject` on it.
|
||||
*/
|
||||
predicate hasTranslatedSyntheticTemporaryObject(Expr expr) {
|
||||
not ignoreExpr(expr) and
|
||||
mustTransformToGLValue(expr) and
|
||||
// If it's a load, we'll just ignore the load in `ignoreLoad()`.
|
||||
not expr.hasLValueToRValueConversion()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the specified `DeclarationEntry` needs an IR translation. An IR translation is only
|
||||
* necessary for automatic local variables, or for static local variables with dynamic
|
||||
@@ -457,9 +304,6 @@ newtype TTranslatedElement =
|
||||
// A separate element to handle the lvalue-to-rvalue conversion step of an
|
||||
// expression.
|
||||
TTranslatedLoad(Expr expr) { hasTranslatedLoad(expr) } or
|
||||
// A temporary object that we had to synthesize ourselves, so that we could do a field access or
|
||||
// method call on a prvalue.
|
||||
TTranslatedSyntheticTemporaryObject(Expr expr) { hasTranslatedSyntheticTemporaryObject(expr) } or
|
||||
// For expressions that would not otherwise generate an instruction.
|
||||
TTranslatedResultCopy(Expr expr) {
|
||||
not ignoreExpr(expr) and
|
||||
@@ -507,7 +351,6 @@ newtype TTranslatedElement =
|
||||
exists(ConstructorFieldInit fieldInit | fieldInit.getExpr().getFullyConverted() = expr) or
|
||||
exists(NewExpr newExpr | newExpr.getInitializer().getFullyConverted() = expr) or
|
||||
exists(ThrowExpr throw | throw.getExpr().getFullyConverted() = expr) or
|
||||
exists(TemporaryObjectExpr temp | temp.getExpr() = expr) or
|
||||
exists(LambdaExpression lambda | lambda.getInitializer().getFullyConverted() = expr)
|
||||
)
|
||||
} or
|
||||
@@ -752,10 +595,10 @@ abstract class TranslatedElement extends TTranslatedElement {
|
||||
abstract TranslatedElement getChild(int id);
|
||||
|
||||
/**
|
||||
* Gets the an identifier string for the element. This id is unique within
|
||||
* Gets the an identifier string for the element. This string is unique within
|
||||
* the scope of the element's function.
|
||||
*/
|
||||
final int getId() { result = getUniqueId() }
|
||||
final string getId() { result = getUniqueId().toString() }
|
||||
|
||||
private TranslatedElement getChildByRank(int rankIndex) {
|
||||
result =
|
||||
|
||||
@@ -15,9 +15,8 @@ private import TranslatedStmt
|
||||
import TranslatedCall
|
||||
|
||||
/**
|
||||
* Gets the TranslatedExpr for the specified expression. If `expr` is a load or synthesized
|
||||
* temporary object, the result is the TranslatedExpr for the load or synthetic temporary object
|
||||
* portion.
|
||||
* Gets the TranslatedExpr for the specified expression. If `expr` is a load,
|
||||
* the result is the TranslatedExpr for the load portion.
|
||||
*/
|
||||
TranslatedExpr getTranslatedExpr(Expr expr) {
|
||||
result.getExpr() = expr and
|
||||
@@ -108,17 +107,12 @@ abstract class TranslatedCoreExpr extends TranslatedExpr {
|
||||
// If this TranslatedExpr doesn't produce the result, then it must represent
|
||||
// a glvalue that is then loaded by a TranslatedLoad.
|
||||
hasTranslatedLoad(expr)
|
||||
or
|
||||
// The expression should be treated as a glvalue because its operand was forced to be a glvalue,
|
||||
// such as for the qualifier of a member access.
|
||||
isPRValueConversionOnGLValue(expr)
|
||||
}
|
||||
|
||||
final override predicate producesExprResult() {
|
||||
// If there's no load or temp object, then this is the only TranslatedExpr for this
|
||||
// If there's no load, then this is the only TranslatedExpr for this
|
||||
// expression.
|
||||
not hasTranslatedLoad(expr) and
|
||||
not hasTranslatedSyntheticTemporaryObject(expr) and
|
||||
// If there's a result copy, then this expression's result is the copy.
|
||||
not exprNeedsCopyIfNotLoaded(expr)
|
||||
}
|
||||
@@ -252,35 +246,19 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext,
|
||||
private TranslatedCondition getCondition() { result = getTranslatedCondition(expr) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a node synthesized to adjust the value category of its operand.
|
||||
* One of:
|
||||
* - `TranslatedLoad` - Convert from glvalue to prvalue by loading from the location.
|
||||
* - `TranslatedSyntheticTemporaryObject` - Convert from prvalue to glvalue by storing to a
|
||||
* temporary variable.
|
||||
*/
|
||||
abstract class TranslatedValueCategoryAdjustment extends TranslatedExpr {
|
||||
final override Instruction getFirstInstruction() { result = getOperand().getFirstInstruction() }
|
||||
|
||||
final override TranslatedElement getChild(int id) { id = 0 and result = getOperand() }
|
||||
|
||||
final override predicate producesExprResult() {
|
||||
// A temp object always produces the result of the expression.
|
||||
any()
|
||||
}
|
||||
|
||||
final TranslatedCoreExpr getOperand() { result.getExpr() = expr }
|
||||
}
|
||||
|
||||
/**
|
||||
* IR translation of an implicit lvalue-to-rvalue conversion on the result of
|
||||
* an expression.
|
||||
*/
|
||||
class TranslatedLoad extends TranslatedValueCategoryAdjustment, TTranslatedLoad {
|
||||
class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
|
||||
TranslatedLoad() { this = TTranslatedLoad(expr) }
|
||||
|
||||
override string toString() { result = "Load of " + expr.toString() }
|
||||
|
||||
override Instruction getFirstInstruction() { result = getOperand().getFirstInstruction() }
|
||||
|
||||
override TranslatedElement getChild(int id) { id = 0 and result = getOperand() }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = LoadTag() and
|
||||
opcode instanceof Opcode::Load and
|
||||
@@ -308,75 +286,18 @@ class TranslatedLoad extends TranslatedValueCategoryAdjustment, TTranslatedLoad
|
||||
result = getOperand().getResult()
|
||||
)
|
||||
}
|
||||
|
||||
final override predicate producesExprResult() {
|
||||
// A load always produces the result of the expression.
|
||||
any()
|
||||
}
|
||||
|
||||
TranslatedCoreExpr getOperand() { result.getExpr() = expr }
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a temporary object synthesized by the IR to hold a class prvalue on which
|
||||
* a member access is going to be performed. This differs from `TranslatedTemporaryObjectExpr` in
|
||||
* that instances of `TranslatedSyntheticTemporaryObject` are synthesized during IR construction,
|
||||
* whereas `TranslatedTemporaryObjectExpr` instances are created from `TemporaryObjectExpr` nodes
|
||||
* from the AST.
|
||||
*/
|
||||
class TranslatedSyntheticTemporaryObject extends TranslatedValueCategoryAdjustment,
|
||||
TTranslatedSyntheticTemporaryObject {
|
||||
TranslatedSyntheticTemporaryObject() { this = TTranslatedSyntheticTemporaryObject(expr) }
|
||||
|
||||
override string toString() { result = "Temporary materialization of " + expr.toString() }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = InitializerVariableAddressTag() and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = getTypeForGLValue(expr.getType())
|
||||
or
|
||||
tag = InitializerStoreTag() and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = getTypeForPRValue(expr.getType())
|
||||
}
|
||||
|
||||
override predicate isResultGLValue() { any() }
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = InitializerVariableAddressTag() and
|
||||
result = getInstruction(InitializerStoreTag()) and
|
||||
kind instanceof GotoEdge
|
||||
or
|
||||
tag = InitializerStoreTag() and
|
||||
result = getParent().getChildSuccessor(this) and
|
||||
kind instanceof GotoEdge
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = getOperand() and result = getInstruction(InitializerVariableAddressTag())
|
||||
}
|
||||
|
||||
override Instruction getResult() { result = getInstruction(InitializerVariableAddressTag()) }
|
||||
|
||||
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = InitializerStoreTag() and
|
||||
(
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInstruction(InitializerVariableAddressTag())
|
||||
or
|
||||
operandTag instanceof StoreValueOperandTag and
|
||||
result = getOperand().getResult()
|
||||
)
|
||||
}
|
||||
|
||||
final override predicate hasTempVariable(TempVariableTag tag, CppType type) {
|
||||
tag = TempObjectTempVar() and
|
||||
type = getTypeForPRValue(expr.getType())
|
||||
}
|
||||
|
||||
final override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = InitializerVariableAddressTag() and
|
||||
result = getIRTempVariable(expr, TempObjectTempVar())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IR translation of an expression that simply returns its result. We generate an otherwise useless
|
||||
* `CopyValue` instruction for these expressions so that there is at least one instruction
|
||||
* associated with the expression.
|
||||
* IR translation of an implicit lvalue-to-rvalue conversion on the result of
|
||||
* an expression.
|
||||
*/
|
||||
class TranslatedResultCopy extends TranslatedExpr, TTranslatedResultCopy {
|
||||
TranslatedResultCopy() { this = TTranslatedResultCopy(expr) }
|
||||
@@ -2128,38 +2049,6 @@ class TranslatedBinaryConditionalExpr extends TranslatedConditionalExpr {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IR translation of the materialization of a temporary object.
|
||||
*
|
||||
* This translation allocates a temporary variable, and initializes it treating `expr.getExpr()` as
|
||||
* its initializer.
|
||||
*/
|
||||
class TranslatedTemporaryObjectExpr extends TranslatedNonConstantExpr,
|
||||
TranslatedVariableInitialization {
|
||||
override TemporaryObjectExpr expr;
|
||||
|
||||
final override predicate hasTempVariable(TempVariableTag tag, CppType type) {
|
||||
tag = TempObjectTempVar() and
|
||||
type = getTypeForPRValue(expr.getType())
|
||||
}
|
||||
|
||||
override Type getTargetType() { result = expr.getType() }
|
||||
|
||||
final override TranslatedInitialization getInitialization() {
|
||||
result = getTranslatedInitialization(expr.getExpr())
|
||||
}
|
||||
|
||||
final override IRVariable getIRVariable() {
|
||||
result = getIRTempVariable(expr, TempObjectTempVar())
|
||||
}
|
||||
|
||||
final override Instruction getInitializationSuccessor() {
|
||||
result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
final override Instruction getResult() { result = getTargetAddress() }
|
||||
}
|
||||
|
||||
/**
|
||||
* IR translation of a `throw` expression.
|
||||
*/
|
||||
|
||||
@@ -26,6 +26,15 @@ class IRBlockBase extends TIRBlock {
|
||||
/** Gets the source location of the first non-`Phi` instruction in this block. */
|
||||
final Language::Location getLocation() { result = getFirstInstruction().getLocation() }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets a string that uniquely identifies this block within its enclosing function.
|
||||
*
|
||||
* This predicate is used by debugging and printing code only.
|
||||
*/
|
||||
final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
@@ -38,15 +47,14 @@ class IRBlockBase extends TIRBlock {
|
||||
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
|
||||
) and
|
||||
this =
|
||||
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
|
||||
rank[result + 1](IRBlock funcBlock, int sortOverride |
|
||||
funcBlock.getEnclosingFunction() = getEnclosingFunction() and
|
||||
funcBlock.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
|
||||
// Ensure that the block containing `EnterFunction` always comes first.
|
||||
if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction
|
||||
then sortOverride = 0
|
||||
else sortOverride = 1
|
||||
|
|
||||
funcBlock order by sortOverride, sortKey1, sortKey2
|
||||
funcBlock order by sortOverride, funcBlock.getUniqueId()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -163,46 +171,6 @@ class IRBlock extends IRBlockBase {
|
||||
not strictlyDominates(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block immediately post-dominates `block`.
|
||||
*
|
||||
* Block `A` immediate post-dominates block `B` if block `A` strictly post-dominates block `B` and
|
||||
* block `B` is a direct successor of block `A`.
|
||||
*/
|
||||
final predicate immediatelyPostDominates(IRBlock block) {
|
||||
blockImmediatelyPostDominates(this, block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block strictly post-dominates `block`.
|
||||
*
|
||||
* Block `A` strictly post-dominates block `B` if block `A` post-dominates block `B` and blocks `A`
|
||||
* and `B` are not the same block.
|
||||
*/
|
||||
final predicate strictlyPostDominates(IRBlock block) {
|
||||
blockImmediatelyPostDominates+(this, block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block is a post-dominator of `block`.
|
||||
*
|
||||
* Block `A` post-dominates block `B` if any control flow path from `B` to the exit block of the
|
||||
* function must pass through block `A`. A block always post-dominates itself.
|
||||
*/
|
||||
final predicate postDominates(IRBlock block) { strictlyPostDominates(block) or this = block }
|
||||
|
||||
/**
|
||||
* Gets a block on the post-dominance frontier of this block.
|
||||
*
|
||||
* The post-dominance frontier of block `A` is the set of blocks `B` such that block `A` does not
|
||||
* post-dominate block `B`, but block `A` does post-dominate an immediate successor of block `B`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final IRBlock postPominanceFrontier() {
|
||||
postDominates(result.getASuccessor()) and
|
||||
not strictlyPostDominates(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block is reachable from the entry block of its function.
|
||||
*/
|
||||
@@ -320,12 +288,3 @@ private module Cached {
|
||||
}
|
||||
|
||||
private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) }
|
||||
|
||||
private predicate blockFunctionExit(IRBlock exit) {
|
||||
exit.getLastInstruction() instanceof ExitFunctionInstruction
|
||||
}
|
||||
|
||||
private predicate blockPredecessor(IRBlock src, IRBlock pred) { src.getAPredecessor() = pred }
|
||||
|
||||
private predicate blockImmediatelyPostDominates(IRBlock postDominator, IRBlock block) =
|
||||
idominance(blockFunctionExit/1, blockPredecessor/2)(_, postDominator, block)
|
||||
|
||||
@@ -494,34 +494,4 @@ module InstructionConsistency {
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the object address operand for the given `FieldAddress` instruction does not have an
|
||||
* address type.
|
||||
*/
|
||||
query predicate fieldAddressOnNonPointer(
|
||||
FieldAddressInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
not instr.getObjectAddressOperand().getIRType() instanceof IRAddressType and
|
||||
message =
|
||||
"FieldAddress instruction '" + instr.toString() +
|
||||
"' has an object address operand that is not an address, in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `this` argument operand for the given `Call` instruction does not have an address
|
||||
* type.
|
||||
*/
|
||||
query predicate thisArgumentIsNonPointer(
|
||||
CallInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
|
||||
) {
|
||||
exists(ThisArgumentOperand thisOperand | thisOperand = instr.getThisArgumentOperand() |
|
||||
not thisOperand.getIRType() instanceof IRAddressType
|
||||
) and
|
||||
message =
|
||||
"Call instruction '" + instr.toString() +
|
||||
"' has a `this` argument operand that is not an address, in function '$@'." and
|
||||
irFunc = getInstructionIRFunction(instr, irFuncText)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,16 +171,6 @@ class Instruction extends Construction::TStageInstruction {
|
||||
*/
|
||||
final string getUniqueId() { result = Construction::getInstructionUniqueId(this) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets two sort keys for this instruction - used to order instructions for printing
|
||||
* in test outputs.
|
||||
*/
|
||||
final predicate hasSortKeys(int key1, int key2) {
|
||||
Construction::instructionHasSortKeys(this, key1, key2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the basic block that contains this instruction.
|
||||
*/
|
||||
@@ -582,17 +572,6 @@ class InitializeParameterInstruction extends VariableInstruction {
|
||||
* Gets the parameter initialized by this instruction.
|
||||
*/
|
||||
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
|
||||
|
||||
/**
|
||||
* Holds if this instruction initializes the parameter with index `index`, or
|
||||
* if `index` is `-1` and this instruction initializes `this`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final predicate hasIndex(int index) {
|
||||
index >= 0 and index = this.getParameter().getIndex()
|
||||
or
|
||||
index = -1 and this.getIRVariable() instanceof IRThisVariable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -616,18 +595,6 @@ class InitializeIndirectionInstruction extends VariableInstruction {
|
||||
* Gets the parameter initialized by this instruction.
|
||||
*/
|
||||
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
|
||||
|
||||
/**
|
||||
* Holds if this instruction initializes the memory pointed to by the parameter with
|
||||
* index `index`, or if `index` is `-1` and this instruction initializes the memory
|
||||
* pointed to by `this`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final predicate hasIndex(int index) {
|
||||
index >= 0 and index = this.getParameter().getIndex()
|
||||
or
|
||||
index = -1 and this.getIRVariable() instanceof IRThisVariable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -802,17 +769,6 @@ class ReturnIndirectionInstruction extends VariableInstruction {
|
||||
* Holds if this instruction is the return indirection for `this`.
|
||||
*/
|
||||
final predicate isThisIndirection() { var instanceof IRThisVariable }
|
||||
|
||||
/**
|
||||
* Holds if this instruction is the return indirection for the parameter with index `index`, or
|
||||
* if this instruction is the return indirection for `this` and `index` is `-1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final predicate hasIndex(int index) {
|
||||
index >= 0 and index = this.getParameter().getIndex()
|
||||
or
|
||||
index = -1 and this.isThisIndirection()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1611,7 +1567,6 @@ class CallInstruction extends Instruction {
|
||||
/**
|
||||
* Gets the argument operand at the specified index.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final PositionalArgumentOperand getPositionalArgumentOperand(int index) {
|
||||
result = getAnOperand() and
|
||||
result.getIndex() = index
|
||||
@@ -1620,27 +1575,10 @@ class CallInstruction extends Instruction {
|
||||
/**
|
||||
* Gets the argument at the specified index.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final Instruction getPositionalArgument(int index) {
|
||||
result = getPositionalArgumentOperand(index).getDef()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument operand at the specified index, or `this` if `index` is `-1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final ArgumentOperand getArgumentOperand(int index) {
|
||||
index >= 0 and result = getPositionalArgumentOperand(index)
|
||||
or
|
||||
index = -1 and result = getThisArgumentOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument at the specified index, or `this` if `index` is `-1`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
final Instruction getArgument(int index) { result = getArgumentOperand(index).getDef() }
|
||||
|
||||
/**
|
||||
* Gets the number of arguments of the call, including the `this` pointer, if any.
|
||||
*/
|
||||
|
||||
@@ -403,27 +403,16 @@ private import PhiInsertion
|
||||
* instruction for the virtual variable as a whole.
|
||||
*/
|
||||
private module PhiInsertion {
|
||||
/**
|
||||
* Holds if `phiBlock` is a block in the dominance frontier of a block that has a definition of the
|
||||
* memory location `defLocation`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate dominanceFrontierOfDefinition(
|
||||
Alias::MemoryLocation defLocation, OldBlock phiBlock
|
||||
) {
|
||||
exists(OldBlock defBlock |
|
||||
phiBlock = Dominance::getDominanceFrontier(defBlock) and
|
||||
definitionHasDefinitionInBlock(defLocation, defBlock)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `Phi` instruction needs to be inserted for location `defLocation` at the beginning of block `phiBlock`.
|
||||
*/
|
||||
predicate definitionHasPhiNode(Alias::MemoryLocation defLocation, OldBlock phiBlock) {
|
||||
dominanceFrontierOfDefinition(defLocation, phiBlock) and
|
||||
/* We can also eliminate those nodes where the definition is not live on any incoming edge */
|
||||
definitionLiveOnEntryToBlock(defLocation, phiBlock)
|
||||
exists(OldBlock defBlock |
|
||||
phiBlock = Dominance::getDominanceFrontier(defBlock) and
|
||||
definitionHasDefinitionInBlock(defLocation, defBlock) and
|
||||
/* We can also eliminate those nodes where the definition is not live on any incoming edge */
|
||||
definitionLiveOnEntryToBlock(defLocation, phiBlock)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -867,8 +856,7 @@ private module CachedForDebugging {
|
||||
exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity |
|
||||
instr = getPhi(phiBlock, location) and
|
||||
result =
|
||||
"Phi Block(" + phiBlock.getFirstInstruction().getUniqueId() + ")[" + specificity + "]: " +
|
||||
location.getUniqueId() and
|
||||
"Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and
|
||||
if location instanceof Alias::VirtualVariable
|
||||
then
|
||||
// Sort Phi nodes for virtual variables before Phi nodes for member locations.
|
||||
@@ -885,24 +873,6 @@ private module CachedForDebugging {
|
||||
result.getAST() = var.getAST() and
|
||||
result.getTag() = var.getTag()
|
||||
}
|
||||
|
||||
cached
|
||||
predicate instructionHasSortKeys(Instruction instr, int key1, int key2) {
|
||||
exists(OldInstruction oldInstr |
|
||||
oldInstr = getOldInstruction(instr) and
|
||||
oldInstr.hasSortKeys(key1, key2)
|
||||
)
|
||||
or
|
||||
instr instanceof TUnreachedInstruction and
|
||||
key1 = maxValue() and
|
||||
key2 = maxValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the maximum representable integer.
|
||||
*/
|
||||
cached
|
||||
int maxValue() { result = 2147483647 }
|
||||
}
|
||||
|
||||
module SSAConsistency {
|
||||
|
||||
@@ -4,8 +4,7 @@ newtype TTempVariableTag =
|
||||
ThrowTempVar() or
|
||||
LambdaTempVar() or
|
||||
EllipsisTempVar() or
|
||||
ThisTempVar() or
|
||||
TempObjectTempVar()
|
||||
ThisTempVar()
|
||||
|
||||
string getTempVariableTagId(TTempVariableTag tag) {
|
||||
tag = ConditionValueTempVar() and result = "CondVal"
|
||||
@@ -19,6 +18,4 @@ string getTempVariableTagId(TTempVariableTag tag) {
|
||||
tag = EllipsisTempVar() and result = "Ellipsis"
|
||||
or
|
||||
tag = ThisTempVar() and result = "This"
|
||||
or
|
||||
tag = TempObjectTempVar() and result = "Temp"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
private import implementations.Allocation
|
||||
private import implementations.Deallocation
|
||||
private import implementations.Fread
|
||||
private import implementations.Getenv
|
||||
private import implementations.Gets
|
||||
private import implementations.IdentityFunction
|
||||
private import implementations.Inet
|
||||
|
||||
@@ -10,7 +10,7 @@ import semmle.code.cpp.models.interfaces.Allocation
|
||||
* An allocation function (such as `malloc`) that has an argument for the size
|
||||
* in bytes.
|
||||
*/
|
||||
private class MallocAllocationFunction extends AllocationFunction {
|
||||
class MallocAllocationFunction extends AllocationFunction {
|
||||
int sizeArg;
|
||||
|
||||
MallocAllocationFunction() {
|
||||
@@ -112,7 +112,7 @@ private class MallocAllocationFunction extends AllocationFunction {
|
||||
* An allocation function (such as `alloca`) that does not require a
|
||||
* corresponding free (and has an argument for the size in bytes).
|
||||
*/
|
||||
private class AllocaAllocationFunction extends AllocationFunction {
|
||||
class AllocaAllocationFunction extends AllocationFunction {
|
||||
int sizeArg;
|
||||
|
||||
AllocaAllocationFunction() {
|
||||
@@ -137,7 +137,7 @@ private class AllocaAllocationFunction extends AllocationFunction {
|
||||
* An allocation function (such as `calloc`) that has an argument for the size
|
||||
* and another argument for the size of those units (in bytes).
|
||||
*/
|
||||
private class CallocAllocationFunction extends AllocationFunction {
|
||||
class CallocAllocationFunction extends AllocationFunction {
|
||||
int sizeArg;
|
||||
int multArg;
|
||||
|
||||
@@ -158,7 +158,7 @@ private class CallocAllocationFunction extends AllocationFunction {
|
||||
* An allocation function (such as `realloc`) that has an argument for the size
|
||||
* in bytes, and an argument for an existing pointer that is to be reallocated.
|
||||
*/
|
||||
private class ReallocAllocationFunction extends AllocationFunction {
|
||||
class ReallocAllocationFunction extends AllocationFunction {
|
||||
int sizeArg;
|
||||
int reallocArg;
|
||||
|
||||
@@ -197,7 +197,7 @@ private class ReallocAllocationFunction extends AllocationFunction {
|
||||
* A miscellaneous allocation function that has no explicit argument for
|
||||
* the size of the allocation.
|
||||
*/
|
||||
private class SizelessAllocationFunction extends AllocationFunction {
|
||||
class SizelessAllocationFunction extends AllocationFunction {
|
||||
SizelessAllocationFunction() {
|
||||
exists(string name |
|
||||
hasGlobalName(name) and
|
||||
@@ -236,6 +236,40 @@ private class SizelessAllocationFunction extends AllocationFunction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `operator new` or `operator new[]` function that may be associated with `new` or
|
||||
* `new[]` expressions. Note that `new` and `new[]` are not function calls, but these
|
||||
* functions may also be called directly.
|
||||
*/
|
||||
class OperatorNewAllocationFunction extends AllocationFunction {
|
||||
OperatorNewAllocationFunction() {
|
||||
exists(string name |
|
||||
hasGlobalName(name) and
|
||||
(
|
||||
// operator new(bytes, ...)
|
||||
name = "operator new"
|
||||
or
|
||||
// operator new[](bytes, ...)
|
||||
name = "operator new[]"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override int getSizeArg() { result = 0 }
|
||||
|
||||
override predicate requiresDealloc() { not exists(getPlacementArgument()) }
|
||||
|
||||
/**
|
||||
* Gets the position of the placement pointer if this is a placement
|
||||
* `operator new` function.
|
||||
*/
|
||||
int getPlacementArgument() {
|
||||
getNumberOfParameters() = 2 and
|
||||
getParameter(1).getType() instanceof VoidPointerType and
|
||||
result = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `sizeExpr` is an expression consisting of a subexpression
|
||||
* `lengthExpr` multiplied by a constant `sizeof` that is the result of a
|
||||
@@ -268,7 +302,7 @@ private predicate deconstructSizeExpr(Expr sizeExpr, Expr lengthExpr, int sizeof
|
||||
/**
|
||||
* An allocation expression that is a function call, such as call to `malloc`.
|
||||
*/
|
||||
private class CallAllocationExpr extends AllocationExpr, FunctionCall {
|
||||
class CallAllocationExpr extends AllocationExpr, FunctionCall {
|
||||
AllocationFunction target;
|
||||
|
||||
CallAllocationExpr() {
|
||||
@@ -319,7 +353,7 @@ private class CallAllocationExpr extends AllocationExpr, FunctionCall {
|
||||
/**
|
||||
* An allocation expression that is a `new` expression.
|
||||
*/
|
||||
private class NewAllocationExpr extends AllocationExpr, NewExpr {
|
||||
class NewAllocationExpr extends AllocationExpr, NewExpr {
|
||||
NewAllocationExpr() { this instanceof NewExpr }
|
||||
|
||||
override int getSizeBytes() { result = getAllocatedType().getSize() }
|
||||
@@ -332,7 +366,7 @@ private class NewAllocationExpr extends AllocationExpr, NewExpr {
|
||||
/**
|
||||
* An allocation expression that is a `new []` expression.
|
||||
*/
|
||||
private class NewArrayAllocationExpr extends AllocationExpr, NewArrayExpr {
|
||||
class NewArrayAllocationExpr extends AllocationExpr, NewArrayExpr {
|
||||
NewArrayAllocationExpr() { this instanceof NewArrayExpr }
|
||||
|
||||
override Expr getSizeExpr() {
|
||||
|
||||
@@ -9,7 +9,7 @@ import semmle.code.cpp.models.interfaces.Deallocation
|
||||
/**
|
||||
* A deallocation function such as `free`.
|
||||
*/
|
||||
private class StandardDeallocationFunction extends DeallocationFunction {
|
||||
class StandardDeallocationFunction extends DeallocationFunction {
|
||||
int freedArg;
|
||||
|
||||
StandardDeallocationFunction() {
|
||||
@@ -89,10 +89,32 @@ private class StandardDeallocationFunction extends DeallocationFunction {
|
||||
override int getFreedArg() { result = freedArg }
|
||||
}
|
||||
|
||||
/**
|
||||
* An `operator delete` or `operator delete[]` function that may be associated
|
||||
* with `delete` or `delete[]` expressions. Note that `delete` and `delete[]`
|
||||
* are not function calls, but these functions may also be called directly.
|
||||
*/
|
||||
class OperatorDeleteDeallocationFunction extends DeallocationFunction {
|
||||
OperatorDeleteDeallocationFunction() {
|
||||
exists(string name |
|
||||
hasGlobalName(name) and
|
||||
(
|
||||
// operator delete(pointer, ...)
|
||||
name = "operator delete"
|
||||
or
|
||||
// operator delete[](pointer, ...)
|
||||
name = "operator delete[]"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override int getFreedArg() { result = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* An deallocation expression that is a function call, such as call to `free`.
|
||||
*/
|
||||
private class CallDeallocationExpr extends DeallocationExpr, FunctionCall {
|
||||
class CallDeallocationExpr extends DeallocationExpr, FunctionCall {
|
||||
DeallocationFunction target;
|
||||
|
||||
CallDeallocationExpr() { target = getTarget() }
|
||||
@@ -103,7 +125,7 @@ private class CallDeallocationExpr extends DeallocationExpr, FunctionCall {
|
||||
/**
|
||||
* An deallocation expression that is a `delete` expression.
|
||||
*/
|
||||
private class DeleteDeallocationExpr extends DeallocationExpr, DeleteExpr {
|
||||
class DeleteDeallocationExpr extends DeallocationExpr, DeleteExpr {
|
||||
DeleteDeallocationExpr() { this instanceof DeleteExpr }
|
||||
|
||||
override Expr getFreedExpr() { result = getExpr() }
|
||||
@@ -112,7 +134,7 @@ private class DeleteDeallocationExpr extends DeallocationExpr, DeleteExpr {
|
||||
/**
|
||||
* An deallocation expression that is a `delete []` expression.
|
||||
*/
|
||||
private class DeleteArrayDeallocationExpr extends DeallocationExpr, DeleteArrayExpr {
|
||||
class DeleteArrayDeallocationExpr extends DeallocationExpr, DeleteArrayExpr {
|
||||
DeleteArrayDeallocationExpr() { this instanceof DeleteArrayExpr }
|
||||
|
||||
override Expr getFreedExpr() { result = getExpr() }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
|
||||
private class Fread extends AliasFunction, RemoteFlowFunction {
|
||||
class Fread extends AliasFunction, RemoteFlowFunction {
|
||||
Fread() { this.hasGlobalName("fread") }
|
||||
|
||||
override predicate parameterNeverEscapes(int n) {
|
||||
|
||||
@@ -6,8 +6,7 @@ import semmle.code.cpp.models.interfaces.FlowSource
|
||||
/**
|
||||
* The standard functions `getdelim`, `getwdelim` and the glibc variant `__getdelim`.
|
||||
*/
|
||||
private class GetDelimFunction extends TaintFunction, AliasFunction, SideEffectFunction,
|
||||
RemoteFlowFunction {
|
||||
class GetDelimFunction extends TaintFunction, AliasFunction, SideEffectFunction, RemoteFlowFunction {
|
||||
GetDelimFunction() { hasGlobalName(["getdelim", "getwdelim", "__getdelim"]) }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput i, FunctionOutput o) {
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Provides an implementation class modeling the POSIX function `getenv`.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
|
||||
/**
|
||||
* The POSIX function `getenv`.
|
||||
*/
|
||||
class Getenv extends LocalFlowFunction {
|
||||
Getenv() { this.hasGlobalName("getenv") }
|
||||
|
||||
override predicate hasLocalFlowSource(FunctionOutput output, string description) {
|
||||
(
|
||||
output.isReturnValueDeref() or
|
||||
output.isReturnValue()
|
||||
) and
|
||||
description = "an environment variable"
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import semmle.code.cpp.models.interfaces.FlowSource
|
||||
/**
|
||||
* The standard functions `gets` and `fgets`.
|
||||
*/
|
||||
private class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, AliasFunction,
|
||||
class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, AliasFunction,
|
||||
SideEffectFunction, RemoteFlowFunction {
|
||||
GetsFunction() {
|
||||
// gets(str)
|
||||
|
||||
@@ -6,7 +6,7 @@ import semmle.code.cpp.models.interfaces.SideEffect
|
||||
/**
|
||||
* The standard function templates `std::move` and `std::forward`.
|
||||
*/
|
||||
private class IdentityFunction extends DataFlowFunction, SideEffectFunction, AliasFunction {
|
||||
class IdentityFunction extends DataFlowFunction, SideEffectFunction, AliasFunction {
|
||||
IdentityFunction() {
|
||||
this.getNamespace().getParentNamespace() instanceof GlobalNamespace and
|
||||
this.getNamespace().getName() = "std" and
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
|
||||
private class InetNtoa extends TaintFunction {
|
||||
class InetNtoa extends TaintFunction {
|
||||
InetNtoa() { hasGlobalName("inet_ntoa") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -11,7 +10,7 @@ private class InetNtoa extends TaintFunction {
|
||||
}
|
||||
}
|
||||
|
||||
private class InetAton extends TaintFunction, ArrayFunction {
|
||||
class InetAton extends TaintFunction, ArrayFunction {
|
||||
InetAton() { hasGlobalName("inet_aton") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -31,7 +30,7 @@ private class InetAton extends TaintFunction, ArrayFunction {
|
||||
}
|
||||
}
|
||||
|
||||
private class InetAddr extends TaintFunction, ArrayFunction, AliasFunction {
|
||||
class InetAddr extends TaintFunction, ArrayFunction, AliasFunction {
|
||||
InetAddr() { hasGlobalName("inet_addr") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -50,7 +49,7 @@ private class InetAddr extends TaintFunction, ArrayFunction, AliasFunction {
|
||||
override predicate parameterIsAlwaysReturned(int index) { none() }
|
||||
}
|
||||
|
||||
private class InetNetwork extends TaintFunction, ArrayFunction {
|
||||
class InetNetwork extends TaintFunction, ArrayFunction {
|
||||
InetNetwork() { hasGlobalName("inet_network") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -63,7 +62,7 @@ private class InetNetwork extends TaintFunction, ArrayFunction {
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 }
|
||||
}
|
||||
|
||||
private class InetMakeaddr extends TaintFunction {
|
||||
class InetMakeaddr extends TaintFunction {
|
||||
InetMakeaddr() { hasGlobalName("inet_makeaddr") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -75,7 +74,7 @@ private class InetMakeaddr extends TaintFunction {
|
||||
}
|
||||
}
|
||||
|
||||
private class InetLnaof extends TaintFunction {
|
||||
class InetLnaof extends TaintFunction {
|
||||
InetLnaof() { hasGlobalName("inet_lnaof") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -84,7 +83,7 @@ private class InetLnaof extends TaintFunction {
|
||||
}
|
||||
}
|
||||
|
||||
private class InetNetof extends TaintFunction {
|
||||
class InetNetof extends TaintFunction {
|
||||
InetNetof() { hasGlobalName("inet_netof") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -93,7 +92,7 @@ private class InetNetof extends TaintFunction {
|
||||
}
|
||||
}
|
||||
|
||||
private class InetPton extends TaintFunction, ArrayFunction {
|
||||
class InetPton extends TaintFunction, ArrayFunction {
|
||||
InetPton() { hasGlobalName("inet_pton") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -113,7 +112,7 @@ private class InetPton extends TaintFunction, ArrayFunction {
|
||||
override predicate hasArrayWithUnknownSize(int bufParam) { bufParam = 2 }
|
||||
}
|
||||
|
||||
private class Gethostbyname extends TaintFunction, ArrayFunction {
|
||||
class Gethostbyname extends TaintFunction, ArrayFunction {
|
||||
Gethostbyname() { hasGlobalName("gethostbyname") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -126,7 +125,7 @@ private class Gethostbyname extends TaintFunction, ArrayFunction {
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 }
|
||||
}
|
||||
|
||||
private class Gethostbyaddr extends TaintFunction, ArrayFunction {
|
||||
class Gethostbyaddr extends TaintFunction, ArrayFunction {
|
||||
Gethostbyaddr() { hasGlobalName("gethostbyaddr") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
|
||||
@@ -13,7 +13,7 @@ import semmle.code.cpp.models.interfaces.Iterator
|
||||
/**
|
||||
* An instantiation of the `std::iterator_traits` template.
|
||||
*/
|
||||
private class IteratorTraits extends Class {
|
||||
class IteratorTraits extends Class {
|
||||
IteratorTraits() {
|
||||
this.hasQualifiedName("std", "iterator_traits") and
|
||||
not this instanceof TemplateClass and
|
||||
@@ -29,7 +29,7 @@ private class IteratorTraits extends Class {
|
||||
/**
|
||||
* A type which has the typedefs expected for an iterator.
|
||||
*/
|
||||
private class IteratorByTypedefs extends Iterator, Class {
|
||||
class IteratorByTypedefs extends Class {
|
||||
IteratorByTypedefs() {
|
||||
this.getAMember().(TypedefType).hasName("difference_type") and
|
||||
this.getAMember().(TypedefType).hasName("value_type") and
|
||||
@@ -43,16 +43,19 @@ private class IteratorByTypedefs extends Iterator, Class {
|
||||
/**
|
||||
* The `std::iterator` class.
|
||||
*/
|
||||
private class StdIterator extends Iterator, Class {
|
||||
class StdIterator extends Class {
|
||||
StdIterator() { this.hasQualifiedName("std", "iterator") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A type that is deduced to be an iterator because there is a corresponding
|
||||
* `std::iterator_traits` instantiation for it.
|
||||
* A type which can be used as an iterator
|
||||
*/
|
||||
private class IteratorByTraits extends Iterator {
|
||||
IteratorByTraits() { exists(IteratorTraits it | it.getIteratorType() = this) }
|
||||
class Iterator extends Type {
|
||||
Iterator() {
|
||||
this instanceof IteratorByTypedefs or
|
||||
exists(IteratorTraits it | it.getIteratorType() = this) or
|
||||
this instanceof StdIterator
|
||||
}
|
||||
}
|
||||
|
||||
private FunctionInput getIteratorArgumentInput(Operator op, int index) {
|
||||
@@ -78,8 +81,7 @@ private FunctionInput getIteratorArgumentInput(Operator op, int index) {
|
||||
/**
|
||||
* A non-member prefix `operator*` function for an iterator type.
|
||||
*/
|
||||
private class IteratorPointerDereferenceOperator extends Operator, TaintFunction,
|
||||
IteratorReferenceFunction {
|
||||
class IteratorPointerDereferenceOperator extends Operator, TaintFunction, IteratorReferenceFunction {
|
||||
FunctionInput iteratorInput;
|
||||
|
||||
IteratorPointerDereferenceOperator() {
|
||||
@@ -99,7 +101,7 @@ private class IteratorPointerDereferenceOperator extends Operator, TaintFunction
|
||||
/**
|
||||
* A non-member `operator++` or `operator--` function for an iterator type.
|
||||
*/
|
||||
private class IteratorCrementOperator extends Operator, DataFlowFunction {
|
||||
class IteratorCrementOperator extends Operator, DataFlowFunction {
|
||||
FunctionInput iteratorInput;
|
||||
|
||||
IteratorCrementOperator() {
|
||||
@@ -116,7 +118,7 @@ private class IteratorCrementOperator extends Operator, DataFlowFunction {
|
||||
/**
|
||||
* A non-member `operator+` function for an iterator type.
|
||||
*/
|
||||
private class IteratorAddOperator extends Operator, TaintFunction {
|
||||
class IteratorAddOperator extends Operator, TaintFunction {
|
||||
FunctionInput iteratorInput;
|
||||
|
||||
IteratorAddOperator() {
|
||||
@@ -133,7 +135,7 @@ private class IteratorAddOperator extends Operator, TaintFunction {
|
||||
/**
|
||||
* A non-member `operator-` function that takes a pointer difference type as its second argument.
|
||||
*/
|
||||
private class IteratorSubOperator extends Operator, TaintFunction {
|
||||
class IteratorSubOperator extends Operator, TaintFunction {
|
||||
FunctionInput iteratorInput;
|
||||
|
||||
IteratorSubOperator() {
|
||||
@@ -151,7 +153,7 @@ private class IteratorSubOperator extends Operator, TaintFunction {
|
||||
/**
|
||||
* A non-member `operator+=` or `operator-=` function for an iterator type.
|
||||
*/
|
||||
private class IteratorAssignArithmeticOperator extends Operator, DataFlowFunction, TaintFunction {
|
||||
class IteratorAssignArithmeticOperator extends Operator, DataFlowFunction, TaintFunction {
|
||||
IteratorAssignArithmeticOperator() {
|
||||
this.hasName(["operator+=", "operator-="]) and
|
||||
this.getDeclaringType() instanceof Iterator
|
||||
@@ -190,7 +192,7 @@ class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunc
|
||||
/**
|
||||
* An `operator++` or `operator--` member function for an iterator type.
|
||||
*/
|
||||
private class IteratorCrementMemberOperator extends MemberFunction, DataFlowFunction, TaintFunction {
|
||||
class IteratorCrementMemberOperator extends MemberFunction, DataFlowFunction, TaintFunction {
|
||||
IteratorCrementMemberOperator() {
|
||||
this.hasName(["operator++", "operator--"]) and
|
||||
this.getDeclaringType() instanceof Iterator
|
||||
@@ -213,7 +215,7 @@ private class IteratorCrementMemberOperator extends MemberFunction, DataFlowFunc
|
||||
/**
|
||||
* A member `operator->` function for an iterator type.
|
||||
*/
|
||||
private class IteratorFieldMemberOperator extends Operator, TaintFunction {
|
||||
class IteratorFieldMemberOperator extends Operator, TaintFunction {
|
||||
IteratorFieldMemberOperator() {
|
||||
this.hasName("operator->") and
|
||||
this.getDeclaringType() instanceof Iterator
|
||||
@@ -228,7 +230,7 @@ private class IteratorFieldMemberOperator extends Operator, TaintFunction {
|
||||
/**
|
||||
* An `operator+` or `operator-` member function of an iterator class.
|
||||
*/
|
||||
private class IteratorBinaryArithmeticMemberOperator extends MemberFunction, TaintFunction {
|
||||
class IteratorBinaryArithmeticMemberOperator extends MemberFunction, TaintFunction {
|
||||
IteratorBinaryArithmeticMemberOperator() {
|
||||
this.hasName(["operator+", "operator-"]) and
|
||||
this.getDeclaringType() instanceof Iterator
|
||||
@@ -243,8 +245,7 @@ private class IteratorBinaryArithmeticMemberOperator extends MemberFunction, Tai
|
||||
/**
|
||||
* An `operator+=` or `operator-=` member function of an iterator class.
|
||||
*/
|
||||
private class IteratorAssignArithmeticMemberOperator extends MemberFunction, DataFlowFunction,
|
||||
TaintFunction {
|
||||
class IteratorAssignArithmeticMemberOperator extends MemberFunction, DataFlowFunction, TaintFunction {
|
||||
IteratorAssignArithmeticMemberOperator() {
|
||||
this.hasName(["operator+=", "operator-="]) and
|
||||
this.getDeclaringType() instanceof Iterator
|
||||
@@ -267,8 +268,7 @@ private class IteratorAssignArithmeticMemberOperator extends MemberFunction, Dat
|
||||
/**
|
||||
* An `operator[]` member function of an iterator class.
|
||||
*/
|
||||
private class IteratorArrayMemberOperator extends MemberFunction, TaintFunction,
|
||||
IteratorReferenceFunction {
|
||||
class IteratorArrayMemberOperator extends MemberFunction, TaintFunction, IteratorReferenceFunction {
|
||||
IteratorArrayMemberOperator() {
|
||||
this.hasName("operator[]") and
|
||||
this.getDeclaringType() instanceof Iterator
|
||||
@@ -287,7 +287,7 @@ private class IteratorArrayMemberOperator extends MemberFunction, TaintFunction,
|
||||
* The `hasTaintFlow` override provides flow through output iterators that return themselves with
|
||||
* `operator*` and use their own `operator=` to assign to the container.
|
||||
*/
|
||||
private class IteratorAssignmentMemberOperator extends MemberFunction, TaintFunction {
|
||||
class IteratorAssignmentMemberOperator extends MemberFunction, TaintFunction {
|
||||
IteratorAssignmentMemberOperator() {
|
||||
this.hasName("operator=") and
|
||||
this.getDeclaringType() instanceof Iterator and
|
||||
@@ -305,13 +305,11 @@ private class IteratorAssignmentMemberOperator extends MemberFunction, TaintFunc
|
||||
* A `begin` or `end` member function, or a related member function, that
|
||||
* returns an iterator.
|
||||
*/
|
||||
private class BeginOrEndFunction extends MemberFunction, TaintFunction, GetIteratorFunction {
|
||||
class BeginOrEndFunction extends MemberFunction, TaintFunction, GetIteratorFunction {
|
||||
BeginOrEndFunction() {
|
||||
this
|
||||
.hasName([
|
||||
"begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend", "before_begin",
|
||||
"cbefore_begin"
|
||||
]) and
|
||||
.hasName(["begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend",
|
||||
"before_begin", "cbefore_begin"]) and
|
||||
this.getType().getUnspecifiedType() instanceof Iterator
|
||||
}
|
||||
|
||||
@@ -330,7 +328,7 @@ private class BeginOrEndFunction extends MemberFunction, TaintFunction, GetItera
|
||||
* The `std::front_inserter`, `std::inserter`, and `std::back_inserter`
|
||||
* functions.
|
||||
*/
|
||||
private class InserterIteratorFunction extends GetIteratorFunction {
|
||||
class InserterIteratorFunction extends GetIteratorFunction {
|
||||
InserterIteratorFunction() {
|
||||
this.hasQualifiedName("std", ["front_inserter", "inserter", "back_inserter"])
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import semmle.code.cpp.models.interfaces.Taint
|
||||
* it does correspond with the constructors we are confident taint should flow
|
||||
* through.
|
||||
*/
|
||||
private class ConversionConstructorModel extends Constructor, TaintFunction {
|
||||
class ConversionConstructorModel extends Constructor, TaintFunction {
|
||||
ConversionConstructorModel() {
|
||||
strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
|
||||
not hasSpecifier("explicit")
|
||||
@@ -32,10 +32,10 @@ private class ConversionConstructorModel extends Constructor, TaintFunction {
|
||||
/**
|
||||
* Model for C++ copy constructors.
|
||||
*/
|
||||
private class CopyConstructorModel extends CopyConstructor, DataFlowFunction {
|
||||
class CopyConstructorModel extends CopyConstructor, DataFlowFunction {
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// data flow from the first constructor argument to the returned object
|
||||
input.isParameterDeref(0) and
|
||||
input.isParameter(0) and
|
||||
(
|
||||
output.isReturnValue()
|
||||
or
|
||||
@@ -47,10 +47,10 @@ private class CopyConstructorModel extends CopyConstructor, DataFlowFunction {
|
||||
/**
|
||||
* Model for C++ move constructors.
|
||||
*/
|
||||
private class MoveConstructorModel extends MoveConstructor, DataFlowFunction {
|
||||
class MoveConstructorModel extends MoveConstructor, DataFlowFunction {
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// data flow from the first constructor argument to the returned object
|
||||
input.isParameterDeref(0) and
|
||||
input.isParameter(0) and
|
||||
(
|
||||
output.isReturnValue()
|
||||
or
|
||||
@@ -62,7 +62,7 @@ private class MoveConstructorModel extends MoveConstructor, DataFlowFunction {
|
||||
/**
|
||||
* Model for C++ copy assignment operators.
|
||||
*/
|
||||
private class CopyAssignmentOperatorModel extends CopyAssignmentOperator, TaintFunction {
|
||||
class CopyAssignmentOperatorModel extends CopyAssignmentOperator, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// taint flow from argument to self
|
||||
input.isParameterDeref(0) and
|
||||
@@ -78,7 +78,7 @@ private class CopyAssignmentOperatorModel extends CopyAssignmentOperator, TaintF
|
||||
/**
|
||||
* Model for C++ move assignment operators.
|
||||
*/
|
||||
private class MoveAssignmentOperatorModel extends MoveAssignmentOperator, TaintFunction {
|
||||
class MoveAssignmentOperatorModel extends MoveAssignmentOperator, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// taint flow from argument to self
|
||||
input.isParameterDeref(0) and
|
||||
|
||||
@@ -13,7 +13,7 @@ import semmle.code.cpp.models.interfaces.Taint
|
||||
* The standard functions `memcpy`, `memmove` and `bcopy`; and the gcc variant
|
||||
* `__builtin___memcpy_chk`.
|
||||
*/
|
||||
private class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffectFunction {
|
||||
class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffectFunction {
|
||||
MemcpyFunction() {
|
||||
// memcpy(dest, src, num)
|
||||
// memmove(dest, src, num)
|
||||
|
||||
@@ -12,8 +12,7 @@ import semmle.code.cpp.models.interfaces.SideEffect
|
||||
/**
|
||||
* The standard function `memset` and its assorted variants
|
||||
*/
|
||||
private class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunction,
|
||||
SideEffectFunction {
|
||||
class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunction, SideEffectFunction {
|
||||
MemsetFunction() {
|
||||
hasGlobalName("memset") or
|
||||
hasGlobalName("wmemset") or
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user